UNPKG

jwt-bearer-client-auth

Version:

Create and verify JWT bearer client assertions from the OAuth-JWT-bearer RFC

90 lines (80 loc) 2.33 kB
/** * @license * Copyright 2015-2022 Open Ag Data Alliance * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { type RSA_JWK, jwk2pem } from 'pem-jwk'; import { verify as jwtVerify } from 'jsonwebtoken'; import { jwksUtils as jwks } from '@oada/certs'; export interface VerifyOptions { token: string; hint: string | false | jwks.JWKs | jwks.JWK; issuer: string; clientId: string; tokenEndpoint: string; payload?: Record<string, unknown>; } export async function verify({ token, hint, issuer, clientId, tokenEndpoint, payload, }: VerifyOptions) { const jwk = await jwks.jwkForSignature(token, hint); const key = jwk.kty === 'PEM' ? jwk.pem : jwk2pem(jwk as RSA_JWK); const jwtPayload = jwtVerify(token, key, { issuer, audience: tokenEndpoint, // HACK: Avoid vulnerability CVE-2022-23540, CVE-2022-23529 algorithms: [ 'HS256', 'HS384', 'HS512', 'RS256', 'RS384', 'RS512', 'ES256', 'ES384', 'ES512', 'PS256', 'PS384', 'PS512', ], }); if (typeof jwtPayload === 'string') { throw new TypeError(`Failed to parse payload: ${jwtPayload}`); } if (!jwtPayload.exp) { throw new Error('exp claim is required'); } // Check required sub key if (jwtPayload.sub !== clientId) { throw new Error('sub claim is inconsistent with clientId'); } // Check for optional not before property if (jwtPayload.nbf && Math.floor(Date.now() / 1000) <= jwtPayload.nbf) { throw new Error('nbf claim violated'); } // Check for any other user required claims if (typeof payload === 'object') { for (const [k, v] of Object.entries(payload)) { if (jwtPayload[k] !== v) { throw new Error(`${k} claim is inconsistent`); } } } return jwtPayload; }