UNPKG

@credo-ts/core

Version:

<p align="center"> <br /> <img alt="Credo Logo" src="https://github.com/openwallet-foundation/credo-ts/blob/c7886cb8377ceb8ee4efe8d264211e561a75072d/images/credo-logo.png" height="250px" /> </p> <h1 align="center"><b>Credo - Core</b></h1

1 lines • 18 kB
{"version":3,"file":"TokenStatusListService.mjs","names":[],"sources":["../../../src/modules/token-status-list/TokenStatusListService.ts"],"sourcesContent":["import { CoseKey, jwkToCoseKey, RegisteredCwtHeaderClaimKey, SignatureAlgorithm } from '@owf/cose'\nimport {\n createHeaderAndPayload,\n fetchStatusList,\n getListFromStatusListJWT,\n StatusList,\n StatusListCwt,\n StatusListCwtPayload,\n} from '@owf/token-status-list'\nimport type { AgentContext } from '../../agent'\nimport { type JwsProtectedHeaderOptions, JwsService, Jwt, JwtPayload } from '../../crypto'\nimport { getMac0Context } from '../../crypto/contexts/mac0Context'\nimport { getSign1Context } from '../../crypto/contexts/sign1Context'\nimport { CredoError } from '../../error'\nimport { injectable } from '../../plugins'\nimport { dateToSeconds } from '../../utils'\nimport { DidsApi, parseDid } from '../dids'\nimport { KeyManagementApi } from '../kms'\nimport type {\n CreateTokenStatusListOptions,\n FetchTokenStatusListOptions,\n StatusListInput,\n TokenStatusListFormat,\n TokenStatusListResult,\n TokenStatusListResultFor,\n UpdateTokenStatusListOptions,\n} from './TokenStatusListOptions'\n\nfunction emptyStatusList(input: Exclude<StatusListInput, StatusList>): StatusList {\n return new StatusList(new Array(input.statusListLength).fill(0), input.bitsPerStatus, input.aggregationUri)\n}\n\n/**\n * @internal\n *\n * @todo with what value should we initialize the array? Valid or Invalid?\n */\n@injectable()\nexport class TokenStatusListService {\n public async createTokenStatusList<Format extends TokenStatusListFormat>(\n agentContext: AgentContext,\n options: CreateTokenStatusListOptions\n ): Promise<Extract<TokenStatusListResult, { format: Format }>> {\n // TODO: jwk could also be supported\n if (options.signer.method !== 'x5c' && options.signer.method !== 'did') {\n throw new Error(\n `signer method '${options.signer.method}' is not supported for creating a token status list. Only x5c and did`\n )\n }\n\n let keyId: string | undefined\n if (options.signer.method === 'x5c') {\n keyId = options.signer.x5c[0].keyId\n } else if (options.signer.method === 'did') {\n const dids = agentContext.dependencyManager.resolve(DidsApi)\n const { publicJwk } = await dids.resolveVerificationMethodFromCreatedDidRecord(options.signer.didUrl)\n keyId = publicJwk.keyId\n }\n\n if (!keyId) {\n throw new CredoError(\n `Unable to establish key id for signer method '${options.signer.method}' when creating a token status list`\n )\n }\n\n const statusList =\n options.statusList instanceof StatusList ? options.statusList : emptyStatusList(options.statusList)\n\n const now = options.now ?? new Date()\n const issuedAt = options.issuedAt ?? now\n\n const kms = agentContext.dependencyManager.resolve(KeyManagementApi)\n const jwk = await kms.getPublicKey({ keyId })\n\n try {\n if (options.format === 'cwt') {\n if (options.signer.method !== 'x5c') {\n throw new CredoError('For a CWT Token Status List, only a signer of type x5c is supported.')\n }\n const cwtPayload = StatusListCwtPayload.create({\n subject: options.statusListUri,\n statusList,\n issuedAt,\n expirationTime: options.expiresAt,\n timeToLive: options.timeToLive,\n additionalClaims: options.additionalPayload,\n })\n const cwtFull = new StatusListCwt({\n payload: cwtPayload,\n protectedHeaders: new Map<number, unknown>([\n [RegisteredCwtHeaderClaimKey.X5Chain, options.signer.x5c.map((cert) => cert.rawCertificate)],\n [RegisteredCwtHeaderClaimKey.Algorithm, jwkToCoseKey.alg(options.alg)],\n ]),\n })\n const shouldAuthenticate = jwk.alg?.toUpperCase().startsWith('HS')\n\n return {\n format: options.format,\n // Mac0 vs Sign1\n statusList: shouldAuthenticate\n ? await cwtFull.authenticateAndEncode({ key: CoseKey.fromJwk(jwk) }, getMac0Context(agentContext))\n : await cwtFull.signAndEncode(\n { signingKey: CoseKey.fromJwk(jwk), algorithm: jwkToCoseKey.alg(options.alg) as SignatureAlgorithm },\n getSign1Context(agentContext)\n ),\n parsed: cwtFull,\n } as Extract<TokenStatusListResult, { format: Format }>\n }\n\n if (options.format === 'jwt') {\n const basePayload: Record<string, unknown> = {\n ...options.additionalPayload,\n sub: options.statusListUri,\n iat: dateToSeconds(issuedAt),\n ...(options.signer.method === 'did' ? { iss: parseDid(options.signer.didUrl).did } : {}),\n }\n if (options.expiresAt !== undefined) {\n basePayload.exp = dateToSeconds(options.expiresAt)\n }\n if (options.timeToLive !== undefined) {\n basePayload.ttl = options.timeToLive\n }\n\n const { header, payload } = createHeaderAndPayload(statusList, basePayload as { sub: string; iat: number }, {\n alg: options.alg,\n typ: 'statuslist+jwt',\n })\n const headerWithKeyReference = {\n ...header,\n ...(options.signer.method === 'x5c'\n ? { x5c: options.signer.x5c.map((cert) => cert.toString('base64')) }\n : {}),\n ...(options.signer.method === 'did' ? { kid: `#${parseDid(options.signer.didUrl).fragment}` } : {}),\n }\n\n const jwsService = agentContext.dependencyManager.resolve(JwsService)\n const jws = await jwsService.createJwsCompact(agentContext, {\n payload: new JwtPayload({ additionalClaims: payload }),\n keyId,\n protectedHeaderOptions: headerWithKeyReference as JwsProtectedHeaderOptions,\n })\n return {\n format: 'jwt',\n statusList: jws,\n parsed: Jwt.fromSerializedJwt(jws),\n } as Extract<TokenStatusListResult, { format: Format }>\n }\n } catch (e) {\n throw new CredoError(`Unable to authenticate or sign the token status list`, { cause: e })\n }\n\n // biome-ignore lint/suspicious/noExplicitAny: `options` is never due to only having two options, but it can ofcourse still happen\n throw new CredoError(`Could not create token status list with format '${(options as any).format}'`)\n }\n\n public async updateTokenStatusList<Format extends TokenStatusListFormat>(\n agentContext: AgentContext,\n options: UpdateTokenStatusListOptions\n ): Promise<Extract<TokenStatusListResult, { format: Format }>> {\n // TODO: jwk could also be supported\n if (options.signer.method !== 'x5c' && options.signer.method !== 'did') {\n throw new Error(\n `signer method '${options.signer.method}' is not supported for updating a token status list. Only x5c and did are`\n )\n }\n\n let keyId: string | undefined\n if (options.signer.method === 'x5c') {\n keyId = options.signer.x5c[0].keyId\n } else if (options.signer.method === 'did') {\n const dids = agentContext.dependencyManager.resolve(DidsApi)\n const { publicJwk } = await dids.resolveVerificationMethodFromCreatedDidRecord(options.signer.didUrl)\n keyId = publicJwk.keyId\n }\n\n if (!keyId) {\n throw new CredoError(\n `Unable to establish key id for signer method '${options.signer.method}' when updating a token status list`\n )\n }\n\n const kms = agentContext.dependencyManager.resolve(KeyManagementApi)\n const jwk = await kms.getPublicKey({ keyId })\n\n const now = options.now ?? new Date()\n const issuedAt = options.issuedAt ?? now\n\n try {\n if (options.token instanceof Uint8Array) {\n if (options.signer.method !== 'x5c') {\n throw new CredoError('For a CWT Token Status List, only a signer of type x5c is supported.')\n }\n const cwt = StatusListCwt.fromToken(options.token)\n if (Array.isArray(options.status)) {\n for (const { index, status } of options.status) {\n cwt.updateStatusList(index, status)\n }\n } else {\n cwt.updateStatusList(options.status.index, options.status.status)\n }\n\n // Apply updated timing claims\n const updatedPayload = StatusListCwtPayload.create({\n subject: cwt.payload.subject,\n statusList: cwt.payload.statusList,\n issuedAt,\n expirationTime: options.expiresAt ?? cwt.payload.expirationTime,\n timeToLive: options.timeToLive ?? cwt.payload.timeToLive,\n })\n const updatedCwt = new StatusListCwt({\n payload: updatedPayload,\n protectedHeaders: new Map<number, unknown>([\n [RegisteredCwtHeaderClaimKey.X5Chain, options.signer.x5c.map((cert) => cert.rawCertificate)],\n [RegisteredCwtHeaderClaimKey.Algorithm, jwkToCoseKey.alg(options.alg)],\n ]),\n })\n\n const shouldAuthenticate = jwk.alg?.toUpperCase().startsWith('HS')\n if (shouldAuthenticate) {\n const mac0Context = getMac0Context(agentContext)\n return {\n format: 'cwt',\n statusList: await updatedCwt.authenticateAndEncode({ key: CoseKey.fromJwk(jwk) }, mac0Context),\n parsed: updatedCwt,\n } as Extract<TokenStatusListResult, { format: Format }>\n } else {\n const sign1Context = getSign1Context(agentContext)\n return {\n format: 'cwt',\n statusList: await updatedCwt.signAndEncode(\n { signingKey: CoseKey.fromJwk(jwk), algorithm: jwkToCoseKey.alg(options.alg) as SignatureAlgorithm },\n sign1Context\n ),\n parsed: updatedCwt,\n } as Extract<TokenStatusListResult, { format: Format }>\n }\n } else if (typeof options.token === 'string') {\n const statusList = getListFromStatusListJWT(options.token)\n const jwt = Jwt.fromSerializedJwt(options.token)\n\n if (Array.isArray(options.status)) {\n for (const { index, status } of options.status) {\n statusList.setStatus(index, status)\n }\n } else {\n statusList.setStatus(options.status.index, options.status.status)\n }\n\n const updatedPayload: Record<string, unknown> = {\n ...jwt.payload.toJson(),\n ...options.additionalPayload,\n iat: dateToSeconds(issuedAt),\n }\n if (options.expiresAt !== undefined) {\n updatedPayload.exp = dateToSeconds(options.expiresAt)\n }\n if (options.timeToLive !== undefined) {\n updatedPayload.ttl = options.timeToLive\n }\n\n // createHeaderAndPayload will overwrite status_list with the updated one\n const { header, payload } = createHeaderAndPayload(\n statusList,\n updatedPayload as { sub: string; iat: number },\n { ...jwt.header, alg: options.alg, typ: 'statuslist+jwt' } as {\n alg: string\n typ: 'statuslist+jwt'\n }\n )\n\n const jwsService = agentContext.dependencyManager.resolve(JwsService)\n const jws = await jwsService.createJwsCompact(agentContext, {\n payload: new JwtPayload({ additionalClaims: payload }),\n keyId,\n protectedHeaderOptions: header as JwsProtectedHeaderOptions,\n })\n return {\n format: 'jwt',\n statusList: jws,\n parsed: Jwt.fromSerializedJwt(jws),\n } as Extract<TokenStatusListResult, { format: Format }>\n }\n } catch (e) {\n throw new CredoError(`Unable to update the token status list`, { cause: e })\n }\n\n throw new CredoError(`Invalid token format for token '${options.token}'. Only string and Uint8Array are supported`)\n }\n\n public async fetchTokenStatusList<AcceptedFormats extends TokenStatusListFormat>(\n agentContext: AgentContext,\n options: FetchTokenStatusListOptions<AcceptedFormats>\n ): Promise<TokenStatusListResultFor<AcceptedFormats>> {\n const token = await fetchStatusList({\n uri: options.uri,\n acceptedFormats: options.acceptedFormats,\n customFetcher: agentContext.config.agentDependencies.fetch,\n })\n\n if (token instanceof Uint8Array) {\n return {\n format: 'cwt',\n statusList: token,\n parsed: StatusListCwt.fromToken(token),\n } as TokenStatusListResultFor<AcceptedFormats>\n }\n\n if (typeof token === 'string') {\n return {\n format: 'jwt',\n statusList: token,\n parsed: Jwt.fromSerializedJwt(token),\n } as TokenStatusListResultFor<AcceptedFormats>\n }\n\n throw new CredoError(`Could not parse token from URL '${options.uri}'`)\n }\n}\n"],"mappings":"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"}