Skip to main content
Version: 0.14.0 (Latest)

🔐 OAuth Drivers

OAuth drivers provide a consistent integration layer for third-party OAuth providers using Passport.js. They encapsulate provider strategy setup and expose Promise-based request and callback helpers for composition into routes and features.


🎯 Overview

OAuth drivers in NodeBlocks are thin wrappers around Passport.js strategies for Google, Twitter, and LINE. Each factory is synchronous — no await is required.

import { drivers } from '@nodeblocks/backend-sdk';

const {
createGoogleOAuthDriver,
createTwitterOAuthDriver,
createLineOAuthDriver,
verifyGoogleCallback,
verifyTwitterCallback,
verifyLineCallback,
PROVIDER_GOOGLE,
PROVIDER_TWITTER,
PROVIDER_LINE,
TWITTER_CALLBACK_STATE_SESSION_KEY,
} = drivers;

Each factory returns a driver object with three methods:

MethodPurpose
initialize(app)Register Passport and any required middleware on the Express app
request(req, res, state)Start the OAuth authorization flow
callback(req, res)Handle the OAuth callback and return the normalized profile

Driver constants:

ExportValueSource file
PROVIDER_GOOGLE'google'src/drivers/oauth/google.ts
PROVIDER_TWITTER'twitter'src/drivers/oauth/twitter/index.ts
PROVIDER_LINE'line'src/drivers/oauth/line/index.ts
TWITTER_CALLBACK_STATE_SESSION_KEY'twitter-callback-state'src/drivers/oauth/twitter/index.ts

Global side effect: Each factory calls passport.use(new ...Strategy(...)) on the shared Passport singleton. Calling multiple factory functions registers strategies on the same Passport instance for the process.

Types — driver interfaces and profile types live under the types namespace (SDK source: src/types/oauth.ts):

import { types } from '@nodeblocks/backend-sdk';

type GoogleOAuthDriver = types.GoogleOAuthDriver;
type GoogleProfile = types.GoogleProfile;
type TwitterOAuthDriver = types.TwitterOAuthDriver;
type TwitterProfile = types.TwitterProfile;
type TwitterCallbackState = types.TwitterCallbackState;
type TwitterRequest = types.TwitterRequest;
type LineOAuthDriver = types.LineOAuthDriver;
type LineProfile = types.LineProfile;
// OAUTH_LOGIN, OAUTH_SIGNUP, OAuthLoginState, ...

Callback URLs: Register the same redirect URI in your OAuth provider console and pass it to the driver factory. SDK OAuth routes use /auth/oauth/{provider}/callback (for example /auth/oauth/google/callback). Your callbackURL must match both the provider registration and the route path mounted by Authentication Service.


📋 Available OAuth Drivers

Google OAuth Driver

Integrates the Passport.js Google OAuth 2.0 strategy (passport-google-oauth20).

createGoogleOAuthDriver

ParameterTypeDefaultDescription
clientIDstringGoogle OAuth client ID
clientSecretstringGoogle OAuth client secret
callbackURLstringRedirect URL after OAuth completion
scopestring[]['email', 'profile']OAuth scopes
verifytypeof verifyGoogleCallbackverifyGoogleCallbackOptional verify function

Returns: GoogleOAuthDriver

const googleDriver = createGoogleOAuthDriver(
process.env.GOOGLE_CLIENT_ID!,
process.env.GOOGLE_CLIENT_SECRET!,
'https://app.com/auth/oauth/google/callback'
);

googleDriver.initialize(app);

Google driver methods

The strategy uses passReqToCallback: true — custom verify functions receive req as the first argument (the default verifyGoogleCallback ignores it).

MethodSignatureReturns
initialize(app: Express) => voidRegisters passport.initialize()
request(req, res, state: string) => Promise<void>Starts OAuth with { prompt: 'consent', state }
callback(req, res) => Promise<GoogleProfile>{ displayName, email, id } with session: false

Twitter OAuth Driver

Custom OAuth 2.0 strategy with PKCE (not the legacy passport-twitter package).

createTwitterOAuthDriver

ParameterTypeDefaultDescription
clientIDstringTwitter application App ID
clientSecretstringTwitter application App Secret
callbackURLstringCallback URL for Twitter OAuth
sessionSecretstringSecret for express-session
verifyVerifyFunctionWithRequestverifyTwitterCallbackOptional verify function

Returns: TwitterOAuthDriver

Session middleware (registered in initialize on ['/auth/oauth/twitter', '/auth/oauth/twitter/callback']):

SettingValue
resavefalse
saveUninitializedfalse
CookiehttpOnly, sameSite: 'lax', secure: 'auto', maxAge: 10 minutes

Strategy defaults (built into TwitterStrategy — not configurable via the factory):

SettingValue
Scopes['users.read', 'tweet.read']
PKCEtrue
Authorization URLhttps://twitter.com/i/oauth2/authorize
Token URLhttps://api.twitter.com/2/oauth2/token
Profile URLhttps://api.twitter.com/2/users/me
const twitterDriver = createTwitterOAuthDriver(
process.env.TWITTER_CLIENT_ID!,
process.env.TWITTER_CLIENT_SECRET!,
'https://app.com/auth/oauth/twitter/callback',
process.env.SESSION_SECRET!
);

twitterDriver.initialize(app);

Twitter driver methods

MethodSignatureReturns
initialize(app: Express) => voidSession middleware on Twitter routes, then passport.initialize()
request(req: TwitterRequest, res, state: TwitterCallbackState) => Promise<void>Stores state in req.session[TWITTER_CALLBACK_STATE_SESSION_KEY], starts OAuth
callback(req: TwitterRequest, res) => Promise<{ state: TwitterCallbackState; user: TwitterProfile }>Session state + user profile

TwitterCallbackState shape: { typeId, redirectUrl, purpose }purpose values align with types.OAUTH_LOGIN / types.OAUTH_SIGNUP.

Callback errors:

ConditionError
Passport returns no userAuthenticationOAuthError with status/info from Passport
Session state missingAuthenticationOAuthError: 'Twitter OAuth authentication failed. Session state required for executing callback not found.'

On success, callback always returns { state, user }state is required at runtime (the TypeScript return type marks it optional, but the implementation rejects when absent).


LINE OAuth Driver

Custom OAuth 2.0 strategy for LINE Login.

createLineOAuthDriver

ParameterTypeDefaultDescription
clientIDstringLINE channel ID
clientSecretstringLINE channel secret
callbackURLstringRedirect URL after OAuth completion
scopestring[]['profile', 'openid', 'email']OAuth scopes (overrides strategy default)
verifyVerifyFunctionWithRequestverifyLineCallbackOptional verify function

Returns: LineOAuthDriver

Strategy defaults (built into LineStrategy):

SettingValue
Authorization URLhttps://access.line.me/oauth2/v2.1/authorize
Token URLhttps://api.line.me/oauth2/v2.1/token
Profile URLhttps://api.line.me/oauth2/v2.1/userinfo
Built-in scope (before factory override)['openid']

The factory scope parameter overrides the built-in default.

const lineDriver = createLineOAuthDriver(
process.env.LINE_CHANNEL_ID!,
process.env.LINE_CHANNEL_SECRET!,
'https://app.com/auth/oauth/line/callback'
);

lineDriver.initialize(app);

LINE driver methods

MethodSignatureReturns
initialize(app: Express) => voidRegisters passport.initialize()
request(req, res, state: string) => Promise<void>Starts OAuth with { session: false, state }
callback(req, res) => Promise<LineProfile>{ name, sub } with session: false

Types vs implementation: LineOAuthDriver.request is typed as LineRequestAuthentication in src/types/oauth.ts, which includes an optional scope parameter — the driver implementation accepts (req, res, state) only.

Callback errors: When Passport returns no user, callback rejects with AuthenticationOAuthError (including status/info from Passport). Verify failures also reject the returned Promise.


Custom verify functions

Each factory accepts an optional verify parameter (defaults shown in the factory tables above). Default verify functions are exported from the drivers namespace:

const { verifyGoogleCallback, verifyTwitterCallback, verifyLineCallback } = drivers;

verifyGoogleCallback

Validates profile.emails[0].value and profile.id, then passes { displayName, email, id } to Passport. displayName may be undefined at runtime if Google omits it; only email and id are validated. Missing required data produces AuthenticationBadRequestError with message 'Google oauth payload error'.

verifyTwitterCallback

Validates profile, profile.id, and profile.username, then passes { displayName: profile.name, id, username } to Passport. displayName may be undefined at runtime if Twitter omits name. Missing data produces AuthenticationOAuthError.

verifyLineCallback

Validates profile.sub, then passes { name: profile?.name, sub } to Passport. name may be undefined at runtime if LINE omits it. Custom verify functions receive LineUserInfoPayload from the internal strategy (src/drivers/oauth/line/strategy.ts); it is not exported from drivers. Missing sub produces AuthenticationOAuthError with message 'Cannot get sub from line oauth payload'.

Verify failures call Passport done(error) — the driver callback Promise rejects.


🔧 Using OAuth Drivers

With Services

Create drivers, call initialize(app) on each, then inject via the third-argument options on authService:

import { services, drivers } from '@nodeblocks/backend-sdk';

const { authService } = services;
const {
createGoogleOAuthDriver,
createTwitterOAuthDriver,
createLineOAuthDriver,
} = drivers;

const googleOAuthDriver = createGoogleOAuthDriver(
process.env.GOOGLE_CLIENT_ID!,
process.env.GOOGLE_CLIENT_SECRET!,
'https://app.com/auth/oauth/google/callback'
);

const twitterOAuthDriver = createTwitterOAuthDriver(
process.env.TWITTER_CLIENT_ID!,
process.env.TWITTER_CLIENT_SECRET!,
'https://app.com/auth/oauth/twitter/callback',
process.env.SESSION_SECRET!
);

const lineOAuthDriver = createLineOAuthDriver(
process.env.LINE_CHANNEL_ID!,
process.env.LINE_CHANNEL_SECRET!,
'https://app.com/auth/oauth/line/callback'
);

googleOAuthDriver.initialize(app);
twitterOAuthDriver.initialize(app);
lineOAuthDriver.initialize(app);

authService(
dataStores,
{
authSecrets: {
authEncSecret: process.env.AUTH_ENC_SECRET!,
authSignSecret: process.env.AUTH_SIGN_SECRET!,
},
},
{ googleOAuthDriver, twitterOAuthDriver, lineOAuthDriver }
);

See Authentication Service for full Express wiring, data stores, session/cookie setup, OAuth env vars, and other third-argument options (mailService, etc.).