UNPKG

@e280/authlocal

Version:

User-sovereign login system for everybody

57 lines (44 loc) 1.74 kB
import {decodeToken} from "./decode.js" import {tokentime} from "./tokentime.js" import {verify} from "../crypto/crypto.js" import {Token, TokenVerifications, TokenVerifyError} from "./types.js" export async function verifyToken<P extends Token>( id: string, token: string, options: TokenVerifications = {}, ): Promise<P> { const [headerText, payloadText] = token.split(".") const {payload, signature} = decodeToken<P>(token) const signingText = `${headerText}.${payloadText}` const signingBytes = new TextEncoder().encode(signingText) if (!await verify(signingBytes, signature, id)) throw new TokenVerifyError("token signature invalid") if (options.atTime !== null) { const atTime = options.atTime ?? Date.now() if (payload.exp) { const expiresAt = tokentime.toMs(payload.exp) if (atTime > expiresAt) throw new TokenVerifyError("token expired") } if (payload.nbf) { const notBefore = tokentime.toMs(payload.nbf) if (atTime < notBefore) throw new TokenVerifyError("token not ready") } } if (options.allowedIssuers) { if (!payload.iss) throw new TokenVerifyError(`required iss (issuer) is missing`) if (!options.allowedIssuers.includes(payload.iss)) throw new TokenVerifyError(`invalid iss (issuer) "${payload.iss}"`) } if (options.allowedAudiences) { if (!payload.aud) throw new TokenVerifyError(`required aud (audience) is missing`) if (!options.allowedAudiences.includes(payload.aud)) throw new TokenVerifyError(`invalid aud (audience) "${payload.aud}"`) } if (payload.aud && !options.allowedAudiences) throw new TokenVerifyError(`allowedAudiences verification option was not provided, but is required because the token included "aud"`) return payload }