メインコンテンツまでスキップ
バージョン: 0.13.0 (Previous)

🛣️ 認証ルート

ルートは Express ミドルウェアではなく SDK のコンポーザーです。共通タスクマップを使用してエンドポイントを選択し、authServiceでサポートされる API をマウントしてください。個々のルートを読む前に Bearer ワークフローを試してください。以下の各ルートはスキーマとフィーチャへのリンクを含み、リクエスト検証と合成が表示されたままになります。

一覧

ルートメソッド / プロトコルパススキーマバリデーター成功ステータス
registerCredentialsRoutePOST / HTTP/auth/registerregisterCredentialsSchemaなし201
loginWithCredentialsRoutePOST / HTTP/auth/loginloginWithCredentialsSchemaなし200
resendMfaCodeRoutePOST / HTTP/auth/mfa/resendresendMfaCodeSchemaなし200
verifyMfaCodeRoutePOST / HTTP/auth/mfa/verifyverifyMfaCodeSchemaなし200
logoutRoutePOST / HTTP/auth/logoutlogoutCookieSchema または logoutBearerSchemaisAuthenticated()204
refreshTokenRoutePOST / HTTP/auth/token/refreshrefreshTokenCookieSchema または refreshTokenBearerSchemaなし; ハンドラーに有効なリフレッシュトークンが必要Cookie 204; Bearer 200
checkTokenRoutePOST / HTTP/auth/token/checkcheckTokenSchemaなし200
deleteRefreshTokensRouteDELETE / HTTP/auth/:identityId/refresh-tokensdeleteRefreshTokensSchemaisAuthenticated()some(admin, self)204
loginWithOnetimeTokenRoutePOST / HTTP/auth/ott/loginloginWithOnetimeTokenSchemaなし200
generateOnetimeTokenRoutePOST / HTTP/auth/ott/generateなしisAuthenticated()checkIdentityType(['admin'])200
restoreOnetimeTokenRoutePOST / HTTP/auth/ott/restoreなしisAuthenticated()checkIdentityType(['admin'])200
invalidateOnetimeTokenRoutePOST / HTTP/auth/ott/invalidateなしisAuthenticated()checkIdentityType(['admin'])200
sendVerificationEmailRoutePOST / HTTP/auth/:identityId/send-verification-emailsendVerificationEmailSchemaisAuthenticated()some(admin, self)204
confirmEmailRoutePOST / HTTP/auth/confirm-emailconfirmEmailSchemaなし204
changeEmailRoutePATCH / HTTP/auth/:identityId/change-emailchangeEmailSchemaisAuthenticated()some(admin, self)204
confirmNewEmailRoutePOST / HTTP/auth/confirm-new-emailconfirmNewEmailSchemaなし204
sendResetPasswordLinkEmailRoutePOST / HTTP/auth/send-reset-password-link-emailsendResetPasswordLinkEmailSchemaなし204
completePasswordResetRoutePOST / HTTP/auth/reset-passwordcompletePasswordResetSchemaなし204
changePasswordRoutePATCH / HTTP/auth/:identityId/change-passwordchangePasswordSchemaisAuthenticated()some(admin, self)204
deactivateRoutePOST / HTTP/auth/deactivatedeactivateSchemaisAuthenticated()some(admin, self)204
activateRoutePOST / HTTP/auth/activateactivateSchemaisAuthenticated()checkIdentityType(['admin'])204
共有ソースコンテキストを表示
import {ok} from 'neverthrow';
import {identity as noop, pick, tap} from 'ramda';

import {
assertDoesNotMatch,
assertIdentityExists,
assertMatches,
assertValidOneTimeTokenExists,
AuthenticationBadRequestError,
AuthenticationConflictError,
AuthenticationForbiddenError,
AuthenticationInvalidInputError,
AuthenticationInvalidTokenError,
AuthenticationNotFoundError,
AuthenticationUnauthorizedError,
AuthenticationUnexpectedDBError,
AuthenticationUnexpectedDbError,
AuthenticationUnexpectedError,
AuthenticationUnprocessableEntityError,
buildTokenVerification,
buildUpdateIdentityActivatedPayload,
buildUpdateIdentityDeactivatedPayload,
buildUpdateIdentityEmailAndEmailVerifiedPayload,
buildUpdateIdentityPasswordPayload,
checkEmailIsUniqueInIdentities,
checkOneTimeToken,
checkToken as checkTokenBlock,
compareStringAgainstHash,
createMfaCode,
createMfaToken,
extractTokenFromAuthorizationHeader,
generateOneTimeToken,
getChangeEmailTokenTarget,
getFingerprint,
getIdentityIdByEmail,
getMfaChallengeTokenTarget,
getResetPasswordTokenTarget,
hash,
invalidateOneTimeToken,
isEmail,
isEmailVerified,
MfaInvalidCodeError,
MfaUnexpectedError,
normalizeBearerLoginResponse,
normalizeBearerRefreshResponse,
normalizeCookieLoginResponse,
sendEmail,
sendMfaCode,
softDeleteRefreshTokens,
storeOneTimeToken,
verifyMfaCode,
} from '../blocks/authentication';
import {normalizeEmptyBody} from '../blocks/common';
import {getIdentityById, updateIdentity} from '../blocks/identity';
import {
buildAcceptInvitationPayload,
buildCheckConfirmEmailTokenPayload,
buildCheckInvitationTokenPayload,
checkToken,
confirmEmail,
confirmEmailTerminator,
createAccessToken,
createRefreshToken,
generateOnetimeToken,
getInvitationById,
getInvitationIdFromTokenInfo,
invalidateOnetimeToken,
isPendingInvitation,
loginWithCredentials,
loginWithOnetimeToken,
logout,
logoutTerminator,
refreshToken,
registerCredentials,
registerTerminator,
restoreOnetimeToken,
sendVerificationEmail,
sendVerificationEmailTerminator,
setResponseCookie,
updateInvitation,
} from '../handlers';
import {
applyPayloadArgs,
compose,
flatMapAsync,
ifElse,
lift,
mapMatchingErrorToFalse,
match,
orThrow,
RouteHandlerPayload,
withLogging,
withRoute,
} from '../primitives';
import {whenCookieAuth} from '../utils/cookie';
import {checkIdentityType, isAuthenticated, isSelf, some} from '../validators';

詳細

registerCredentialsRoute

実装

エンドポイント: POST /auth/register

資格情報を使用して登録します。

アクセス: パブリック。

リクエスト: registerCredentialsSchema が JSON の資格情報と任意の招待トークンを検証します。

パイプライン: 招待トークン付きのリクエストでは、checkTokenregisterCredentials の前後で Invitation ハンドラーを実行します。通常のリクエストでは registerCredentials を直接実行し、続けて registerTerminator を実行します。

成功: {email, id} を含む 201 を返します。

失敗: 400 は不足または不正な入力、401 は無効な招待トークン、404 は存在しない招待、422 は重複するアイデンティティ、500 は永続化の失敗です。

完全なソースを表示
export const registerCredentialsRoute = withRoute({
handler: compose(
ifElse(
match(Boolean, ['params', 'requestBody', 'token']),
compose(
withLogging(buildCheckInvitationTokenPayload),
flatMapAsync(withLogging(checkToken)),
flatMapAsync(withLogging(getInvitationIdFromTokenInfo)),
flatMapAsync(withLogging(getInvitationById)),
flatMapAsync(withLogging(isPendingInvitation)),
flatMapAsync(withLogging(registerCredentials)),
flatMapAsync(withLogging(buildAcceptInvitationPayload)),
flatMapAsync(withLogging(updateInvitation)),
),
withLogging(registerCredentials),
),
// TODO: flatMapAsync(withLogging(sendRegistrationCompleteEmail)),
lift(withLogging(registerTerminator)),
),
method: 'POST',
path: '/auth/register',
validators: [],
});

loginWithCredentialsRoute

実装

エンドポイント: POST /auth/login

資格情報を使用してログインします。

アクセス: パブリック。

リクエスト: loginWithCredentialsSchema が JSON のメールアドレス、パスワード、および任意のフィンガープリントを検証します。

パイプライン: loginWithCredentials を実行します。MFA 分岐ではチャレンジを生成して送信し、セッション分岐では createAccessTokencreateRefreshToken、任意の setResponseCookie、モード固有の正規化を実行します。

成功: MFA トークン、cookie モードの cookie 付き {id}、または Bearer の {accessToken, id, refreshToken} を含む 200 を返します。

失敗: ロック済みまたは誤った資格情報は 401、MFA の失敗は 400 または 500 です。

完全なソースを表示
export const loginWithCredentialsRoute = withRoute({
handler: compose(
ifElse(
match(Boolean, ['context', 'configuration', 'isMfaEnabled']),
compose(
withLogging(loginWithCredentials),
flatMapAsync(
withLogging(applyPayloadArgs(createMfaCode, [['context', 'configuration', 'mfaCodeLength']], 'mfaCode')),
),
flatMapAsync(
withLogging(
applyPayloadArgs(
createMfaToken,
[
['context', 'db', 'onetimetokens'],
['context', 'configuration', 'authSecrets'],
['context', 'configuration', 'onetimeTokenSignOptions'],
['params', 'requestBody', 'fingerprint'],
['context', 'data', 'identity', 'id'],
['context', 'data', 'identity', 'email'],
['context', 'data', 'mfaCode'],
],
'token',
),
),
),
flatMapAsync(
withLogging(
applyPayloadArgs(
sendMfaCode,
[
['context', 'mailService'],
['context', 'configuration', 'mfaCodeEmailConfig', 'emailConfig', 'subject'],
['context', 'configuration', 'mfaCodeEmailConfig', 'sender'],
['context', 'configuration', 'mfaCodeEmailConfig', 'emailConfig', 'bodyTemplate'],
['context', 'data', 'identity', 'email'],
['context', 'data', 'mfaCode'],
],
'hasSentMfaCode',
),
),
),
flatMapAsync(withLogging(applyPayloadArgs(pick(['token']), [['context', 'data']], 'mfaTokenObj'))),
lift(orThrow([[MfaUnexpectedError, 500]], [['context', 'data', 'mfaTokenObj'], 200])),
),
compose(
withLogging(loginWithCredentials),
flatMapAsync(withLogging(createAccessToken)),
flatMapAsync(withLogging(createRefreshToken)),
flatMapAsync(
withLogging(
whenCookieAuth(
compose(
withLogging(setResponseCookie),
flatMapAsync(
withLogging(applyPayloadArgs(normalizeCookieLoginResponse, [['context', 'data']], 'loginResponse')),
),
),
withLogging(applyPayloadArgs(normalizeBearerLoginResponse, [['context', 'data']], 'loginResponse')),
),
),
),
lift(withLogging(orThrow([], [['context', 'data', 'loginResponse']]))),
),
),
),
method: 'POST',
path: '/auth/login',
validators: [],
});

resendMfaCodeRoute

実装

エンドポイント: POST /auth/mfa/resend

代替の MFA チャレンジを発行します。

アクセス: 有効な MFA チャレンジトークンを持つ呼び出し元にはパブリックです。

リクエスト: resendMfaCodeSchema がトークンと任意のフィンガープリントを検証します。

パイプライン: getFingerprintgetMfaChallengeTokenTargetcheckTokencreateMfaCodecreateMfaTokensendMfaCode、レスポンス選択の順に実行します。

成功: 置き換え用チャレンジトークンを含む 200 を返します。

失敗: 400 は無効なトークンまたはコード入力、401 はトークン検証失敗、500 はトークン、データベース、またはメールの失敗です。

完全なソースを表示
export const resendMfaCodeRoute = withRoute({
handler: compose(
withLogging(applyPayloadArgs(getMfaChallengeTokenTarget, [], 'target')),
flatMapAsync(withLogging(applyPayloadArgs(getFingerprint, [['context', 'request', 'headers']], 'fingerprint'))),
flatMapAsync(
withLogging(
applyPayloadArgs(
checkOneTimeToken,
[
['context', 'configuration', 'authSecrets'],
['context', 'data', 'target'],
['params', 'requestBody', 'token'],
],
'tokenInfo',
),
),
),
flatMapAsync(
withLogging(
applyPayloadArgs(
assertValidOneTimeTokenExists,
[
['context', 'db', 'onetimetokens'],
['params', 'requestBody', 'token'],
],
'doesOneTimeTokenExist',
),
),
),
flatMapAsync(
withLogging(
applyPayloadArgs(
invalidateOneTimeToken,
[
['context', 'db', 'onetimetokens'],
['params', 'requestBody', 'token'],
],
'isInvalidatedSuccessfully',
),
),
),
flatMapAsync(
withLogging(
applyPayloadArgs(
getIdentityById,
[
['context', 'db', 'identities'],
['context', 'data', 'tokenInfo', 'data', 'identityId'],
],
'identity',
),
),
),
flatMapAsync(
withLogging(applyPayloadArgs(createMfaCode, [['context', 'configuration', 'mfaCodeLength']], 'mfaCode')),
),
flatMapAsync(
withLogging(
applyPayloadArgs(
createMfaToken,
[
['context', 'db', 'onetimetokens'],
['context', 'configuration', 'authSecrets'],
['context', 'configuration', 'onetimeTokenSignOptions'],
['params', 'requestBody', 'fingerprint'],
['context', 'data', 'identity', 'id'],
['context', 'data', 'identity', 'email'],
['context', 'data', 'mfaCode'],
],
'token',
),
),
),
flatMapAsync(
withLogging(
applyPayloadArgs(
sendMfaCode,
[
['context', 'mailService'],
['context', 'configuration', 'mfaCodeEmailConfig', 'emailConfig', 'subject'],
['context', 'configuration', 'mfaCodeEmailConfig', 'sender'],
['context', 'configuration', 'mfaCodeEmailConfig', 'emailConfig', 'bodyTemplate'],
['context', 'data', 'identity', 'email'],
['context', 'data', 'mfaCode'],
],
'hasSentMfaCode',
),
),
),
flatMapAsync(withLogging(applyPayloadArgs(pick(['token']), [['context', 'data']], 'mfaTokenObj'))),
lift(
orThrow(
[
[MfaInvalidCodeError, 400],
[MfaUnexpectedError, 500],
[AuthenticationUnauthorizedError, 401],
[AuthenticationForbiddenError, 403],
[AuthenticationInvalidInputError, 400],
[AuthenticationNotFoundError, 404],
[AuthenticationUnexpectedError, 500],
[AuthenticationUnexpectedDbError, 500],
[AuthenticationUnprocessableEntityError, 422],
],
[['context', 'data', 'mfaTokenObj'], 200],
),
),
),
method: 'POST',
path: '/auth/mfa/resend',
validators: [],
});

verifyMfaCodeRoute

実装

エンドポイント: POST /auth/mfa/verify

MFA チャレンジを検証します。

アクセス: チャレンジトークンとコードを持つ呼び出し元にはパブリックです。

リクエスト: verifyMfaCodeSchema がトークン、コード、フィンガープリントを検証。

パイプライン: ターゲット、フィンガープリント、トークンを確認し、トークンを無効化して verifyMfaCode を実行します。その後アイデンティティを取得し、createAccessTokencreateRefreshToken、任意の setResponseCookie、モード固有の正規化を実行します。

成功: cookie モードでは cookie 付き {id}、Bearer モードではセッショントークンを含む 200 を返します。

失敗: 400 は無効なコードまたは入力、401 は無効なトークン、403 は無効化済みトークン、404 は存在しないアイデンティティ、500 は永続化またはセッションの失敗です。

完全なソースを表示
export const verifyMfaCodeRoute = withRoute({
handler: compose(
applyPayloadArgs(getMfaChallengeTokenTarget, [], 'target'),
flatMapAsync(withLogging(applyPayloadArgs(getFingerprint, [['context', 'request', 'headers']], 'fingerprint'))),
flatMapAsync(
withLogging(
applyPayloadArgs(
checkOneTimeToken,
[
['context', 'configuration', 'authSecrets'],
['context', 'data', 'target'],
['params', 'requestBody', 'token'],
],
'tokenInfo',
),
),
),
flatMapAsync(
withLogging(
applyPayloadArgs(
assertValidOneTimeTokenExists,
[
['context', 'db', 'onetimetokens'],
['params', 'requestBody', 'token'],
],
'doesOneTimeTokenExist',
),
),
),
flatMapAsync(
withLogging(
applyPayloadArgs(
invalidateOneTimeToken,
[
['context', 'db', 'onetimetokens'],
['params', 'requestBody', 'token'],
],
'isInvalidatedSuccessfully',
),
),
),

flatMapAsync(
withLogging(
applyPayloadArgs(
verifyMfaCode,
[
['params', 'requestBody', 'code'],
['context', 'data', 'tokenInfo', 'data', 'code'],
],
'isCodeValid',
),
),
),
flatMapAsync(
withLogging(
applyPayloadArgs(
getIdentityById,
[
['context', 'db', 'identities'],
['context', 'data', 'tokenInfo', 'data', 'identityId'],
],
'identity',
),
),
),
flatMapAsync(withLogging(createAccessToken)),
flatMapAsync(withLogging(createRefreshToken)),
lift(
tap(
withLogging(
orThrow([
[MfaInvalidCodeError, 400],
[MfaUnexpectedError, 500],
[AuthenticationUnauthorizedError, 401],
[AuthenticationForbiddenError, 403],
[AuthenticationInvalidInputError, 400],
[AuthenticationNotFoundError, 404],
[AuthenticationUnexpectedError, 500],
[AuthenticationUnexpectedDbError, 500],
[AuthenticationUnprocessableEntityError, 422],
]),
),
),
),
flatMapAsync(
withLogging(
whenCookieAuth(
compose(
withLogging(setResponseCookie),
flatMapAsync(
withLogging(applyPayloadArgs(normalizeCookieLoginResponse, [['context', 'data']], 'loginResponse')),
),
),
withLogging(applyPayloadArgs(normalizeBearerLoginResponse, [['context', 'data']], 'loginResponse')),
),
),
),
lift(withLogging(orThrow([], [['context', 'data', 'loginResponse']]))),
),
method: 'POST',
path: '/auth/mfa/verify',
validators: [],
});

logoutRoute

実装

エンドポイント: POST /auth/logout — 現在のセッションを無効化します。認証済み(isAuthenticated())。

リクエスト: logoutCookieSchema または logoutBearerSchema

パイプライン: logoutlogoutTerminator

成功: 204。cookie がクリアされ、有効なリフレッシュレコードが取り消されます。失敗: 認証失敗、401 アイデンティティミスマッチ、または 500 取消失敗。

完全なソースを表示
export const logoutRoute = withRoute({
handler: compose(withLogging(logout), lift(withLogging(logoutTerminator))),
method: 'POST',
path: '/auth/logout',
validators: [isAuthenticated()],
});

refreshTokenRoute

実装

エンドポイント: POST /auth/token/refresh

アクセス/リフレッシュトークンのペアをローテーションします。

アクセス: 有効なリフレッシュトークンを所持する呼び出し元。ルートバリデーターはありません。

リクエスト: refreshTokenCookieSchema または refreshTokenBearerSchema

パイプライン: refreshToken、任意の setResponseCookie、cookie の空本文または Bearer トークンの正規化を順に実行します。

成功: cookie モードではローテーション済み cookie を含む 204、Bearer モードでは {accessToken, refreshToken} を含む 200 を返します。

失敗: 401 は無効または再利用済みトークン、422 はトークン不足、400 は失敗した取り消し、500 は永続化の失敗です。

完全なソースを表示
export const refreshTokenRoute = withRoute({
handler: whenCookieAuth(
compose(
withLogging(refreshToken),
flatMapAsync(withLogging(setResponseCookie)),
flatMapAsync(withLogging(applyPayloadArgs(normalizeEmptyBody, [], 'refreshResponse'))),
lift(withLogging(orThrow([], [['context', 'data', 'refreshResponse'], 204]))),
),
compose(
withLogging(refreshToken),
flatMapAsync(
withLogging(applyPayloadArgs(normalizeBearerRefreshResponse, [['context', 'data']], 'refreshResponse')),
),
lift(withLogging(orThrow([], [['context', 'data', 'refreshResponse']]))),
),
),
method: 'POST',
path: '/auth/token/refresh',
validators: [],
});

checkTokenRoute

実装

エンドポイント: POST /auth/token/check

アクセスまたはワンタイムトークンを検証します。

アクセス: パブリック。本文のトークンから有効性を確立します。

リクエスト: checkTokenSchema が本文トークンを検証。

パイプライン: ハンドラーではなくブロックの checkToken を実行し、続けて orThrow を実行します。

成功: 検証済みの tokenInfo を含む 200 を返します。有効なワンタイムトークンは消費されます。

失敗: 400 は無効なトークンまたはセキュリティチェック失敗、401 は検証失敗、500 はワンタイムトークンのデータベース失敗です。

完全なソースを表示
export const checkTokenRoute = withRoute({
handler: compose(
withLogging(
applyPayloadArgs(checkTokenBlock, [
['context', 'db', 'onetimetokens'],
['context', 'configuration', 'authSecrets'],
['context', 'request'],
['params', 'requestBody', 'token'],
['params', 'requestBody', 'target'],
]),
),
lift(
withLogging(
orThrow(
[
[AuthenticationInvalidTokenError, 400],
[AuthenticationUnauthorizedError, 401],
[AuthenticationUnexpectedDBError, 500],
],
[['context', 'data', 'tokenInfo']],
),
),
),
),
method: 'POST',
path: '/auth/token/check',
validators: [],
});

deleteRefreshTokensRoute

実装

エンドポイント: DELETE /auth/:identityId/refresh-tokens

アイデンティティのリフレッシュトークンレコードを取り消します。

アクセス: 認証済みの管理者または一致するアイデンティティです。

リクエスト: deleteRefreshTokensSchema がパス ID を検証。

パイプライン: softDeleteRefreshTokens、空本文の正規化、orThrow を順に実行します。

成功: アクティブなリフレッシュレコードが一致しない場合も含めて 204 を返します。

失敗: 認証または認可の失敗、あるいは 500 のデータベース失敗です。

完全なソースを表示
export const deleteRefreshTokensRoute = withRoute({
handler: compose(
withLogging(
applyPayloadArgs(
softDeleteRefreshTokens,
[
// TODO: once refresh tokens have been moved to their own collection, pass in that collection instead of identities:
['context', 'db', 'identities'],
['params', 'requestParams', 'identityId'],
],
'hasSoftDeletedRefreshTokens',
),
),
lift(orThrow([[AuthenticationUnexpectedDbError, 500]], [['context', 'data', 'hasSoftDeletedRefreshTokens'], 204])),
),
method: 'DELETE',
path: '/auth/:identityId/refresh-tokens',
validators: [
isAuthenticated(),
some(checkIdentityType(['admin']), isSelf(['params', 'requestParams', 'identityId'])),
],
});

loginWithOnetimeTokenRoute

実装

エンドポイント: POST /auth/ott/login

パスワードレスログインを完了します。

アクセス: アクティブでログイン用途のワンタイムトークンを持つ呼び出し元にはパブリックです。

リクエスト: loginWithOnetimeTokenSchema が本文のトークンとフィンガープリントを検証します。

パイプライン: loginWithOnetimeToken、トークン無効化、createAccessTokencreateRefreshToken、任意の setResponseCookie、モード固有の正規化を順に実行します。

成功: cookie モードでは cookie 付き {id}、Bearer モードではセッション本文を含む 200 を返します。

失敗: 401 は検証失敗、403 は不正または無効なトークン、404 は存在しないアイデンティティ、またはセッション生成の失敗です。

完全なソースを表示
export const loginWithOnetimeTokenRoute = withRoute({
handler: compose(
withLogging(loginWithOnetimeToken),
flatMapAsync(
withLogging(
applyPayloadArgs(
invalidateOneTimeToken,
[
['context', 'db', 'onetimetokens'],
['params', 'requestBody', 'token'],
],
'isInvalidatedSuccessfully',
),
),
),
flatMapAsync(withLogging(createAccessToken)),
flatMapAsync(withLogging(createRefreshToken)),
flatMapAsync(
withLogging(
whenCookieAuth(
compose(
withLogging(setResponseCookie),
flatMapAsync(
withLogging(applyPayloadArgs(normalizeCookieLoginResponse, [['context', 'data']], 'loginResponse')),
),
),
withLogging(applyPayloadArgs(normalizeBearerLoginResponse, [['context', 'data']], 'loginResponse')),
),
),
),
lift(withLogging(orThrow([], [['context', 'data', 'loginResponse']]))),
),
method: 'POST',
path: '/auth/ott/login',
validators: [],
});

generateOnetimeTokenRoute

実装

エンドポイント: POST /auth/ott/generate

カスタムフロー用のワンタイムトークンを生成します。

アクセス: 認証済み管理者です。

リクエスト: スキーマは合成されません。ランタイムはオブジェクトの JSON tokenData を要求し、targetfingerprint を受け入れます。

パイプライン: generateOnetimeToken を実行します。

成功: ハンドラーが成功した Result<RouteHandlerPayload> を含むデフォルトの 200 を返します。このレガシールートにはターミネーターがなく、authService によってマウントされません。

失敗: 400 は無効なデータまたは挿入結果、500 は生成またはデータベースの失敗です。

完全なソースを表示
export const generateOnetimeTokenRoute = withRoute({
handler: compose(withLogging(generateOnetimeToken)),
method: 'POST',
path: '/auth/ott/generate',
validators: [isAuthenticated(), checkIdentityType(['admin'])],
});

restoreOnetimeTokenRoute

実装

エンドポイント: POST /auth/ott/restore

無効化されたワンタイムトークンを復元します。

アクセス: 認証済み管理者です。

リクエスト: スキーマは合成されません。ランタイムは本文の token を読み取ります。

パイプライン: restoreOnetimeToken を実行します。

成功: ハンドラーが成功した Result<RouteHandlerPayload> を含むデフォルトの 200 を返します。一致するレコードが 0 件でも成功します。

失敗: 401 はデコード不能なトークン、422 はステートレスでないトークン、500 は更新の失敗です。

完全なソースを表示
export const restoreOnetimeTokenRoute = withRoute({
handler: compose(withLogging(restoreOnetimeToken)),
method: 'POST',
path: '/auth/ott/restore',
validators: [isAuthenticated(), checkIdentityType(['admin'])],
});

invalidateOnetimeTokenRoute

実装

エンドポイント: POST /auth/ott/invalidate

ワンタイムトークンを無効化します。

アクセス: 認証済み管理者です。

リクエスト: スキーマは合成されません。ランタイムはパイプラインコンテキストまたは本文から tokenfingerprint を読み取ります。

パイプライン: invalidateOnetimeToken を実行します。

成功: ハンドラーが成功した Result<RouteHandlerPayload> を含むデフォルトの 200 を返します。一致するレコードが 0 件でも成功します。

失敗: 422 は不足、不正、またはステートレスでない入力、500 は更新の失敗です。

完全なソースを表示
export const invalidateOnetimeTokenRoute = withRoute({
handler: compose(withLogging(invalidateOnetimeToken)),
method: 'POST',
path: '/auth/ott/invalidate',
validators: [isAuthenticated(), checkIdentityType(['admin'])],
});

sendVerificationEmailRoute

実装

エンドポイント: POST /auth/:identityId/send-verification-email

確認メールを送信します。

アクセス: 認証済みの管理者または一致するアイデンティティです。

リクエスト: sendVerificationEmailSchema がパスIDと本文フィンガープリントを検証。

パイプライン: sendVerificationEmail、続けて sendVerificationEmailTerminator を実行します。

成功: トークンの保存とメール配信が成功すると 204 を返します。

失敗: 400 は無効または不足している構成もしくはメール、404 は存在しないアイデンティティ、501 はトークン生成失敗、500 は保存またはメールの失敗です。

完全なソースを表示
export const sendVerificationEmailRoute = withRoute({
handler: compose(withLogging(sendVerificationEmail), lift(withLogging(sendVerificationEmailTerminator))),
method: 'POST',
path: '/auth/:identityId/send-verification-email',
validators: [
isAuthenticated(),
some(checkIdentityType(['admin']), isSelf(['params', 'requestParams', 'identityId'])),
],
});

confirmEmailRoute

実装

エンドポイント: POST /auth/confirm-email

メール確認トークンを確認します。

アクセス: 確認トークンを持つ呼び出し元にはパブリックです。

リクエスト: confirmEmailSchema が本文トークンを検証。

パイプライン: buildCheckConfirmEmailTokenPayload、ハンドラーの checkTokenconfirmEmailconfirmEmailTerminator を順に実行します。

成功: 保存済みトークンを消費してアイデンティティを確認済みにし、204 を返します。

失敗: 400 は無効なトークン、401 は検証失敗、403 は不正なトークンデータ、404 は存在しないアイデンティティ、409 は既に確認済み、500 は永続化の失敗です。

完全なソースを表示
export const confirmEmailRoute = withRoute({
handler: compose(
withLogging(buildCheckConfirmEmailTokenPayload),
flatMapAsync(withLogging(checkToken)),
flatMapAsync(withLogging(confirmEmail)),
lift(withLogging(confirmEmailTerminator)),
),
method: 'POST',
path: '/auth/confirm-email',
validators: [],
});

changeEmailRoute

実装

エンドポイント: PATCH /auth/:identityId/change-email

メールアドレス変更フローを開始します。

アクセス: 認証済みの管理者または一致するアイデンティティです。

リクエスト: changeEmailSchema がパスのアイデンティティと新しいメールアドレスを検証。

パイプライン: アイデンティティと一意性の確認、ターゲットとリクエストセキュリティの構築、トークン生成と保存、sendEmail、空本文の正規化、orThrow を順に実行します。

成功: 確認トークンをメール送信した後に 204 を返します。

失敗: 400 から 500 までにマッピングされる Authentication エラーです。404 のアイデンティティ、409 のメール競合、422 のフィンガープリント形式を含みます。

完全なソースを表示
export const changeEmailRoute = withRoute({
handler: compose(
withLogging(
applyPayloadArgs(
assertIdentityExists,
[
['context', 'db', 'identities'],
['params', 'requestParams', 'identityId'],
],
'doesIdentityExist',
),
),
flatMapAsync(
withLogging(
applyPayloadArgs(
mapMatchingErrorToFalse(checkEmailIsUniqueInIdentities, [AuthenticationConflictError]),
[
['context', 'db', 'identities'],
['params', 'requestBody', 'email'],
],
'isEmailUnique',
),
),
),
flatMapAsync(
ifElse(
match(Boolean, ['context', 'data', 'isEmailUnique']),
compose(
applyPayloadArgs(getChangeEmailTokenTarget, [], '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'],
['params', 'requestParams', 'identityId'],
['params', 'requestBody', 'email'],
],
'token',
),
),
),
flatMapAsync(
withLogging(
applyPayloadArgs(
storeOneTimeToken,
[
['context', 'db', 'onetimetokens'],
['context', 'data', 'token'],
],
'isStoredOneTimeToken',
),
),
),
flatMapAsync(
withLogging(
applyPayloadArgs(
sendEmail,
[
['context', 'mailService'],
['context', 'configuration', 'verifyEmailConfig', 'sender'],
['context', 'configuration', 'verifyEmailConfig', 'emailConfig'],
['params', 'requestBody', 'email'],
['context', 'data', 'token'],
],
'hasSentEmail',
),
),
),
flatMapAsync(withLogging(applyPayloadArgs(normalizeEmptyBody, [], 'normalizedBody'))),
),
applyPayloadArgs(noop, [[]]),
),
),
lift(
withLogging(
orThrow(
[
[AuthenticationInvalidInputError, 400],
[AuthenticationBadRequestError, 400],
[AuthenticationUnauthorizedError, 401],
[AuthenticationForbiddenError, 403],
[AuthenticationNotFoundError, 404],
[AuthenticationConflictError, 409],
[AuthenticationUnprocessableEntityError, 422],
[AuthenticationUnexpectedDbError, 500],
[AuthenticationUnexpectedError, 500],
],
[['context', 'data', 'normalizedBody'], 204],
),
),
),
),
method: 'PATCH',
path: '/auth/:identityId/change-email',
validators: [
isAuthenticated(),
some(checkIdentityType(['admin']), isSelf(['params', 'requestParams', 'identityId'])),
],
});

confirmNewEmailRoute

実装

エンドポイント: POST /auth/confirm-new-email

新しいメールアドレスを確認します。

アクセス: メールアドレス変更トークンを持つ呼び出し元にはパブリックです。

リクエスト: confirmNewEmailSchema がトークンとフィンガープリントを検証。

パイプライン: トークンのターゲットとセキュリティ確認、ワンタイムトークンの検証と無効化、メールアドレスの一意性と形式確認、アイデンティティ更新、空本文の正規化、orThrow を順に実行します。

成功: 204 を返します。

失敗: 400 から 500 までにマッピングされる Authentication エラーです。特に 401 の検証、403 の無効なトークン、409 のメール競合、404 のアイデンティティを含みます。

完全なソースを表示
export const confirmNewEmailRoute = withRoute({
handler: compose(
applyPayloadArgs(getChangeEmailTokenTarget, [], '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(
checkOneTimeToken,
[
['context', 'configuration', 'authSecrets'],
['context', 'data', 'target'],
['params', 'requestBody', 'token'],
],
'tokenInfo',
),
),
),
flatMapAsync(
withLogging(
applyPayloadArgs(
assertValidOneTimeTokenExists,
[
['context', 'db', 'onetimetokens'],
['params', 'requestBody', 'token'],
],
'doesOneTimeTokenExist',
),
),
),
flatMapAsync(
withLogging(
applyPayloadArgs(
invalidateOneTimeToken,
[
['context', 'db', 'onetimetokens'],
['params', 'requestBody', 'token'],
],
'isInvalidatedSuccessfully',
),
),
),
flatMapAsync(
withLogging(
applyPayloadArgs(
checkEmailIsUniqueInIdentities,
[
['context', 'db', 'identities'],
['context', 'data', 'tokenInfo', 'data', 'email'],
],
'isEmailUnique',
),
),
),
flatMapAsync(
withLogging(applyPayloadArgs(isEmail, [['context', 'data', 'tokenInfo', 'data', 'email']], 'isEmail')),
),
flatMapAsync(
withLogging(
applyPayloadArgs(
buildUpdateIdentityEmailAndEmailVerifiedPayload,
[['context', 'data', 'tokenInfo', 'data', 'email']],
'identityFieldsToUpdate',
),
),
),
flatMapAsync(
withLogging(
applyPayloadArgs(
updateIdentity,
[
['context', 'db', 'identities'],
['context', 'data', 'tokenInfo', 'data', 'identityId'],
['context', 'data', 'identityFieldsToUpdate'],
],
'hasUpdatedIdentity',
),
),
),
flatMapAsync(withLogging(applyPayloadArgs(normalizeEmptyBody, [], 'normalizedBody'))),
lift(
withLogging(
orThrow(
[
[AuthenticationInvalidInputError, 400],
[AuthenticationBadRequestError, 400],
[AuthenticationUnauthorizedError, 401],
[AuthenticationForbiddenError, 403],
[AuthenticationNotFoundError, 404],
[AuthenticationConflictError, 409],
[AuthenticationUnprocessableEntityError, 422],
[AuthenticationUnexpectedDbError, 500],
[AuthenticationUnexpectedError, 500],
],
[['context', 'data', 'normalizedBody'], 204],
),
),
),
),
method: 'POST',
path: '/auth/confirm-new-email',
validators: [],
});

sendResetPasswordLinkEmailRoute

実装

エンドポイント: POST /auth/send-reset-password-link-email

パスワードリセットリンクを送信します。

アクセス: パブリックです。

リクエスト: sendResetPasswordLinkEmailSchema が本文メールアドレスを検証。

パイプライン: アイデンティティ検索、リセット用ターゲット、フィンガープリント、セキュリティの構築、トークン生成と保存、sendEmail、空本文の正規化、orThrow を順に実行します。

成功: 204 を返します。

失敗: 404 の不明なメールアドレス、422 の不正なフィンガープリント、500 の生成、保存、メール失敗を含む Authentication エラーです。

完全なソースを表示
export const sendResetPasswordLinkEmailRoute = withRoute({
handler: compose(
withLogging(
applyPayloadArgs(
getIdentityIdByEmail,
[
['context', 'db', 'identities'],
['params', 'requestBody', 'email'],
],
'identityId',
),
),
flatMapAsync(applyPayloadArgs(getResetPasswordTokenTarget, [], '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(
sendEmail,
[
['context', 'mailService'],
['context', 'configuration', 'sendResetPasswordEmailConfig', 'sender'],
['context', 'configuration', 'sendResetPasswordEmailConfig', 'emailConfig'],
['params', 'requestBody', 'email'],
['context', 'data', 'token'],
],
'hasSentEmail',
),
),
),
flatMapAsync(withLogging(applyPayloadArgs(normalizeEmptyBody, [], 'normalizedBody'))),
lift(
withLogging(
orThrow(
[
[AuthenticationInvalidInputError, 400],
[AuthenticationBadRequestError, 400],
[AuthenticationUnauthorizedError, 401],
[AuthenticationForbiddenError, 403],
[AuthenticationNotFoundError, 404],
[AuthenticationConflictError, 409],
[AuthenticationUnprocessableEntityError, 422],
[AuthenticationUnexpectedDbError, 500],
[AuthenticationUnexpectedError, 500],
],
[['context', 'data', 'normalizedBody'], 204],
),
),
),
),
method: 'POST',
path: '/auth/send-reset-password-link-email',
validators: [],
});

completePasswordResetRoute

実装

エンドポイント: POST /auth/reset-password

トークンを使用してパスワードリセットを完了します。

アクセス: 有効なリセットトークンを持つ呼び出し元にはパブリックです。

リクエスト: completePasswordResetSchema が置換後のパスワードを検証します。ランタイムは Authorization ヘッダーからもリセットトークンを読み取りますが、そのソースパラメーターは現在スキーマでコメントアウトされています。

パイプライン: トークンの抽出と確認、アイデンティティとパスワードの比較、トークン無効化、パスワードのハッシュ化と更新、通知メール、空本文の正規化、orThrow を順に実行します。

成功: 204 を返します。

失敗: 400 から 500 までにマッピングされる Authentication エラーです。401 のトークン失敗、403 の無効なトークン、404 のアイデンティティを含みます。

完全なソースを表示
export const completePasswordResetRoute = withRoute({
handler: compose(
applyPayloadArgs(getResetPasswordTokenTarget, [], '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(
extractTokenFromAuthorizationHeader,
[['context', 'request', 'headers', 'authorization']],
'oneTimeToken',
),
),
),
flatMapAsync(
withLogging(
applyPayloadArgs(
checkOneTimeToken,
[
['context', 'configuration', 'authSecrets'],
['context', 'data', 'target'],
['context', 'data', 'oneTimeToken'],
],
'tokenInfo',
),
),
),
flatMapAsync(
withLogging(
applyPayloadArgs(
assertValidOneTimeTokenExists,
[
['context', 'db', 'onetimetokens'],
['context', 'data', 'oneTimeToken'],
],
'doesOneTimeTokenExist',
),
),
),
flatMapAsync(
withLogging(
applyPayloadArgs(
getIdentityById,
[
['context', 'db', 'identities'],
['context', 'data', 'tokenInfo', 'data', 'identityId'],
],
'identity',
),
),
),
flatMapAsync(
withLogging(
applyPayloadArgs(
compareStringAgainstHash,
[
['context', 'data', 'identity', 'password'],
['params', 'requestBody', 'password'],
],
'newValueMatches',
),
),
),
flatMapAsync(
withLogging(
applyPayloadArgs(assertDoesNotMatch, [['context', 'data', 'newValueMatches']], 'newValueDoesNotMatch'),
),
),
flatMapAsync(
withLogging(
applyPayloadArgs(
invalidateOneTimeToken,
[
['context', 'db', 'onetimetokens'],
['context', 'data', 'oneTimeToken'],
],
'isInvalidatedSuccessfully',
),
),
),
flatMapAsync(withLogging(applyPayloadArgs(hash, [['params', 'requestBody', 'password']], 'hashedPassword'))),
flatMapAsync(
withLogging(
applyPayloadArgs(
buildUpdateIdentityPasswordPayload,
[['context', 'data', 'hashedPassword']],
'identityFieldsToUpdate',
),
),
),
flatMapAsync(
withLogging(
applyPayloadArgs(
updateIdentity,
[
['context', 'db', 'identities'],
['context', 'data', 'tokenInfo', 'data', 'identityId'],
['context', 'data', 'identityFieldsToUpdate'],
],
'identityId',
),
),
),
flatMapAsync(
withLogging(
applyPayloadArgs(
softDeleteRefreshTokens,
[
['context', 'db', 'identities'],
['context', 'data', 'tokenInfo', 'data', 'identityId'],
],
'hasSoftDeletedRefreshTokens',
),
),
),
flatMapAsync(
withLogging(
applyPayloadArgs(
sendEmail,
[
['context', 'mailService'],
['context', 'configuration', 'resetPasswordSuccessConfig', 'sender'],
['context', 'configuration', 'resetPasswordSuccessConfig', 'emailConfig'],
['context', 'data', 'identity', 'email'],
],
'hasSentEmail',
),
),
),
flatMapAsync(withLogging(applyPayloadArgs(normalizeEmptyBody, [], 'normalizedBody'))),
lift(
withLogging(
orThrow(
[
[AuthenticationInvalidInputError, 400],
[AuthenticationBadRequestError, 400],
[AuthenticationUnauthorizedError, 401],
[AuthenticationForbiddenError, 403],
[AuthenticationNotFoundError, 404],
[AuthenticationConflictError, 409],
[AuthenticationUnprocessableEntityError, 422],
[AuthenticationUnexpectedDbError, 500],
[AuthenticationUnexpectedError, 500],
],
[['context', 'data', 'normalizedBody'], 204],
),
),
),
),
method: 'POST',
path: '/auth/reset-password',
validators: [],
});

changePasswordRoute

実装

エンドポイント: PATCH /auth/:identityId/change-password

認証済みアイデンティティのパスワードを変更します。

アクセス: 認証済みの管理者または一致するアイデンティティです。

リクエスト: changePasswordSchema がパスのアイデンティティ、現在のパスワード、新しいパスワードを検証。

パイプライン: アイデンティティ検索、現在と新しいパスワードの比較、ハッシュ化と更新、リフレッシュトークンの取り消し、sendEmail、空本文の正規化、orThrow を順に実行します。

成功: 204 を返します。

失敗: 現在のパスワードが無効または再利用済みである場合と、アイデンティティが存在しない場合を含む、400 から 500 までにマッピングされる Authentication エラーです。

完全なソースを表示
export const changePasswordRoute = withRoute({
handler: compose(
withLogging(
applyPayloadArgs(
getIdentityById,
[
['context', 'db', 'identities'],
['params', 'requestParams', 'identityId'],
],
'identity',
),
),
flatMapAsync(
withLogging(
applyPayloadArgs(
compareStringAgainstHash,
[
['context', 'data', 'identity', 'password'],
['params', 'requestBody', 'password'],
],
'oldValueMatches',
),
),
),
flatMapAsync(
withLogging(applyPayloadArgs(assertMatches, [['context', 'data', 'oldValueMatches']], 'assertOldValueMatches')),
),
flatMapAsync(
withLogging(
applyPayloadArgs(
compareStringAgainstHash,
[
['context', 'data', 'identity', 'password'],
['params', 'requestBody', 'newPassword'],
],
'newValueMatches',
),
),
),
flatMapAsync(
withLogging(
applyPayloadArgs(assertDoesNotMatch, [['context', 'data', 'newValueMatches']], 'newValueDoesNotMatch'),
),
),
flatMapAsync(withLogging(applyPayloadArgs(hash, [['params', 'requestBody', 'newPassword']], 'hashedPassword'))),
flatMapAsync(
withLogging(
applyPayloadArgs(
buildUpdateIdentityPasswordPayload,
[['context', 'data', 'hashedPassword']],
'identityFieldsToUpdate',
),
),
),
flatMapAsync(
withLogging(
applyPayloadArgs(
updateIdentity,
[
['context', 'db', 'identities'],
['params', 'requestParams', 'identityId'],
['context', 'data', 'identityFieldsToUpdate'],
],
'identityId',
),
),
),
flatMapAsync(
withLogging(
applyPayloadArgs(
softDeleteRefreshTokens,
[
['context', 'db', 'identities'],
['params', 'requestParams', 'identityId'],
],
'hasSoftDeletedRefreshTokens',
),
),
),
flatMapAsync(
withLogging(
applyPayloadArgs(
sendEmail,
[
['context', 'mailService'],
['context', 'configuration', 'changePasswordConfig', 'sender'],
['context', 'configuration', 'changePasswordConfig', 'emailConfig'],
['context', 'data', 'identity', 'email'],
],
'hasSentEmail',
),
),
),
flatMapAsync(withLogging(applyPayloadArgs(normalizeEmptyBody, [], 'normalizedBody'))),
lift(
withLogging(
orThrow(
[
[AuthenticationInvalidInputError, 400],
[AuthenticationBadRequestError, 400],
[AuthenticationUnauthorizedError, 401],
[AuthenticationForbiddenError, 403],
[AuthenticationNotFoundError, 404],
[AuthenticationConflictError, 409],
[AuthenticationUnprocessableEntityError, 422],
[AuthenticationUnexpectedDbError, 500],
[AuthenticationUnexpectedError, 500],
],
[['context', 'data', 'normalizedBody'], 204],
),
),
),
),
method: 'PATCH',
path: '/auth/:identityId/change-password',
validators: [
isAuthenticated(),
some(checkIdentityType(['admin']), isSelf(['params', 'requestParams', 'identityId'])),
],
});

deactivateRoute

実装

エンドポイント: POST /auth/deactivate

アイデンティティを非アクティブ化します。

アクセス: 認証済みの管理者、または本文の identityId です。

リクエスト: deactivateSchema が本文のアイデンティティ ID を検証。

パイプライン: アイデンティティ検索と確認済みメールのガード、非アクティブ化の更新、リフレッシュトークンの取り消し、モードに応じたアクセストークン検証、条件付きメール、空本文の正規化、orThrow を順に実行します。

成功: 204 を返します。

失敗: 認証または認可の失敗、あるいは 400 から 500 までにマッピングされる Authentication エラーです。

完全なソースを表示
export const deactivateRoute = withRoute({
handler: compose(
withLogging(
applyPayloadArgs(
getIdentityById,
[
['context', 'db', 'identities'],
['params', 'requestBody', 'identityId'],
],
'identity',
),
),
flatMapAsync(
withLogging(
applyPayloadArgs(isEmailVerified, [['context', 'data', 'identity', 'emailVerified']], 'isEmailVerified'),
),
),
flatMapAsync(withLogging(applyPayloadArgs(buildUpdateIdentityDeactivatedPayload, [], 'identityFieldsToUpdate'))),
flatMapAsync(
withLogging(
applyPayloadArgs(
updateIdentity,
[
['context', 'db', 'identities'],
['params', 'requestBody', 'identityId'],
['context', 'data', 'identityFieldsToUpdate'],
],
'identityId',
),
),
),
flatMapAsync(
withLogging(
applyPayloadArgs(
softDeleteRefreshTokens,
[
// TODO: once refresh tokens have been moved to their own collection, pass in that collection instead of identities:
['context', 'db', 'identities'],
['params', 'requestBody', 'identityId'],
],
'hasSoftDeletedRefreshTokens',
),
),
),
flatMapAsync(
withLogging(
whenCookieAuth(
withLogging(applyPayloadArgs(noop, [['context', 'request', 'cookies', 'accessToken']], 'accessToken')),
withLogging(
applyPayloadArgs(
extractTokenFromAuthorizationHeader,
[['context', 'request', 'headers', 'authorization']],
'accessToken',
),
),
),
),
),
flatMapAsync(
withLogging(
applyPayloadArgs(
checkTokenBlock,
[
['context', 'db', 'onetimetokens'],
['context', 'configuration', 'authSecrets'],
['context', 'request'],
['context', 'data', 'accessToken'],
],
'checkTokenResult',
),
),
),
flatMapAsync(
ifElse(
(input: RouteHandlerPayload) =>
input?.params?.requestBody?.identityId === input?.context?.data?.checkTokenResult?.tokenInfo?.identityId,
compose(
withLogging(
applyPayloadArgs(
sendEmail,
[
['context', 'mailService'],
['context', 'configuration', 'deactivateIdentityEmailConfig', 'sender'],
['context', 'configuration', 'deactivateIdentityEmailConfig', 'emailConfig'],
['context', 'data', 'identity', 'email'],
],
'hasSentEmail',
),
),
),
async (input) => ok(input),
),
),
flatMapAsync(withLogging(applyPayloadArgs(normalizeEmptyBody, [], 'normalizedBody'))),
lift(
withLogging(
orThrow(
[
[AuthenticationInvalidInputError, 400],
[AuthenticationBadRequestError, 400],
[AuthenticationUnauthorizedError, 401],
[AuthenticationForbiddenError, 403],
[AuthenticationNotFoundError, 404],
[AuthenticationConflictError, 409],
[AuthenticationUnprocessableEntityError, 422],
[AuthenticationUnexpectedDbError, 500],
[AuthenticationUnexpectedError, 500],
],
[['context', 'data', 'normalizedBody'], 204],
),
),
),
),
method: 'POST',
path: '/auth/deactivate',
validators: [isAuthenticated(), some(checkIdentityType(['admin']), isSelf(['params', 'requestBody', 'identityId']))],
});

activateRoute

実装

エンドポイント: POST /auth/activate

アイデンティティを再アクティブ化します。

アクセス: 認証済み管理者です。

リクエスト: activateSchema が JSON アイデンティティ ID を検証。

パイプライン: アイデンティティ検索、確認済みメールのガード、アクティブ化の更新、空本文の正規化、orThrow を順に実行します。

成功: 204 を返します。

失敗: 認証または管理者の失敗、あるいは 400 から 500 までにマッピングされる Authentication エラーです。

完全なソースを表示
export const activateRoute = withRoute({
handler: compose(
withLogging(
applyPayloadArgs(
getIdentityById,
[
['context', 'db', 'identities'],
['params', 'requestBody', 'identityId'],
],
'identity',
),
),
flatMapAsync(
withLogging(
applyPayloadArgs(isEmailVerified, [['context', 'data', 'identity', 'emailVerified']], 'isEmailVerified'),
),
),
flatMapAsync(withLogging(applyPayloadArgs(buildUpdateIdentityActivatedPayload, [], 'identityFieldsToUpdate'))),
flatMapAsync(
withLogging(
applyPayloadArgs(
updateIdentity,
[
['context', 'db', 'identities'],
['params', 'requestBody', 'identityId'],
['context', 'data', 'identityFieldsToUpdate'],
],
'identityId',
),
),
),
flatMapAsync(withLogging(applyPayloadArgs(normalizeEmptyBody, [], 'normalizedBody'))),
lift(
withLogging(
orThrow(
[
[AuthenticationInvalidInputError, 400],
[AuthenticationBadRequestError, 400],
[AuthenticationUnauthorizedError, 401],
[AuthenticationForbiddenError, 403],
[AuthenticationNotFoundError, 404],
[AuthenticationConflictError, 409],
[AuthenticationUnprocessableEntityError, 422],
[AuthenticationUnexpectedDbError, 500],
[AuthenticationUnexpectedError, 500],
],
[['context', 'data', 'normalizedBody'], 204],
),
),
),
),
method: 'POST',
path: '/auth/activate',
validators: [isAuthenticated(), checkIdentityType(['admin'])],
});