kavachOS
Authentication

OAuth providers

How OAuth 2.0 sign-in works in KavachOS and how to add any provider.

The oauth() plugin adds social sign-in to KavachOS using OAuth 2.0 authorization code flow with PKCE. It handles token exchange, account linking, and session creation automatically.

Built-in providers

Generic setup

lib/kavach.ts
import { createKavach } from 'kavachos';
import { oauth } from 'kavachos/plugins/oauth'; 

const kavach = await createKavach({
  database: { provider: 'postgres', url: process.env.DATABASE_URL! },
  secret: process.env.KAVACH_SECRET!,
  baseUrl: 'https://auth.example.com',
  plugins: [
    oauth({ 
      providers: [ 
        {
          id: 'google',
          clientId: process.env.GOOGLE_CLIENT_ID!,
          clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
        },
      ],
    }), 
  ],
});

The callback URL registered with each provider follows the pattern:

https://auth.example.com/auth/oauth/{providerId}/callback

The sign-in flow

  1. Your frontend redirects to /auth/oauth/{providerId}/authorize. KavachOS generates a state parameter and PKCE challenge, then redirects to the provider.
  2. The provider redirects back with an authorization code.
  3. KavachOS exchanges the code for tokens, fetches the user profile, and creates or updates the user record.
  4. A session cookie is set and the user lands on your redirect destination.

The user ID is stable: the same provider account always resolves to the same row in kavach_users.

Account linking

When a sign-in email matches an existing user, KavachOS links the OAuth identity to that account automatically. A single user can connect multiple providers.

// Check connected providers
const connections = await kavach.auth.getOAuthConnections(userId);

// Disconnect a provider (requires at least one remaining sign-in method)
await kavach.auth.disconnectOAuth(userId, 'github');

Auto-linking trusts the email from the provider. If your threat model requires stronger guarantees, set autoLink: false and handle linking explicitly.

Custom providers

Any OAuth 2.0 provider works by supplying the authorization and token endpoints:

oauth({
  providers: [
    {
      id: 'linear',
      clientId: process.env.LINEAR_CLIENT_ID!,
      clientSecret: process.env.LINEAR_CLIENT_SECRET!,
      authorizationUrl: 'https://linear.app/oauth/authorize', 
      tokenUrl: 'https://api.linear.app/oauth/token', 
      userInfoUrl: 'https://api.linear.app/graphql', 
      scopes: ['read'], 
      getUserProfile: (data) => ({ 
        id: data.data.viewer.id,
        email: data.data.viewer.email,
        name: data.data.viewer.name,
      }), 
    },
  ],
})

getUserProfile maps the raw provider response to { id, email?, name?, image? }. The id is the provider-side user ID, stored for deduplication.

On this page