Skip to content

Repository Interfaces

Database Schema Examples

The Database Schema Reference gives SQL examples for these repositories.

Auth Code Repository

The OAuthAuthCodeRepository interface manages the authorization codes. Its methods find a code, issue a new code, store a code, revoke a code, and report if a code is revoked.

Used in Grants: Authorization Code

ts
interface OAuthAuthCodeRepository {
  // Fetch auth code entity from storage by code
  getByIdentifier(authCodeCode: string): Promise<OAuthAuthCode>;

  // Asynchronously issues a new OAuthAuthCode for the given client, user, and scopes.
  // The returned auth code should not be persisted yet.
  // Note: The `expiresAt` value set here may be replaced by the authorization server
  //   using the TTL configured in `enableGrantType`.
  issueAuthCode(
    client: OAuthClient,
    user: OAuthUser | undefined,
    scopes: OAuthScope[],
  ): OAuthAuthCode | Promise<OAuthAuthCode>;

  // An async call that should persist an OAuthAuthCode into your storage.
  persist(authCode: OAuthAuthCode): Promise<void>;

  // This async method is called when an auth code is validated by the
  // authorization server. Return `true` if the auth code has been
  // manually revoked. If the code is still valid return `false`
  isRevoked(authCodeCode: string): Promise<boolean>;

  revoke(authCodeCode: string): Promise<void>;
}

getByIdentifier must return the code challenge

With an opaque authorization code, the stored row is the only record of the challenge. If your query does not read the codeChallenge and codeChallengeMethod columns, the server cannot enforce PKCE. The server then rejects the code with invalid_grant while requiresPKCE is true, which is the default. The server does not issue a token without a verifier. ADR 0009 gives the full decision.

Client Repository

The OAuthClientRepository interface manages the Clients. Its methods find a Client by its client_id, and validate the Client against the grant type and the client secret.

Used in Grants: Authorization Code · Client Credentials · Refresh Token · Password · Implicit · Custom

ts
interface OAuthClientRepository {
  // Fetch client entity from storage by client_id
  getByIdentifier(clientId: string): Promise<OAuthClient>;

  // check the grant type and secret against the client
  isClientValid(
    grantType: GrantIdentifier,
    client: OAuthClient,
    clientSecret?: string,
  ): Promise<boolean>;
}

getByIdentifier must throw for an unknown client_id. If your repository returns undefined, the server reports a failed client authentication (invalid_client). It does not report a server error.

Scope Repository

The OAuthScopeRepository interface manages the scopes. Its methods find scopes by their names, and make the final set of scopes. In the last step you can add a scope, or remove one, after the server validates the request against the scopes of the Client.

Used in Grants: Authorization Code · Client Credentials · Refresh Token · Password · Implicit · Custom

ts
interface OAuthScopeRepository {
  // Find all scopes by scope names
  getAllByIdentifiers(scopeNames: string[]): Promise<OAuthScope[]>;

  // This method is called right before an access token or authorization code is created.
  // Here you can validate the set of scopes requested are valid for the current client,
  // and optionally append additional scopes or remove requested scopes.
  finalize(
    scopes: OAuthScope[],
    identifier: GrantIdentifier,
    client: OAuthClient,
    user_id?: OAuthUserIdentifier,
  ): Promise<OAuthScope[]>;
}

Token Repository

The OAuthTokenRepository interface manages the tokens. Its methods issue a token, store a token, issue a Refresh Token, revoke a token, and find a token by its Refresh Token.

Used in Grants: Authorization Code · Client Credentials · Refresh Token · Password · Implicit · Custom

ts
interface OAuthTokenRepository {
  // Asynchronously issues a new OAuthToken for the given client, scopes, and optional user.
  // The returned token should not be persisted yet.
  // Note: The `accessTokenExpiresAt` value set here will be replaced by the
  //   authorization server using the TTL configured in `enableGrantType`.
  issueToken(client: OAuthClient, scopes: OAuthScope[], user?: OAuthUser | null): Promise<OAuthToken>;

  // An async call that should persist an OAuthToken into your storage.
  persist(accessToken: OAuthToken): Promise<void>;

  // Adds refresh token fields to an already-persisted OAuthToken and updates storage.
  // This method should update the token record in your storage; persist() will not be called again.
  // Note: The `refreshTokenExpiresAt` value set here is kept. The authorization
  //   server does not replace it.
  issueRefreshToken(accessToken: OAuthToken, client: OAuthClient): Promise<OAuthToken>;

  // This async method is called when a refresh token is used to reissue
  // an access token. The original access token is revoked, and a new
  // access token is issued.
  revoke(accessToken: OAuthToken): Promise<void>;

  // This async method, if implemented, will be called by the authorization
  // code grant if the original authorization code is reused.
  // See https://www.rfc-editor.org/rfc/rfc6749#section-4.1.2 for why.
  revokeDescendantsOf?(authCodeId: string): Promise<void>;

  // This async method is called when a refresh token is validated by the
  // authorization server. Return `true` if the refresh token has been
  // manually revoked. If the token is still valid return `false`
  isRefreshTokenRevoked(refreshToken: OAuthToken): Promise<boolean>;

  // (Optional) Called by the OIDC /userinfo endpoint and /token/introspect to
  // detect flag-based revocation of an access token (the row still exists with
  // a future expiry but has been marked revoked). Without it, those endpoints
  // only treat a token as revoked once it is deleted from storage or expires.
  // Return `true` if the access token has been revoked, otherwise `false`.
  isAccessTokenRevoked?(accessToken: OAuthToken): Promise<boolean>;

  // Fetch refresh token entity from storage by refresh token
  getByRefreshToken(refreshTokenToken: string): Promise<OAuthToken>;

  // (Optional)
  //     Required if using /revoke RFC7009 "OAuth 2.0 Token Revocation"
  //     Required if using /introspect RFC7662 "OAuth 2.0 Token Introspection"
  // @see https://tsoauth2server.com/docs/endpoints/introspect
  // @see https://tsoauth2server.com/docs/endpoints/revoke
  getByAccessToken?(accessTokenToken: string): Promise<OAuthToken>;
}

User Repository

The OAuthUserRepository interface manages the users. Its method finds a user by their credentials, and it validates those credentials. The server also supplies the grant type and the Client, and you can make more checks with them.

Used in Grants: Authorization Code · Password

ts
interface OAuthUserRepository {
  // Fetch user entity from storage by identifier. A provided password may
  // be used to validate the users credentials. Grant type and client are provided
  // for additional checks if desired
  getUserByCredentials(
    identifier: OAuthUserIdentifier,
    password?: string,
    grantType?: GrantIdentifier,
    client?: OAuthClient,
  ): Promise<OAuthUser | undefined>;
}