UNPKG

oidc-provider

Version:

OAuth 2.0 Authorization Server implementation for Node.js with OpenID Connect

128 lines (99 loc) 3.62 kB
import { AuthorizationPending, ExpiredToken, InvalidGrant } from '../../helpers/errors.js'; import presence from '../../helpers/validate_presence.js'; import instance from '../../helpers/weak_cache.js'; import revoke from '../../helpers/revoke.js'; import dpopValidate from '../../helpers/validate_dpop.js'; import { checkAttestBinding } from '../../helpers/check_attest_binding.js'; import checkRar from '../../shared/check_rar.js'; import { throwIfAsyncGrantError, checkMtlsCert, checkDpopRequired, validateGrant, validateAccount, checkAccountMismatch, createAccessToken, applyMtlsBinding, applyDpopBinding, resolveAndApplyResource, createRefreshToken, issueIdToken, buildTokenResponse, } from '../../helpers/grant_common.js'; export const gty = 'ciba'; export const handler = async function cibaHandler(ctx) { presence(ctx, 'auth_req_id'); const { findAccount, issueRefreshToken, conformIdTokenClaims, features: { userinfo, mTLS: { getCertificate }, dPoP: { allowReplay }, resourceIndicators, richAuthorizationRequests, }, } = instance(ctx.oidc.provider).configuration; const dPoP = await dpopValidate(ctx); const request = await ctx.oidc.provider.BackchannelAuthenticationRequest.find( ctx.oidc.params.auth_req_id, { ignoreExpiration: true }, ); if (!request) { throw new InvalidGrant('backchannel authentication request not found'); } if (request.clientId !== ctx.oidc.client.clientId) { throw new InvalidGrant('client mismatch'); } const cert = checkMtlsCert(ctx, getCertificate); checkDpopRequired(ctx, dPoP); if (request.isExpired) { throw new ExpiredToken('backchannel authentication request is expired'); } if (!request.grantId && !request.error) { throw new AuthorizationPending(); } if (ctx.oidc.client.clientAuthMethod === 'attest_jwt_client_auth') { await checkAttestBinding(ctx, request); } if (request.consumed) { await revoke(ctx, request.grantId); throw new InvalidGrant('backchannel authentication request already consumed'); } await request.consume(); throwIfAsyncGrantError(request); const grant = await validateGrant(ctx, request.grantId); ctx.oidc.entity('BackchannelAuthenticationRequest', request); ctx.oidc.entity('Grant', grant); const account = await validateAccount(ctx, findAccount, request, 'backchannel authentication request'); checkAccountMismatch(request, grant); ctx.oidc.entity('Account', account); const { RefreshToken } = ctx.oidc.provider; const at = createAccessToken( ctx, ctx.oidc.provider.AccessToken, { ...request, accountId: account.accountId }, gty, ); applyMtlsBinding(at, cert); await applyDpopBinding(ctx, dPoP, at, allowReplay); await checkRar(ctx, () => {}); await resolveAndApplyResource(ctx, request, at, grant, { userinfo, resourceIndicators }); if (richAuthorizationRequests.enabled && at.resourceServer) { at.rar = await richAuthorizationRequests.rarForBackchannelResponse(ctx, at.resourceServer); } ctx.oidc.entity('AccessToken', at); const accessToken = await at.save(); const refreshToken = await createRefreshToken(ctx, request, at, gty, { issueRefreshToken, RefreshToken, }); const idToken = await issueIdToken(ctx, request, at, grant, { conformIdTokenClaims, userinfo, }); ctx.body = buildTokenResponse(at, accessToken, { idToken, refreshToken, source: request, rar: at.rar, }); }; export const parameters = new Set(['auth_req_id']); export const grantType = 'urn:openid:params:grant-type:ciba';