Skip to main content
zerotal

Documentation


Documentation / zerotal / auth / OAuth2Driver

Abstract Class: OAuth2Driver

Defined in: packages/auth/src/social/drivers/OAuth2Driver.ts:86

Abstract base for every social-login provider driver.

A driver turns a provider's OAuth2 authorization-code flow into a normalized SocialUser. The two calls an app makes are redirect (send the user to the provider) and user (handle the callback); everything in between — CSRF state, PKCE, token exchange, profile fetch — is handled here.

Remarks

PKCE (RFC 7636, S256) is on by default (usesPKCE). redirect generates a code_verifier, stores it in the session, and sends only its S256 code_challenge to the provider; user replays the verifier during token exchange. A stolen authorization code is therefore useless without the session-bound verifier. Providers that don't support PKCE ignore the extra params.

CSRF state is a random UUID written to the session by redirect and verified (constant-time) against the callback's state by user. Both the state and the PKCE verifier are single-use: user consumes and forgets them from the session so they can never be replayed on a later callback.

redirect_uri is fixed from config.redirectUrl — it is never taken from the request, so it can't be tampered with.

Both redirect and user read the current HttpContext from async-local storage; nothing needs to be passed explicitly. For flows with no request (mobile/SPA), use stateless + user(rawCode) or userFromToken.

Concrete drivers implement the provider endpoints and profile mapping: authUrl, tokenUrl, userUrl, normalise, defaultScopes, with optional hooks extraAuthParams, afterNormalise, _doUser, _extractCodeAndState.

Example

import { Social } from "@zerotal/auth";

// Step 1 — send the user to GitHub (stores state + PKCE verifier in session):
async redirect() {
  return Social.driver("github").redirect();
}

// Step 2 — handle the callback (verifies state, exchanges the code):
async callback() {
  const socialUser = await Social.driver("github").user();
  // socialUser.id, .name, .email, .avatar, .token …
  // find-or-create your app user, then log them in.
}

Extended by

Constructors

Constructor

new OAuth2Driver(config): OAuth2Driver

Defined in: packages/auth/src/social/drivers/OAuth2Driver.ts:92

Parameters

config

OAuth2Config

Returns

OAuth2Driver

Callback

userFromToken()

userFromToken(token): Promise<SocialUser>

Defined in: packages/auth/src/social/drivers/OAuth2Driver.ts:186

Retrieve a user's profile from an access token you already hold — e.g. a native/mobile app that obtained the token via its own SDK. Skips the code-exchange step entirely; the returned user carries no refresh token or expiry (those come from exchange).

const socialUser = await Social.driver('github').userFromToken(accessToken);

Parameters

token

string

An access token already obtained out-of-band (e.g. a mobile SDK).

Returns

Promise<SocialUser>

The normalized profile for that token.

Throws

If the provider's user-info request fails.


user()

user(code?): Promise<SocialUser>

Defined in: packages/auth/src/social/drivers/OAuth2Driver.ts:386

Handle the OAuth2 callback, or exchange a raw code in stateless mode.

Stateful (session-based): call with no arguments — the driver reads the current HttpContext from async-local storage, extracts code + state, verifies state against the session, then exchanges the code for a profile:

async callback({ params }: HttpContext) {
  const socialUser = await Social.driver(params.provider).user();
}

Stateless (SPA / mobile): call with the raw code — skips session/state verification entirely:

const socialUser = await Social.driver('github').stateless().user(rawCode);

Throws Error('invalid_state') or Error('missing_code') on validation failure — catch in your controller and redirect accordingly.

Parameters

code?

string

Optional raw authorization code for stateless flows; omit for the stateful session-based callback.

Returns

Promise<SocialUser>

The normalized SocialUser for the authenticated account.

Throws

When the callback state is missing or mismatched.

Throws

When no authorization code is present.

Throws

When the provider's token exchange fails.

Configuration

stateless()

stateless(): this

Defined in: packages/auth/src/social/drivers/OAuth2Driver.ts:208

Return a shallow copy of this driver with CSRF state verification disabled.

Use this for stateless API/mobile endpoints that receive a raw code directly (no session, no state param):

const socialUser = await Social.driver('google').stateless().user(rawCode);

A copy is returned so the registered singleton is never mutated.

Returns

this


scopes()

scopes(scopes): this

Defined in: packages/auth/src/social/drivers/OAuth2Driver.ts:224

Add scopes to the authorization request, merged with the default/configured scopes. Returns a copy so the registered singleton is never mutated.

Social.driver('github').scopes(['read:user', 'public_repo']).redirect();

Parameters

scopes

string[]

Returns

this


setScopes()

setScopes(scopes): this

Defined in: packages/auth/src/social/drivers/OAuth2Driver.ts:236

Replace all scopes on the authorization request. Returns a copy so the registered singleton is never mutated.

Parameters

scopes

string[]

Returns

this


with()

with(params): this

Defined in: packages/auth/src/social/drivers/OAuth2Driver.ts:259

Append optional parameters to the authorization redirect URL. Useful for provider-specific options such as Google's access_type=offline + prompt=consent (required to receive a refresh token) or hd for hosted-domain restriction.

Do not pass reserved keys (client_id, redirect_uri, scope, state, response_type) — those are managed by the driver. Returns a copy.

Social.driver('google')
  .with({ access_type: 'offline', prompt: 'consent' })
  .redirect();

Parameters

params

Record<string, string>

Returns

this

Other

config

protected config: OAuth2Config

Defined in: packages/auth/src/social/drivers/OAuth2Driver.ts:87


authUrl()

abstract protected authUrl(): string

Defined in: packages/auth/src/social/drivers/OAuth2Driver.ts:98

Returns

string


tokenUrl()

abstract protected tokenUrl(): string

Defined in: packages/auth/src/social/drivers/OAuth2Driver.ts:99

Returns

string


userUrl()

abstract protected userUrl(): string

Defined in: packages/auth/src/social/drivers/OAuth2Driver.ts:100

Returns

string


normalise()

abstract protected normalise(raw, token): SocialUser

Defined in: packages/auth/src/social/drivers/OAuth2Driver.ts:101

Parameters

raw

Record<string, unknown>

token

string

Returns

SocialUser


defaultScopes()

abstract protected defaultScopes(): string[]

Defined in: packages/auth/src/social/drivers/OAuth2Driver.ts:102

Returns

string[]


extraAuthParams()

protected extraAuthParams(): Record<string, string>

Defined in: packages/auth/src/social/drivers/OAuth2Driver.ts:105

Extra query parameters appended to the authorization redirect URL.

Returns

Record<string, string>


scopeSeparator()

protected scopeSeparator(): string

Defined in: packages/auth/src/social/drivers/OAuth2Driver.ts:110

Separator used to join scope values. Default: space.

Returns

string


includeResponseType()

protected includeResponseType(): boolean

Defined in: packages/auth/src/social/drivers/OAuth2Driver.ts:119

Whether to include response_type=code in the authorization URL. Most providers require it; GitHub does not accept it and returns 404. Override to return false for providers that omit it.

Returns

boolean


usesPKCE()

protected usesPKCE(): boolean

Defined in: packages/auth/src/social/drivers/OAuth2Driver.ts:130

Whether to use PKCE (RFC 7636, S256) on the authorization-code flow. Default true: a stolen authorization code cannot be exchanged without the per-flow code_verifier held in the session, and providers that do not support PKCE simply ignore the extra parameters. Override to return false for a provider that rejects unknown params.

Returns

boolean


afterNormalise()

protected afterNormalise(user, _token): Promise<SocialUser>

Defined in: packages/auth/src/social/drivers/OAuth2Driver.ts:138

Called after normalise() — override to enrich the user object (e.g. fetch a secondary endpoint). Default: returns the user unchanged.

Parameters

user

SocialUser

_token

string

Returns

Promise<SocialUser>


_extractCodeAndState()

protected _extractCodeAndState(ctx): Promise<{ code: string | null; state: string | null; }>

Defined in: packages/auth/src/social/drivers/OAuth2Driver.ts:150

Override to change how code and state are extracted from the incoming request. Default: reads ?code=&state= from the query string.

This is an internal hook called by user() — it receives the HttpContext resolved from async-local storage. Apple overrides this to read from a POST form body instead.

Parameters

ctx

SocialHttpContext

Returns

Promise<{ code: string | null; state: string | null; }>


_doUser()

protected _doUser(code, codeVerifier?): Promise<SocialUser>

Defined in: packages/auth/src/social/drivers/OAuth2Driver.ts:162

Override to change the full code→SocialUser pipeline. Apple overrides this to decode an id_token JWT instead of calling a user-info endpoint.

Parameters

code

string

codeVerifier?

string

Returns

Promise<SocialUser>


_exchangeCode()

protected _exchangeCode(code, codeVerifier?): Promise<TokenBundle>

Defined in: packages/auth/src/social/drivers/OAuth2Driver.ts:434

Parameters

code

string

codeVerifier?

string

Returns

Promise<TokenBundle>


_fetchRaw()

protected _fetchRaw(token): Promise<Record<string, unknown>>

Defined in: packages/auth/src/social/drivers/OAuth2Driver.ts:475

Parameters

token

string

Returns

Promise<Record<string, unknown>>

Redirect

redirectUrl()

redirectUrl(state, codeVerifier?): string

Defined in: packages/auth/src/social/drivers/OAuth2Driver.ts:293

Build the full authorization URL (for low-level use or testing). Prefer redirect() in controllers.

When a PKCE codeVerifier is supplied, its S256 challenge is sent as code_challenge + code_challenge_method (RFC 7636 §4.3).

Parameters

state

string

The CSRF state token to embed in the URL.

codeVerifier?

string

Optional PKCE verifier; when present its S256 challenge is sent.

Returns

string

The fully-built authorization URL.


redirect()

redirect(): void

Defined in: packages/auth/src/social/drivers/OAuth2Driver.ts:336

Generate a CSRF state token, store it in the session, and redirect the user to the provider's authorization page.

The current HTTP context is read automatically from async-local storage — no need to pass it explicitly:

async redirect({ params }: HttpContext) {
  return Social.driver(params.provider).redirect();
}

Returns

void

Throws

If called outside an HTTP request; use .stateless().user(code) for request-less flows.