kavachOS
Authentication

OIDC provider

Act as an OpenID Connect identity provider.

KavachOS can act as a full OpenID Connect identity provider. External apps register as clients and authenticate their users through KavachOS using the standard authorization code flow with PKCE, ID tokens, refresh tokens, discovery, and JWKS.

This is distinct from using OAuth providers with KavachOS. Here, KavachOS is the provider.

Setup

Generate a signing key

import { generateKeyPair, exportJWK } from 'jose';

const { privateKey } = await generateKeyPair('RS256');

Store the private key securely. The corresponding public key is exposed at the JWKS endpoint so clients can verify tokens.

Create the module

lib/kavach.ts
import { generateKeyPair } from 'jose';
import { createOidcProviderModule } from 'kavachos/auth';

const { privateKey } = await generateKeyPair('RS256');

const oidc = createOidcProviderModule(
  {
    issuer: 'https://auth.example.com',
    signingKey: privateKey,
  },
  db,
  async (userId, scopes) => {
    const user = await getUser(userId);
    return {
      sub: user.id,
      email: scopes.includes('email') ? user.email : undefined,
      name: scopes.includes('profile') ? user.name : undefined,
      emailVerified: user.emailVerified,
    };
  },
);

The third argument is a GetUserClaimsFn callback. KavachOS calls it to build ID tokens and userinfo responses. You control what data is returned per scope.

Register a client

const result = await oidc.registerClient({
  clientName: 'My App',
  redirectUris: ['https://app.example.com/callback'],
});

if (result.success) {
  console.log(result.data.clientId);
  console.log(result.data.clientSecret); // shown once, store it now
}

The clientSecret is only returned on registration. It is stored hashed. If you lose it, delete and re-register the client.

Configuration options

OptionTypeDefaultDescription
issuerstringrequiredIssuer URL, e.g. https://auth.example.com
signingKeyCryptoKey | JWKrequiredRSA or EC private key for signing tokens
signingAlgorithmstringRS256JWT algorithm (RS256, ES256, etc.)
accessTokenTtlnumber3600Access token lifetime in seconds
refreshTokenTtlnumber2592000Refresh token lifetime in seconds (30 days)
authCodeTtlnumber600Authorization code lifetime in seconds
idTokenTtlnumber3600ID token lifetime in seconds
supportedScopesstring[]['openid', 'profile', 'email']Scopes this provider accepts

Authorization code flow

  1. Client redirects user to {issuer}/authorize with response_type=code, client_id, redirect_uri, scope, and optionally code_challenge + code_challenge_method=S256.
  2. Your app authenticates the user, then calls oidc.authorize({ ...params, userId }) to issue a code.
  3. Client exchanges the code at {issuer}/token for access_token, id_token, and refresh_token.
  4. Client can refresh tokens using grant_type=refresh_token. Refresh tokens rotate on each use.

PKCE with S256 is supported. If the authorization request includes a code_challenge, the token request must include the matching code_verifier. Authorization codes are single-use and expire after 10 minutes by default.

Endpoints

MethodPathDescription
GET/.well-known/openid-configurationDiscovery document
GET/.well-known/jwks.jsonPublic signing keys
GET/authorizeAuthorization endpoint
POST/tokenToken endpoint
GET/POST/userinfoUserInfo endpoint
POST/registerDynamic client registration

On this page