API reference

yatta/oauth

OAuth 2.0: code + PKCE, client credentials, rotating refresh.

10 exported symbols and 43 members, read from src/func/oauth.ts.

Construct

clientsFromEnv

function

Read `OAUTH_CLIENTS` — a JSON list — into client inputs.

A bad entry is skipped with a warning instead of taking the process down: one mistyped redirect URI used to discard the good clients next to it, and the failure surfaced only as "invalid_client" at runtime.

clientsFromEnv(env: Record<string, string | undefined>): OAuthClientInput[]
env
Usually `process.env`.

createOAuthServer

function

Build an OAuth 2.0 authorization server.

Mount the three handlers on `/oauth/authorize`, `/oauth/token` and `/oauth/revoke`. The wire format is plain form-encoded HTTP, so the handlers take and return `Request`/`Response` directly.

createOAuthServer(options: OAuthServerOptions)

MemoryOAuthStore

class

In-memory store. Fine for one server, and for tests.

Nothing survives a restart: issued tokens stop working and registered clients have to be registered again. Pass your own `store` to change that, and keep the write in that store out of the check-then-write window.

class MemoryOAuthStore implements OAuthStore

10 members

  • clientsproperty
    clients:
  • codesproperty
    codes:
  • tokensproperty
    tokens:
  • saveClient
    saveClient(client: OAuthClient): void
  • getClient
    getClient(id: string): OAuthClient | undefined
  • saveCode
    saveCode(code: AuthCode): void
  • getCode
    getCode(codeHash: string): AuthCode | undefined
  • saveToken
    saveToken(token: OAuthTokenRecord): void
  • getToken
    getToken(tokenHash: string): OAuthTokenRecord | undefined
  • sweep
    sweep(now: number): void

Types

IdTokenIssuer

interface

Builds the OIDC ID token for a token reply.

Return null when there is no identity to sign (the user is gone, or the scope has no "openid"). `createOIDC` supplies one of these; a code exchange fails if it throws, and a refresh leaves the token out rather than stranding a grant whose old refresh token is already spent.

interface IdTokenIssuer

1 member

  • [call]call
    (args: { clientId: string; userId: string; scope: string[]; nonce?: string; }): Promise<string | null> | string | null

OAuthClient

interface

A registered OAuth client.

`secretHash` is empty for a public client — a mobile or browser app that cannot keep a secret. Public clients must use PKCE, because nothing else proves the code came back to whoever started the flow.

interface OAuthClient

5 members

  • idproperty
    id: string
  • secretHashproperty
    secretHash: string
  • redirectUrisproperty
    redirectUris: string[]
  • nameproperty
    name?: string
  • allowedScopesproperty
    allowedScopes?: string[]

OAuthClientInput

interface

What `registerClient` takes.

The secret is handed over in the clear exactly once, here; only its SHA-256 hash is kept. There is no way to read it back, which is why registering a client is a startup job rather than an API.

5 members

  • idproperty
    id: string
  • secretproperty
    secret?: string
  • redirectUrisproperty
    redirectUris: string[]
  • nameproperty
    name?: string
  • allowedScopesproperty
    allowedScopes?: string[]

OAuthServerOptions

interface

12 members

  • issuerproperty
    issuer: string
  • storeproperty
    store?: OAuthStore
  • clientsproperty
    clients?: OAuthClientInput[]
  • resolveUserproperty
    resolveUser?: (req: Request) => Promise<string | null>
  • accessTokenTtlSecproperty
    accessTokenTtlSec?: number
  • refreshTokenTtlSecproperty
    refreshTokenTtlSec?: number
  • codeTtlSecproperty
    codeTtlSec?: number
  • issueIdTokenproperty
    issueIdToken?: IdTokenIssuer
  • scopesproperty
    scopes?: string[]
  • requirePkceproperty
    requirePkce?: boolean
  • allowPlainPkceproperty
    allowPlainPkce?: boolean
  • approveproperty
    approve?: (info: { userId: string; client: OAuthClient; scope: string[]; }) => Promise<boolean> | boolean

OAuthStore

interface

Where clients, codes and tokens live.

Every method is synchronous by design: a check and its matching write must not be separated by an `await`, or two simultaneous requests can both spend the same code. A persistent store should therefore keep its state in memory and write through, not read from disk on each call.

interface OAuthStore

7 members

  • saveClient
    saveClient(client: OAuthClient): void
  • getClient
    getClient(id: string): OAuthClient | undefined
  • saveCode
    saveCode(code: AuthCode): void
  • getCode
    getCode(codeHash: string): AuthCode | undefined
  • saveToken
    saveToken(token: OAuthTokenRecord): void
  • getToken
    getToken(tokenHash: string): OAuthTokenRecord | undefined
  • sweep
    sweep(now: number): void?

OAuthTokenInfo

interface

What `getAccessToken` returns for a live token.

interface OAuthTokenInfo

3 members

  • userIdproperty
    userId: string | null
  • clientIdproperty
    clientId: string
  • scopeproperty
    scope: string[]

OAuthServer

type

The object `createOAuthServer` returns.

type OAuthServer = ReturnType<typeof createOAuthServer>
Tip
Most of the types above are inferred. You rarely import AuthConfig or JobPayload — declaring your schema once is enough for the rest to follow. See Typed keys.