Skip to main content
Version: 0.14.0 (Previous)

🔐 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
clientIDstring—Google OAuth client ID
clientSecretstring—Google OAuth client secret
callbackURLstring—Redirect 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
clientIDstring—Twitter application App ID
clientSecretstring—Twitter application App Secret
callbackURLstring—Callback URL for Twitter OAuth
sessionSecretstring—Secret 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
clientIDstring—LINE channel ID
clientSecretstring—LINE channel secret
callbackURLstring—Redirect 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.).