Open tailhook opened 4 years ago
I'm going to postpone self-encoded token support. The reasons are below. But first let's take a look how opaque tokens work.
To authorize token, you insert it into a database with appropriate properties. Something along the lines of:
WITH MODULE auth
MyToken := INSERT Token {
token_id := make_token_id(),
expires := datetime_current() + to_duration(hours := 24),
database := 'my_database',
role := 'my_role',
# any other needed settings
}
SELECT MyToken { token_id }
Then, you can use the token_id
as Bearer
token or any equivalent method described above.
The main issue with self-encoding tokens is that currently they are structures like this:
So generally even at the layer (3) there is not much data relevant for edgedb is fetched. And (2) only provides user name (which is generally an external user name, not edgedb's one when using OAuth).
So the reasons to only support opaque tokens for now is:
So the current proposal is to implement opaque tokens only, and postpone self-encoding tokens to the time when both will be true:
Great summary, thanks @tailhook!
The issue with a non-self-encoded token, as you pointed out, is that we will have to store token metadata somewhere. Storing it in a database begs a question: which database? We currently avoid having a "special" database to store global metadata and instead rely on metadata in Postgres shared catalogs (pg_database
and pg_roles
). This arrangement makes maintaining large quantities of user-associated metadata, such as a list of valid tokens, quite cumbersome, especially where expiring tokens are considered.
I think we should take a closer look at using JWT as the token protocol from the get-go using EdgeDB-specific claims (probably just the name of the database role for now). This would make it easier to add support for OIDC later as well.
Okay, if we don't care about compatibility with anything, we can use JWT for encoding our own things, but...
To revoke a token we have to store some token metadata. This is generally a lot less actual storage, but structurally it's the same. I don't believe we can get to production without any way of revoking tokens.
We currently avoid having a "special" database to store global metadata and instead rely on metadata in Postgres
Do you think this will continue to be true when we have ACLs?
To revoke a token we have to store some token metadata.
You only need to keep a set of revoked token ids. Revocation is also a relatively rare event, so the set will not be large, which makes shared catalog storage feasible.
Do you think this will continue to be true when we have ACLs?
Yes. The authorization scopes will be encoded as claims in JWT.
Yes. The authorization scopes will be encoded as claims in JWT.
I'm not asking about scopes. I'm about the actual access control lists, rules, whatever. I expect quite a bit of metadata about relations between users and data. I expect them to be stored somewhere.
Oh. The actual access rules will be defined in the schema with DDL/SDL: https://edgedb.com/roadmap/#access_control. The scopes in the token will effectively populate globals in a session, which, in turn, will trigger relevant access rules.
Well, so ACLs will depend on the database. We can do the same with tokens, since the current spec declares a database in the URL wss://host.name/ws/database_name
, we can look for the tokens in the database itself, rather than using a "special" database.
To Do: take a look at PASETO:
That's a good one, thanks for sharing
This adds to RFC 1001 at #4
Overview
Features supported by HTTP:
Authorization
headerCookie
Features suported by browser-based WebSockets:
Cookie
Authentication schemes:
Related protocols:
CREATE ROLE
/ALTER ROLE
statements, but out of scope of this research.Commercial providers:
Related tools: `. JAAS, Pac4J, Apache Shiro -- java scpecific, not researched closely (but look like just Java interfaces for all other protocols)
Requirements
Proposal
Generally authentication should work by providing a Bearer token which is either:
The downside of (2) is that it's harder to revoke already created token, while the downside of (1) is that edgedb needs to keep track of all the tokens that are active now. Upside of (1) is that it's possible to integrate with more systems (in particular ones doesn't support OIDC, or that support OIDC in the way that is incompatible to edgedb).
The token can be transmitted in the one of three ways (all can be used interchangeably):
Authorization: Bearer <token>
-- works for HTTP as well as non-browser websocketsCookie: <cookie_name>=<token>
-- works everywhere, but can be problematic to set a cookie for a domain that is devoted solely to edgedb (we may add a mechanism for that later)param
inClientHandshake
, this works on WebSockets only and is needed for browser-based websockets where usingCookie
is not apropriate.RFC6750 allows passing access_token as form-encoded body parameter and as URI query parameter. We don't allow that now, but we may consider adding them in future if compelling use cases arise.
Configuration:
cookie_name
in the "port" configurationIt's unclear whether we want to allow configuring JWT parameters in particular encryption schema. Also I expect secret keys to be generated and replicated within the edgedb itself, but we can have a mechanism to provide users' keys.
Structure of the Self-Encoded Token
TO DO: research OpenID Connect
Future Extensions
In the future, we should consider at least following ways of authentication:
All of them might only be supported in commercial version.
Update: Note on RFC6750 of access_token usage