Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support refresh tokens #18

Open
tahpot opened this issue Apr 27, 2022 · 4 comments
Open

Support refresh tokens #18

tahpot opened this issue Apr 27, 2022 · 4 comments
Assignees
Labels
enhancement New feature or request
Milestone

Comments

@tahpot
Copy link
Member

tahpot commented Apr 27, 2022

The current auth flow is as follows:

  1. DID signs a consent message to unlock a context using the private key for the DID (this happens in the Vault).
  2. The signed consent message is sent to the storage node to perform all CRUD operations to manage their databases. This consent message is the "key" to unlock all database operation and never expires. This key is sent back to the web browser by the Vault, giving the web browser never-ending access. This needs to be fixed.
  3. The signed consent message is sent to the storage node to obtain a CouchDB auth token (with a fixed expiry) that is used to actually read / write from the DID's databases.

The key issue is at step (2).

We require the following capabilities:

  • As a Verida Vault I can sign a consent message using my private key to obtain a refresh token and access token to gain access to a storage node server for a given application context.
  • As a Verida Vault I can store the refresh token, linked to a given application context and Verida dApp so that I can track all the applications I have logged into
  • As a Verida dApp I can send a message to the Verida Vault and receive a refresh token and access token so that I can gain access to a storage node server for a given application context
  • As a Verida dApp I can use the access token to make requests to the storage node server so that I can manage databases
  • As a Verida dApp I can use the access token to make requests directly to CouchDB so that I can read / write database data
  • As a Verida dApp I can use the refresh token to obtain a new access token if it expires so that the user isn't logged out of the application
  • As a Verida dApp I can use the refresh token to obtain a new refresh token if it is getting close to expire so that the user isn't logged out of the application
  • As a Verida Vault I can revoke a refresh token, linked to a given application context and Verida dApp so that I can revoke database access to an application

The proposed new flow is as follows:

  1. DID signs a consent message to unlock a context using the private key for the DID (this happens in the Vault).
  2. The Vault sends the signed consent message to the storage node, which generates a storage node refresh token and access token via a new authenticate() endpoint
  3. The refresh token expires after (30?) days
  4. The Vault stores the refresh token in a app_connections database, linked to the application context and domain name that made the SSO request
  5. The Vault returns both tokens to the web browser.
  6. The web browser can use the access token make requests to the storage node or the couchdb server
  7. The web browser can use the refresh token to obtain a new access token / refresh token from the storage node server, if the access token / refresh token expires
  8. The Vault can revoke a refresh token granted to any Verida dApp at any time

Note: access tokens can't be revoked in couchdb, so we don't support revoking them at all. Instead they are short lived (5 minutes).

This requires the following updates to storage node:

  • generateAuthJwt(did:string, contextName: string) -- Begin the auth flow. Storage node generates a JWT containing a unique string that must be signed by the DID to complete authentication. Expires after 60 seconds.
  • authenticate(authJwt: string, did: string, contextName: string, signature: string, deviceId: string) -- Authenticate access to the storage node and couchdb server. Verifies a signature is signed for a given contextName and contains the unique string from a valid JWT. Returns a new refresh token and access token. Stores the refresh token on the server.
  • get(contextName: string, refreshToken: string) -- Update the existing get() method to accept a refresh token and return an access token with the database hostname.
  • invalidateDeviceId(deviceId: string) -- Invalidates all refresh tokens that have been generated for a given deviceId. This allows the Vault to sign out a particular device.
  • regenerateRefreshToken(refreshToken: string, contextName: string) -- Invalidates an existing refresh token and generates a new one. This enables an application to update the refresh token with a more recent expiry.
  • request validator middleware must be updated to validate all requests against a supplied access token.
  • Garbage collection of expired refresh tokens
@tahpot tahpot added the enhancement New feature or request label Apr 27, 2022
@tahpot tahpot self-assigned this Apr 27, 2022
@tahpot
Copy link
Member Author

tahpot commented May 2, 2022

The following endpoints are implemented with working unit tests:

  • generateAuthJwt()
  • authenticate()
  • get()

The request validator has been updated.

Endpoints remaining:

  • invalidateDeviceId()
  • regenerateRefreshToken()

Other remaining:

  • Garbage collection of expired refresh tokens

@tahpot
Copy link
Member Author

tahpot commented May 2, 2022

Other changes also made that require client-ts updates:

  • Add support for /user/deleteDatabase() endpoint. Requires adding support in client-ts
  • Modify database CRUD operations to accept a non-hashed database name and the storage-node then hashes the database name. This is for increased security to prevent a malicious user creating database operations on databases not linked to their DID. Requires updating in client-ts to send the database name, not the hash.

@tahpot
Copy link
Member Author

tahpot commented May 6, 2022

  • Renamed /user/get to `/user/connect'
  • Split /auth out from /user for better clarity
  • Fix some broken unit tests
  • Added support for regenerateRefreshToken()
  • Added support for invalidateDeviceId()

@tahpot
Copy link
Member Author

tahpot commented May 7, 2022

  • Fixed issues with invalidDeviceId() support
  • Added support for garbage collection of refresh tokens

@tahpot tahpot added this to the v2.0.0 milestone Jan 13, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

1 participant