Skip to main content

Recipes

Client credentials flow

Register a confidential client with client_credentials in allowedGrantTypes, then request tokens without a redirect or authorization code.

Introspect and revoke tokens

const introspection = oauth.introspectToken({
clientId: 'web-app',
token: token.accessToken
});

oauth.revokeToken({
clientId: 'web-app',
token: String(token.refreshToken),
tokenTypeHint: 'refresh_token'
});

Expose JWKS for token verification

const jwks = oauth.getJWKS();

Serve jwks from GET /api/auth/oauth/.well-known/jwks.json (registered automatically) so resource servers can validate access tokens.

import { QAbstractOAuthConsentStore, type QOAuthConsent } from '@quik/oauth-server';

class DBConsentStore extends QAbstractOAuthConsentStore<MyConsentRepository> {
protected persistConsent(consent: QOAuthConsent): QOAuthConsent {
return this.repository.upsert(consent);
}

protected fetchConsent(id: string): QOAuthConsent | undefined {
return this.repository.findById(id);
}

protected findConsent(clientId: string, userId: string): QOAuthConsent | undefined {
return this.repository.findByClientAndUser(clientId, userId);
}

protected removeConsent(id: string): void {
this.repository.deleteById(id);
}

protected clearConsents(): void {
this.repository.deleteAll();
}

protected removeRevoked(now: number): number {
return this.repository.deleteRevokedSince(now);
}
}

Activate custom stores with setOAuthClientStore, setOAuthAuthorizationCodeStore, setOAuthRefreshTokenStore, and setOAuthConsentStore.

Use fixed signing keys in production

Set oauth.server.jwks.signing.privateKeyPem and publicKeyPem explicitly, and set oauth.server.jwks.signing.allowGeneratedFallback to false — otherwise a fresh ephemeral key pair is generated per process start when running outside NODE_ENV=development, invalidating previously issued tokens.

Checklist

  • Rotate refresh tokens (oauth.server.refreshToken.rotateOnUse) unless a client explicitly needs static refresh tokens.
  • Keep oauth.server.accessToken.timeToLiveSeconds short and rely on refresh tokens for long-lived sessions.