Skip to end of banner
Go to start of banner

Personal Access Tokens (aka API Keys v2)

Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 5 Next »

The current implementation for API keys has a number of issues

  • API keys are stored in plaintext

  • Only one API key can be generated at a time

    • Revoking the API key invalidates all sessions authenticated with the API key

  • Computing the signature required to use them is complicated

  • API keys allow access to all account functions, which may be unnecessary in certain contexts

Some of these issues have been resolved in the designs for Synapse OAuth 2 authorization mechanisms. A new design for API keys would allow us to take advantage of these solutions, and deprecate API keys.

Inspiration: GitHub Personal Access Tokens

Use Cases

The main use case for user-generated refresh tokens differ little from API keys:

  • The ability to authenticate requests from command line clients without a password

    • Both real-time sessions and automated jobs.

Resolving the issues stated above simply make the authentication process more secure, and provides users with additional protections.

This sample scenario identified by Jordan Kiang (Unlicensed) is useful as it helps determine additional requirements:

  1. I have an external S3 bucket configured as a storage location

  2. I have instrumented an S3 event notification in AWS such that new objects trigger a Lambda function

  3. The Lambda function uses the Python synapse client to add an external S3 file handle to Synapse for each new object

  4. I want this to be a relatively "set it and forget it" sequence, not involving any human interaction

Requirements and Design Decisions

  • A user must be able to issue multiple access tokens

  • A user must be able to view metadata about their active tokens, e.g. scope, a custom name/identifier

  • A user must be able to revoke an individual access token

  • Generated access tokens must use scopes as defined in the OAuth 2 implementation

  • Generated access tokens should only expire if unused for 180 days or manually revoked

  • From the perspective of a client application, access tokens must be “stateless”, i.e. they are not single use or rotating

  • Generated access tokens should be bearer tokens

    • A password/session token must not be required to use an access token

  • It should be impossible for any actor to determine the token other than the issuer at creation time (i.e. store a hash).

Services

Access tokens cannot be modified after they have been created.

Service

Request Parameters

Response Body

Notes

POST /auth/v1/userGeneratedToken

Body:

AccessTokenGenerationRequest {

name: string (a unique-to-the-user, human-readable name. if unspecified, a UUID will be generated)

scope: Array<OAuthScope> (scopes granted by the token)

claims: OIDCClaimsRequest (claims granted by the token)

}

AccessTokenGenerationResponse {

token: String, An opaque, cryptographically-unguessable access token.

metadata: AccessTokenRecord, metadata related to the token. See below for fields.

}

Generates a token that users can copy and paste into the command line client.

GET /auth/v1/userGeneratedToken

None

Paginated list of AccessTokenRecord: {

id: unique ID of the access token

scope: Array<OAuthScope> scope of the token

claims: claims granted by the token

name: string, human readable name

lastUsed: the last time an access token was used

createdOn: the date/time the access token was created

}

Retrieves a paginated list of the user’s generated access tokens.

DELETE /auth/v1/userGeneratedToken/{id}

Path param:

id: the id of the token to delete

None

Revokes the token if it’s a valid access token.

DELETE /auth/v1/userGeneratedToken

None

None

Revokes all personal access tokens.

How to use a personal access token

The personal access token can be used by doing two things

  1. Put a valid identifier, (e.g. username) in the userId header (just like API keys)

  2. Put the token in the Authorization header with the ‘Bearer’ keyword preceding the token (Authorization: Bearer <token>)

Open Questions

Which of these services be accessible via scoped access tokens?

  • I think by default, creating/deleting should not be accessible at all. If a use case comes up, then these can be accessible via the authorize scope. Viewing the list of tokens can be done with the view scope.

What should the token look like? Some options

  • Opaque token

    • With or without additional userId header?

  • Non-expiring, signed JWT (containing a token ID and a user ID)

This is partially an implementation question. The ability to revoke tokens requires storing tokens or token identifiers in persistent storage. Performance is critical because a lookup will be done for every authenticated request made by a programmatic client.

  • No labels