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 • 14.3 kB
{"version":3,"file":"credentialUse.mjs","names":[],"sources":["../../src/utils/credentialUse.ts"],"sourcesContent":["import { AgentContext } from '../agent'\nimport { CredoError } from '../error/CredoError'\nimport { Mdoc } from '../modules/mdoc/Mdoc'\nimport { MdocRecord, type MdocRecordInstances, MdocRepository } from '../modules/mdoc/repository'\nimport { decodeSdJwtVc } from '../modules/sd-jwt-vc/decodeSdJwtVc'\nimport { SdJwtVcRecord, type SdJwtVcRecordInstances, SdJwtVcRepository } from '../modules/sd-jwt-vc/repository'\nimport type { SdJwtVc } from '../modules/sd-jwt-vc/SdJwtVcService'\nimport {\n W3cJsonLdVerifiableCredential,\n W3cJwtVerifiableCredential,\n W3cV2JwtVerifiableCredential,\n W3cV2SdJwtVerifiableCredential,\n type W3cV2VerifiableCredential,\n type W3cVerifiableCredential,\n} from '../modules/vc'\nimport {\n W3cCredentialRecord,\n type W3cCredentialRecordInstances,\n W3cCredentialRepository,\n W3cV2CredentialRecord,\n type W3cV2CredentialRecordInstances,\n W3cV2CredentialRepository,\n} from '../modules/vc/repository'\nimport {\n CredentialMultiInstanceState,\n CredentialMultiInstanceUseMode,\n CredentialMultiInstanceUseUpdateMode,\n} from './credentialUseTypes'\n\nexport { CredentialMultiInstanceState, CredentialMultiInstanceUseMode, CredentialMultiInstanceUseUpdateMode }\n\ntype CredentialRecord = W3cCredentialRecord | SdJwtVcRecord | MdocRecord | W3cV2CredentialRecord\n\ntype CredentialInstanceReturnType<Record extends CredentialRecord> = Record extends W3cCredentialRecord\n ? W3cVerifiableCredential\n : Record extends W3cV2CredentialRecord\n ? W3cV2VerifiableCredential\n : Record extends SdJwtVcRecord\n ? SdJwtVc\n : Record extends MdocRecord\n ? Mdoc\n : CredentialRecord\n\ntype CredentialRepositoryForRecord<Record extends CredentialRecord> = Record extends W3cCredentialRecord\n ? W3cCredentialRepository\n : Record extends W3cV2CredentialRepository\n ? W3cV2VerifiableCredential\n : Record extends SdJwtVcRepository\n ? SdJwtVc\n : Record extends MdocRepository\n ? Mdoc\n : W3cCredentialRepository | W3cV2CredentialRepository | SdJwtVcRepository | MdocRepository\n\n/**\n * Checks whether an instance can be used from the provided record based on\n * the required credential use mode.\n */\nexport function canUseInstanceFromCredentialRecord({\n credentialRecord,\n useMode,\n}: {\n credentialRecord: CredentialRecord\n useMode: CredentialMultiInstanceUseMode\n}) {\n // If we're not required to use a new instance, we can always return the first instance\n if (\n useMode === CredentialMultiInstanceUseMode.First ||\n useMode === CredentialMultiInstanceUseMode.NewOrFirst ||\n (useMode === CredentialMultiInstanceUseMode.NewIfReceivedInBatch &&\n [CredentialMultiInstanceState.SingleInstanceUnused, CredentialMultiInstanceState.SingleInstanceUsed].includes(\n credentialRecord.multiInstanceState\n ))\n ) {\n return true\n }\n\n // Otherwise we return whether we can use a new instance\n return (\n credentialRecord.multiInstanceState === CredentialMultiInstanceState.MultiInstanceFirstUnused ||\n credentialRecord.multiInstanceState === CredentialMultiInstanceState.SingleInstanceUnused\n )\n}\n\nexport interface UseInstanceFromCredentialRecordOptions<Record extends CredentialRecord> {\n agentContext: AgentContext\n\n /**\n * Which mode to use for usage of the credential instance. See {@link CredentialMultiInstanceUseMode} for\n * more information on the available options\n */\n useMode: CredentialMultiInstanceUseMode\n credentialRecord: Record\n\n /**\n * The update mode for the credential record when a new instance is used.\n *\n * @default `CredentialMultiInstanceUseUpdateMode.RefetchAndUpdateWithLock`\n */\n updateMode?: CredentialMultiInstanceUseUpdateMode\n}\n\nexport interface UseInstanceFromCredentialRecordReturn<Record extends CredentialRecord> {\n /**\n * The credential instance with the kms key id\n */\n credentialInstance: CredentialInstanceReturnType<Record>\n\n /**\n * If the first instance was used, this value will be `true`. The first time\n * the first instance is used, technically the credential is not reused yet,\n * but we make no distinction between this.\n */\n isReused: boolean\n\n /**\n * Whether the last new instance was used. If `isReused` is `true` this value will\n * always be `false`.\n *\n * The last new instance is the second entry in the mdoc instances, since the first\n * one is reserved for 'reused' usage.\n */\n isLastNewInstance: boolean\n}\n\n/**\n * Extract an instance with the correct kms key id from the credential instances\n * on the record. Note that if an instance is extracted (that is not reused) it\n * will remove the instance from the record, and the record should be updated in\n * storage after usage.\n *\n * Note that the last credential instance is never removed from the record. So if the\n * method indicates the last instance has been used, you should remove the credential\n * from storage if you don't want it to be used anymore in the future.\n */\nexport async function useInstanceFromCredentialRecord<Record extends CredentialRecord>({\n credentialRecord,\n useMode,\n agentContext,\n updateMode = CredentialMultiInstanceUseUpdateMode.RefetchAndUpdateWithLock,\n}: UseInstanceFromCredentialRecordOptions<Record>): Promise<UseInstanceFromCredentialRecordReturn<Record>> {\n let extractResult = extractInstanceAndUpdateRecord({\n credentialRecord,\n useMode,\n })\n\n if (\n (!extractResult.isReused || extractResult.isLastNewInstance) &&\n updateMode === CredentialMultiInstanceUseUpdateMode.Update\n ) {\n await updateCredentialRecord(agentContext, credentialRecord)\n }\n\n if (\n (!extractResult.isReused || extractResult.isLastNewInstance) &&\n updateMode === CredentialMultiInstanceUseUpdateMode.RefetchAndUpdateWithLock\n ) {\n // the method is generic, but the types don't work well generically\n const repository = repositoryForRecord(agentContext, credentialRecord) as SdJwtVcRepository\n\n await repository.updateByIdWithLock(\n agentContext,\n credentialRecord.id,\n async (freshCredentialRecord: SdJwtVcRecord) => {\n extractResult = extractInstanceAndUpdateRecord({\n credentialRecord: freshCredentialRecord,\n useMode,\n })\n\n return freshCredentialRecord\n }\n )\n }\n\n let transformedCredentialInstance: W3cVerifiableCredential | Mdoc | SdJwtVc | W3cV2VerifiableCredential\n if (credentialRecord instanceof MdocRecord) {\n const { issuerSignedBase64Url, kmsKeyId } = extractResult.credentialInstance as MdocRecordInstances[0]\n transformedCredentialInstance = Mdoc.fromBase64Url(issuerSignedBase64Url)\n transformedCredentialInstance.deviceKeyId = kmsKeyId ?? transformedCredentialInstance.deviceKey.legacyKeyId\n } else if (credentialRecord instanceof SdJwtVcRecord) {\n const { compactSdJwtVc, kmsKeyId } = extractResult.credentialInstance as SdJwtVcRecordInstances[0]\n\n transformedCredentialInstance = {\n ...decodeSdJwtVc(compactSdJwtVc, credentialRecord.typeMetadata),\n kmsKeyId,\n }\n } else if (credentialRecord instanceof W3cCredentialRecord) {\n const { credential } = extractResult.credentialInstance as W3cCredentialRecordInstances[0]\n\n transformedCredentialInstance =\n typeof credential === 'string'\n ? W3cJwtVerifiableCredential.fromSerializedJwt(credential)\n : W3cJsonLdVerifiableCredential.fromJson(credential)\n } else if (credentialRecord instanceof W3cV2CredentialRecord) {\n const { credential } = extractResult.credentialInstance as W3cV2CredentialRecordInstances[0]\n\n transformedCredentialInstance = credential.includes('~')\n ? W3cV2SdJwtVerifiableCredential.fromCompact(credential)\n : W3cV2JwtVerifiableCredential.fromCompact(credential)\n } else {\n throw new CredoError('Unsupported record type')\n }\n\n return {\n credentialInstance: transformedCredentialInstance as CredentialInstanceReturnType<Record>,\n isReused: extractResult.isReused,\n isLastNewInstance: extractResult.isLastNewInstance,\n }\n}\n\nfunction extractInstanceAndUpdateRecord<Record extends CredentialRecord>({\n credentialRecord,\n useMode,\n}: Pick<UseInstanceFromCredentialRecordOptions<Record>, 'credentialRecord' | 'useMode'>) {\n if (credentialRecord.credentialInstances.length === 1 || useMode === CredentialMultiInstanceUseMode.First) {\n const isFirstUnused =\n credentialRecord.multiInstanceState === CredentialMultiInstanceState.MultiInstanceFirstUnused ||\n credentialRecord.multiInstanceState === CredentialMultiInstanceState.SingleInstanceUnused\n\n const isMultiInstance =\n credentialRecord.multiInstanceState === CredentialMultiInstanceState.MultiInstanceFirstUnused ||\n credentialRecord.multiInstanceState === CredentialMultiInstanceState.MultiInstanceFirstUsed\n\n const allowsReuse =\n // New does not allow reuse\n useMode === CredentialMultiInstanceUseMode.New\n ? false\n : // NewIfReceivedInBatch only allows reuse if the credential was not multi instance\n useMode === CredentialMultiInstanceUseMode.NewIfReceivedInBatch\n ? !isMultiInstance\n : // Otherwise we allow reuse (First or NewOrFirst)\n true\n\n if (isFirstUnused) {\n credentialRecord.multiInstanceState =\n credentialRecord.multiInstanceState === CredentialMultiInstanceState.SingleInstanceUnused\n ? CredentialMultiInstanceState.SingleInstanceUsed\n : CredentialMultiInstanceState.MultiInstanceFirstUsed\n } else if (!allowsReuse) {\n throw new CredoError(\n `Unable to extract new credential instance from ${credentialRecord.type} with id '${credentialRecord.id}', since it only contains a single credential instance but using a new instance is required due to use mode '${useMode}'.`\n )\n }\n\n return {\n isReused: !isFirstUnused,\n credentialInstance: credentialRecord.credentialInstances[0],\n isLastNewInstance: isFirstUnused,\n }\n }\n\n // We have multiple instances, so we pop the last one (never the first one)\n const _credentialInstance = credentialRecord.credentialInstances.pop()\n if (!_credentialInstance) {\n throw new CredoError(\n `Unable to extract credential instance from ${credentialRecord.type} with id '${credentialRecord.id}', since the credential record does not contain any credential instances.`\n )\n }\n\n return {\n credentialInstance: _credentialInstance,\n isReused: false,\n isLastNewInstance: false,\n }\n}\n\nfunction repositoryForRecord<Record extends CredentialRecord>(\n agentContext: AgentContext,\n record: CredentialRecord\n): CredentialRepositoryForRecord<Record> {\n if (record instanceof W3cCredentialRecord)\n return agentContext.resolve(W3cCredentialRepository) as CredentialRepositoryForRecord<Record>\n if (record instanceof W3cV2CredentialRecord)\n return agentContext.resolve(W3cV2CredentialRepository) as CredentialRepositoryForRecord<Record>\n if (record instanceof MdocRecord) return agentContext.resolve(MdocRepository) as CredentialRepositoryForRecord<Record>\n return agentContext.resolve(SdJwtVcRepository) as CredentialRepositoryForRecord<Record>\n}\n\nfunction updateCredentialRecord(agentContext: AgentContext, record: CredentialRecord) {\n if (record instanceof W3cCredentialRecord)\n return agentContext.resolve(W3cCredentialRepository).update(agentContext, record)\n if (record instanceof W3cV2CredentialRecord)\n return agentContext.resolve(W3cV2CredentialRepository).update(agentContext, record)\n if (record instanceof MdocRecord) return agentContext.resolve(MdocRepository).update(agentContext, record)\n return agentContext.resolve(SdJwtVcRepository).update(agentContext, record)\n}\n"],"mappings":"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"}