Skip to main content
Version: 0.15.0 (Latest)

🛣️ OAuth routes

OAuth routes are SDK composers, not Express middleware. Provider drivers perform browser redirects; callback routes create a one-time login token and redirect the browser.

Inventory​

RouteMethod / protocolPathSchemaValidatorsSuccess status
googleOAuthRouteGET / HTTP/auth/oauth/googlegoogleOauthSchemaNoneDriver redirect
googleOAuthCallbackRouteGET / HTTP/auth/oauth/google/callbackNoneNone302 redirect
lineOAuthRouteGET / HTTP/auth/oauth/linelineOauthSchemaNoneDriver redirect
lineOAuthCallbackRouteGET / HTTP/auth/oauth/line/callbackNoneNone302 redirect
twitterOAuthRouteGET / HTTP/auth/oauth/twittertwitterOauthSchemaNoneDriver redirect
twitterOAuthCallbackRouteGET / HTTP/auth/oauth/twitter/callbackNoneNone302 redirect

Details​

googleOAuthRoute​

Implementation

Endpoint: GET /auth/oauth/google

Access: Public; validators: [].

Request: googleOauthSchema validates fp, purpose, redirectUrl, optional typeId, and an optional empty JSON object.

Pipeline: validateURL → requestGoogleOAuth → orThrow.

Redirect validation: Checks params.requestQuery.redirectUrl against configuration.oauth.allowedRedirectOrigins before preparing provider state or invoking the driver. Malformed/disallowed URLs return AuthenticationInvalidInputError (400) and stop the provider request. Missing/empty origins produce AuthenticationUnexpectedError; the initiation route maps it to 500.

Success: The injected googleOAuthDriver.request handles the provider redirect.

Failure: AuthenticationInvalidTokenError and AuthenticationInvalidInputError map to 400; AuthenticationUnexpectedError maps to 500.

View complete source
export const googleOAuthRoute = withRoute({
handler: compose(
withLogging(
applyPayloadArgs(
validateURL,
[
['context', 'configuration', 'oauth', 'allowedRedirectOrigins'],
['params', 'requestQuery', 'redirectUrl'],
],
'isRedirectUrlValid',
),
),
flatMapAsync(
withLogging(
applyPayloadArgs(requestGoogleOAuth, [
['context', 'configuration', 'authSecrets'],
['context', 'googleOAuthDriver', 'request'],
['context', 'request'],
['context', 'response'],
['params', 'requestQuery', 'fp'],
['params', 'requestQuery', 'purpose'],
['params', 'requestQuery', 'redirectUrl'],
['params', 'requestQuery', 'typeId'],
['context', 'configuration', 'onetimeTokenSignOptions'],
]),
),
),
lift(
withLogging(
orThrow([
[AuthenticationInvalidTokenError, 400],
[AuthenticationInvalidInputError, 400],
[AuthenticationUnexpectedError, 500],
]),
),
),
),
method: 'GET',
path: '/auth/oauth/google',
validators: [],
});

googleOAuthCallbackRoute​

Implementation

Endpoint: GET /auth/oauth/google/callback

Access: Public; validators: [].

Request: No composed schema. The route reads provider callback data and requestQuery.state directly.

Pipeline: authenticateGoogleOAuth → extractOAuthLoginState → validateURL → signup-only checkEmailIsUniqueInIdentities → verifyGoogleOAuth → getLoginTokenTarget → getFingerprint → buildTokenVerification → generateOneTimeToken → storeOneTimeToken → generateRedirectURL → redirectTo → orThrow.

Redirect validation: After extracting the signed login state, before the signup email uniqueness check, identity resolution and token generation, validateURL checks loginState.redirectUrl against configuration.oauth.allowedRedirectOrigins. A valid absolute URL with an allowed origin is required before generating the redirect. Invalid/disallowed URLs map to AuthenticationInvalidInputError (400); undefined/empty origins map to AuthenticationUnexpectedError (500). See configuration.

Success: redirectTo sends 302 using the state's redirect URL with onetimeToken appended. generateRedirectURL preserves its percent encoding without additional decoding.

Failure: Invalid token/input is 400, no identity is 404, and database or missing-origin configuration failures are 500. A driver exception before a Result escapes to the service error handler.

View complete source
export const googleOAuthCallbackRoute = withRoute({
handler: compose(
withLogging(
applyPayloadArgs(
authenticateGoogleOAuth,
[
['context', 'googleOAuthDriver', 'callback'],
['context', 'request'],
['context', 'response'],
],
'profile',
),
),
flatMapAsync(
withLogging(
applyPayloadArgs(
extractOAuthLoginState,
[
['context', 'configuration', 'authSecrets'],
['params', 'requestQuery', 'state'],
],
'loginState',
),
),
),
flatMapAsync(
withLogging(
applyPayloadArgs(
validateURL,
[
['context', 'configuration', 'oauth', 'allowedRedirectOrigins'],
['context', 'data', 'loginState', 'redirectUrl'],
],
'isRedirectUrlValid',
),
),
),
flatMapAsync(
ifElse(
(input: RouteHandlerPayload) => input?.context?.data?.loginState?.purpose === OAUTH_SIGNUP,
withLogging(
applyPayloadArgs(
checkEmailIsUniqueInIdentities,
[
['context', 'db', 'identities'],
['context', 'data', 'profile', 'email'],
],
'isEmailUnique',
),
),
async input => ok(input),
),
),
flatMapAsync(
withLogging(
applyPayloadArgs(
verifyGoogleOAuth,
[
['context', 'db', 'identities'],
['context', 'data', 'loginState'],
['context', 'data', 'profile'],
],
'identityId',
),
),
),
flatMapAsync(applyPayloadArgs(getLoginTokenTarget, [], 'target')),
flatMapAsync(withLogging(applyPayloadArgs(getFingerprint, [['context', 'request', 'headers']], 'fingerprint'))),
flatMapAsync(
withLogging(
applyPayloadArgs(
buildTokenVerification,
[
['context', 'request'],
['context', 'data', 'target'],
['context', 'data', 'fingerprint'],
],
'tokenVerification',
),
),
),
flatMapAsync(
withLogging(
applyPayloadArgs(
generateOneTimeToken,
[
['context', 'configuration', 'authSecrets'],
['context', 'configuration', 'onetimeTokenSignOptions'],
['context', 'data', 'tokenVerification'],
['context', 'data', 'identityId'],
],
'token',
),
),
),
flatMapAsync(
withLogging(
applyPayloadArgs(
storeOneTimeToken,
[
['context', 'db', 'onetimetokens'],
['context', 'data', 'token'],
],
'isStoredOneTimeToken',
),
),
),
flatMapAsync(
withLogging(
applyPayloadArgs(
generateRedirectURL,
[
['context', 'data', 'token'],
['context', 'data', 'loginState', 'redirectUrl'],
],
'redirectUrl',
),
),
),
flatMapAsync(
withLogging(
applyPayloadArgs(redirectTo, [
['context', 'response'],
['context', 'data', 'redirectUrl'],
]),
),
),
lift(
withLogging(
orThrow([
[AuthenticationInvalidTokenError, 400],
[AuthenticationInvalidInputError, 400],
[AuthenticationNotFoundError, 404],
[AuthenticationUnexpectedDBError, 500],
[AuthenticationUnexpectedError, 500],
]),
),
),
),
method: 'GET',
path: '/auth/oauth/google/callback',
validators: [],
});

lineOAuthRoute​

Implementation

Endpoint: GET /auth/oauth/line

Access: Public; validators: [].

Request: lineOauthSchema validates fp, purpose, redirectUrl, optional typeId, and an optional empty JSON object.

Pipeline: validateURL → requestLineOAuth → orThrow.

Redirect validation: Checks params.requestQuery.redirectUrl against configuration.oauth.allowedRedirectOrigins before preparing provider state or invoking the driver. Malformed/disallowed URLs return AuthenticationInvalidInputError (400) and stop the provider request. Missing/empty origins produce AuthenticationUnexpectedError; the initiation route maps it to 500.

Success: The injected lineOAuthDriver.request handles the provider redirect.

Failure: AuthenticationInvalidInputError maps to 400; AuthenticationUnexpectedError maps to 500. Other errors propagate without an explicit route mapping.

View complete source
export const lineOAuthRoute = withRoute({
handler: compose(
withLogging(
applyPayloadArgs(
validateURL,
[
['context', 'configuration', 'oauth', 'allowedRedirectOrigins'],
['params', 'requestQuery', 'redirectUrl'],
],
'isRedirectUrlValid',
),
),
flatMapAsync(
withLogging(
applyPayloadArgs(
requestLineOAuth,
[
['context', 'configuration', 'authSecrets'],
['context', 'lineOAuthDriver', 'request'],
['context', 'request'],
['context', 'response'],
['params', 'requestQuery', 'fp'],
['params', 'requestQuery', 'purpose'],
['params', 'requestQuery', 'redirectUrl'],
['params', 'requestQuery', 'typeId'],
['context', 'configuration', 'onetimeTokenSignOptions'],
],
'state',
),
),
),
lift(
withLogging(
orThrow([
[AuthenticationInvalidInputError, 400],
[AuthenticationUnexpectedError, 500],
]),
),
),
),
method: 'GET',
path: '/auth/oauth/line',
validators: [],
});

lineOAuthCallbackRoute​

Implementation

Endpoint: GET /auth/oauth/line/callback

Access: Public; validators: [].

Request: No composed schema; reads provider callback data and requestQuery.state.

Pipeline: authenticateLineOAuth → extractOAuthLoginState → validateURL → verifyLineOAuth → getLoginTokenTarget → getFingerprint → buildTokenVerification → generateOneTimeToken → storeOneTimeToken → generateRedirectURL → redirectTo → orThrow.

Redirect validation: After extracting the signed login state, before identity resolution and token generation, validateURL checks loginState.redirectUrl against configuration.oauth.allowedRedirectOrigins. A valid absolute URL with an allowed origin is required before generating the redirect. Invalid/disallowed URLs map to AuthenticationInvalidInputError (400); undefined/empty origins map to AuthenticationUnexpectedError (500). See configuration.

Success: 302 redirect with the generated onetimeToken. generateRedirectURL preserves the state redirect URL's percent encoding without additional decoding.

Failure: Input 400, missing identity 404, database, missing-origin configuration, or OAuth error 500; a thrown driver callback bypasses its Result wrapper.

View complete source
export const lineOAuthCallbackRoute = withRoute({
handler: compose(
withLogging(
applyPayloadArgs(
authenticateLineOAuth,
[
['context', 'lineOAuthDriver', 'callback'],
['context', 'request'],
['context', 'response'],
],
'profile',
),
),
flatMapAsync(
withLogging(
applyPayloadArgs(
extractOAuthLoginState,
[
['context', 'configuration', 'authSecrets'],
['params', 'requestQuery', 'state'],
],
'loginState',
),
),
),
flatMapAsync(
withLogging(
applyPayloadArgs(
validateURL,
[
['context', 'configuration', 'oauth', 'allowedRedirectOrigins'],
['context', 'data', 'loginState', 'redirectUrl'],
],
'isRedirectUrlValid',
),
),
),
flatMapAsync(
withLogging(
applyPayloadArgs(
verifyLineOAuth,
[
['context', 'db', 'identities'],
['context', 'data', 'loginState'],
['context', 'data', 'profile'],
],
'identityId',
),
),
),
flatMapAsync(applyPayloadArgs(getLoginTokenTarget, [], 'target')),
flatMapAsync(withLogging(applyPayloadArgs(getFingerprint, [['context', 'request', 'headers']], 'fingerprint'))),
flatMapAsync(
withLogging(
applyPayloadArgs(
buildTokenVerification,
[
['context', 'request'],
['context', 'data', 'target'],
['context', 'data', 'fingerprint'],
],
'tokenVerification',
),
),
),
flatMapAsync(
withLogging(
applyPayloadArgs(
generateOneTimeToken,
[
['context', 'configuration', 'authSecrets'],
['context', 'configuration', 'onetimeTokenSignOptions'],
['context', 'data', 'tokenVerification'],
['context', 'data', 'identityId'],
],
'token',
),
),
),
flatMapAsync(
withLogging(
applyPayloadArgs(
storeOneTimeToken,
[
['context', 'db', 'onetimetokens'],
['context', 'data', 'token'],
],
'isStoredOneTimeToken',
),
),
),
flatMapAsync(
withLogging(
applyPayloadArgs(
generateRedirectURL,
[
['context', 'data', 'token'],
['context', 'data', 'loginState', 'redirectUrl'],
],
'redirectUrl',
),
),
),
flatMapAsync(
withLogging(
applyPayloadArgs(redirectTo, [
['context', 'response'],
['context', 'data', 'redirectUrl'],
]),
),
),
lift(
withLogging(
orThrow([
[AuthenticationInvalidInputError, 400],
[AuthenticationNotFoundError, 404],
[AuthenticationUnexpectedDBError, 500],
[AuthenticationUnexpectedError, 500],
[AuthenticationOAuthError, 500],
]),
),
),
),
method: 'GET',
path: '/auth/oauth/line/callback',
validators: [],
});

twitterOAuthRoute​

Implementation

Endpoint: GET /auth/oauth/twitter

Access: Public; validators: [].

Request: twitterOauthSchema validates purpose, redirectUrl, optional typeId, and an optional empty JSON object.

Pipeline: validateURL → prepareTwitterCallbackState → requestTwitterOAuth → orThrow.

Redirect validation: Checks params.requestQuery.redirectUrl against configuration.oauth.allowedRedirectOrigins before preparing provider state or invoking the driver. Malformed/disallowed URLs return AuthenticationInvalidInputError (400) and stop the provider request. Missing/empty origins produce AuthenticationUnexpectedError; the initiation route maps it to 500.

Success: The injected twitterOAuthDriver.request handles the provider redirect.

Failure: AuthenticationInvalidInputError maps to 400; AuthenticationUnexpectedError maps to 500. Other errors propagate without an explicit route mapping.

View complete source
export const twitterOAuthRoute = withRoute({
handler: compose(
withLogging(
applyPayloadArgs(
validateURL,
[
['context', 'configuration', 'oauth', 'allowedRedirectOrigins'],
['params', 'requestQuery', 'redirectUrl'],
],
'isRedirectUrlValid',
),
),
flatMapAsync(
withLogging(
applyPayloadArgs(
prepareTwitterCallbackState,
[
['params', 'requestQuery', 'purpose'],
['params', 'requestQuery', 'redirectUrl'],
['params', 'requestQuery', 'typeId'],
],
'state',
),
),
),
flatMapAsync(
withLogging(
applyPayloadArgs(requestTwitterOAuth, [
['context', 'twitterOAuthDriver', 'request'],
['context', 'request'],
['context', 'response'],
['context', 'data', 'state'],
]),
),
),
lift(
withLogging(
orThrow([
[AuthenticationInvalidInputError, 400],
[AuthenticationUnexpectedError, 500],
]),
),
),
),
method: 'GET',
path: '/auth/oauth/twitter',
validators: [],
});

twitterOAuthCallbackRoute​

Implementation

Endpoint: GET /auth/oauth/twitter/callback

Access: Public; validators: [].

Request: No composed schema; Twitter's driver supplies callback state.

Pipeline: authenticateTwitterOAuth → validateURL → verifyTwitterOAuth → getLoginTokenTarget → getFingerprint → buildTokenVerification → generateOneTimeToken → storeOneTimeToken → generateRedirectURL → redirectTo → orThrow.

Redirect validation: After the provider driver callback returns state and profile, before identity resolution and token generation, validateURL checks callbackResult.state.redirectUrl against configuration.oauth.allowedRedirectOrigins. A valid absolute URL with an allowed origin is required before generating the redirect. Invalid/disallowed URLs map to AuthenticationInvalidInputError (400); undefined/empty origins map to AuthenticationUnexpectedError (500). See configuration.

Success: 302 redirect with the generated onetimeToken. generateRedirectURL preserves the state redirect URL's percent encoding without additional decoding.

Failure: Input 400, missing identity 404, database, missing-origin configuration, or OAuth failure 500.

View complete source
export const twitterOAuthCallbackRoute = withRoute({
handler: compose(
withLogging(
applyPayloadArgs(
authenticateTwitterOAuth,
[
['context', 'twitterOAuthDriver', 'callback'],
['context', 'request'],
['context', 'response'],
],
'callbackResult',
),
),
flatMapAsync(
withLogging(
applyPayloadArgs(
validateURL,
[
['context', 'configuration', 'oauth', 'allowedRedirectOrigins'],
['context', 'data', 'callbackResult', 'state', 'redirectUrl'],
],
'isRedirectUrlValid',
),
),
),
flatMapAsync(
withLogging(
applyPayloadArgs(
verifyTwitterOAuth,
[
['context', 'db', 'identities'],
['context', 'data', 'callbackResult', 'state', 'purpose'],
['context', 'data', 'callbackResult', 'state', 'typeId'],
['context', 'data', 'callbackResult', 'user'],
],
'identityId',
),
),
),
flatMapAsync(applyPayloadArgs(getLoginTokenTarget, [], 'target')),
flatMapAsync(withLogging(applyPayloadArgs(getFingerprint, [['context', 'request', 'headers']], 'fingerprint'))),
flatMapAsync(
withLogging(
applyPayloadArgs(
buildTokenVerification,
[
['context', 'request'],
['context', 'data', 'target'],
['context', 'data', 'fingerprint'],
],
'tokenVerification',
),
),
),
flatMapAsync(
withLogging(
applyPayloadArgs(
generateOneTimeToken,
[
['context', 'configuration', 'authSecrets'],
['context', 'configuration', 'onetimeTokenSignOptions'],
['context', 'data', 'tokenVerification'],
['context', 'data', 'identityId'],
],
'token',
),
),
),
flatMapAsync(
withLogging(
applyPayloadArgs(
storeOneTimeToken,
[
['context', 'db', 'onetimetokens'],
['context', 'data', 'token'],
],
'isStoredOneTimeToken',
),
),
),
flatMapAsync(
withLogging(
applyPayloadArgs(
generateRedirectURL,
[
['context', 'data', 'token'],
['context', 'data', 'callbackResult', 'state', 'redirectUrl'],
],
'redirectUrl',
),
),
),
flatMapAsync(
withLogging(
applyPayloadArgs(redirectTo, [
['context', 'response'],
['context', 'data', 'redirectUrl'],
]),
),
),
lift(
withLogging(
orThrow([
[AuthenticationInvalidInputError, 400],
[AuthenticationNotFoundError, 404],
[AuthenticationUnexpectedDBError, 500],
[AuthenticationUnexpectedError, 500],
[AuthenticationOAuthError, 500],
]),
),
),
),
method: 'GET',
path: '/auth/oauth/twitter/callback',
validators: [],
});