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:
I have an external S3 bucket configured as a storage location
I have instrumented an S3 event notification in AWS such that new objects trigger a Lambda function
The Lambda function uses the Python synapse client to add an external S3 file handle to Synapse for each new object
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 refresh 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 {
} | AccessTokenGenerationResponse {
} | Generates a token that users can copy and paste into the command line client. |
GET /auth/v1/userGeneratedToken | None | Paginated list of AccessTokenRecord: {
} | Retrieves a paginated list of the user’s generated access tokens. |
DELETE /auth/v1/userGeneratedToken | Param (only one is required): id: the id of the token to delete OR token: the token to delete | None | Revokes the token if it’s a valid access token |
How to use a personal access token
The personal access token can be used by doing two things
Put a valid identifier, (e.g. username) in the
userId
header (just like API keys)Put the token in the Authorization header with the ‘Bearer’ keyword preceding the token (
Authorization: Bearer <token>
)