diff --git a/packages/openid4vc-client/CHANGELOG.md b/packages/openid4vc-client/CHANGELOG.md deleted file mode 100644 index 0c03004490..0000000000 --- a/packages/openid4vc-client/CHANGELOG.md +++ /dev/null @@ -1,27 +0,0 @@ -# Change Log - -All notable changes to this project will be documented in this file. -See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. - -## [0.4.2](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.1...v0.4.2) (2023-10-05) - -**Note:** Version bump only for package @aries-framework/openid4vc-client - -## [0.4.1](https://github.com/hyperledger/aries-framework-javascript/compare/v0.4.0...v0.4.1) (2023-08-28) - -**Note:** Version bump only for package @aries-framework/openid4vc-client - -# [0.4.0](https://github.com/hyperledger/aries-framework-javascript/compare/v0.3.3...v0.4.0) (2023-06-03) - -### Bug Fixes - -- remove scope check from response ([#1450](https://github.com/hyperledger/aries-framework-javascript/issues/1450)) ([7dd4061](https://github.com/hyperledger/aries-framework-javascript/commit/7dd406170c75801529daf4bebebde81e84a4cb79)) - -### Features - -- **core:** add W3cCredentialsApi ([c888736](https://github.com/hyperledger/aries-framework-javascript/commit/c888736cb6b51014e23f5520fbc4074cf0e49e15)) -- **openid4vc-client:** openid authorization flow ([#1384](https://github.com/hyperledger/aries-framework-javascript/issues/1384)) ([996c08f](https://github.com/hyperledger/aries-framework-javascript/commit/996c08f8e32e58605408f5ed5b6d8116cea3b00c)) -- **openid4vc-client:** pre-authorized ([#1243](https://github.com/hyperledger/aries-framework-javascript/issues/1243)) ([3d86e78](https://github.com/hyperledger/aries-framework-javascript/commit/3d86e78a4df87869aa5df4e28b79cd91787b61fb)) -- **openid4vc:** jwt format and more crypto ([#1472](https://github.com/hyperledger/aries-framework-javascript/issues/1472)) ([bd4932d](https://github.com/hyperledger/aries-framework-javascript/commit/bd4932d34f7314a6d49097b6460c7570e1ebc7a8)) -- outbound message send via session ([#1335](https://github.com/hyperledger/aries-framework-javascript/issues/1335)) ([582c711](https://github.com/hyperledger/aries-framework-javascript/commit/582c711728db12b7d38a0be2e9fa78dbf31b34c6)) -- support more key types in jws service ([#1453](https://github.com/hyperledger/aries-framework-javascript/issues/1453)) ([8a3f03e](https://github.com/hyperledger/aries-framework-javascript/commit/8a3f03eb0dffcf46635556defdcebe1d329cf428)) diff --git a/packages/openid4vc-client/README.md b/packages/openid4vc-client/README.md deleted file mode 100644 index 540339fef7..0000000000 --- a/packages/openid4vc-client/README.md +++ /dev/null @@ -1,167 +0,0 @@ -

-
- Hyperledger Aries logo -

-

Aries Framework JavaScript Open ID Connect For Verifiable Credentials Client Module

-

- License - typescript - @aries-framework/openid4vc-client version - -

-
- -Open ID Connect For Verifiable Credentials Client Module for [Aries Framework JavaScript](https://github.com/hyperledger/aries-framework-javascript). - -### Installation - -Make sure you have set up the correct version of Aries Framework JavaScript according to the AFJ repository. - -```sh -yarn add @aries-framework/openid4vc-client -``` - -### Quick start - -#### Requirements - -Before a credential can be requested, you need the issuer URI. This URI starts with `openid-initiate-issuance://` and is provided by the issuer. The issuer URI is commonly acquired by scanning a QR code. - -#### Module registration - -In order to get this module to work, we need to inject it into the agent. This makes the module's functionality accessible through the agent's `modules` api. - -```ts -import { OpenId4VcClientModule } from '@aries-framework/openid4vc-client' - -const agent = new Agent({ - config: { - /* config */ - }, - dependencies: agentDependencies, - modules: { - openId4VcClient: new OpenId4VcClientModule(), - /* other custom modules */ - }, -}) - -await agent.initialize() -``` - -How the module is injected and the agent has been initialized, you can access the module's functionality through `agent.modules.openId4VcClient`. - -#### Preparing a DID - -In order to request a credential, you'll need to provide a DID that the issuer will use for setting the credential subject. In the following snippet we create one for the sake of the example, but this can be any DID that has a _authentication verification method_ with key type `Ed25519`. - -```ts -// first we create the DID -const did = await agent.dids.create({ - method: 'key', - options: { - keyType: KeyType.Ed25519, - }, -}) - -// next we do some assertions and extract the key identifier (kid) - -if ( - !did.didState.didDocument || - !did.didState.didDocument.authentication || - did.didState.didDocument.authentication.length === 0 -) { - throw new Error("Error creating did document, or did document has no 'authentication' verificationMethods") -} - -const [verificationMethod] = did.didState.didDocument.authentication -const kid = typeof verificationMethod === 'string' ? verificationMethod : verificationMethod.id -``` - -#### Requesting the credential (Pre-Authorized) - -Now a credential issuance can be requested as follows. - -```ts -const w3cCredentialRecord = await agent.modules.openId4VcClient.requestCredentialPreAuthorized({ - issuerUri, - kid, - checkRevocationState: false, -}) - -console.log(w3cCredentialRecord) -``` - -#### Full example - -```ts -import { OpenId4VcClientModule } from '@aries-framework/openid4vc-client' -import { agentDependencies } from '@aries-framework/node' // use @aries-framework/react-native for React Native -import { Agent, KeyDidCreateOptions } from '@aries-framework/core' - -const run = async () => { - const issuerUri = '' // The obtained issuer URI - - // Create the Agent - const agent = new Agent({ - config: { - /* config */ - }, - dependencies: agentDependencies, - modules: { - openId4VcClient: new OpenId4VcClientModule(), - /* other custom modules */ - }, - }) - - // Initialize the Agent - await agent.initialize() - - // Create a DID - const did = await agent.dids.create({ - method: 'key', - options: { - keyType: KeyType.Ed25519, - }, - }) - - // Assert DIDDocument is valid - if ( - !did.didState.didDocument || - !did.didState.didDocument.authentication || - did.didState.didDocument.authentication.length === 0 - ) { - throw new Error("Error creating did document, or did document has no 'authentication' verificationMethods") - } - - // Extract key identified (kid) for authentication verification method - const [verificationMethod] = did.didState.didDocument.authentication - const kid = typeof verificationMethod === 'string' ? verificationMethod : verificationMethod.id - - // Request the credential - const w3cCredentialRecord = await agent.modules.openId4VcClient.requestCredentialPreAuthorized({ - issuerUri, - kid, - checkRevocationState: false, - }) - - // Log the received credential - console.log(w3cCredentialRecord) -} -``` diff --git a/packages/openid4vc-client/src/OpenId4VcClientApi.ts b/packages/openid4vc-client/src/OpenId4VcClientApi.ts deleted file mode 100644 index 5049c518ba..0000000000 --- a/packages/openid4vc-client/src/OpenId4VcClientApi.ts +++ /dev/null @@ -1,53 +0,0 @@ -import type { - GenerateAuthorizationUrlOptions, - PreAuthCodeFlowOptions, - AuthCodeFlowOptions, -} from './OpenId4VcClientServiceOptions' -import type { W3cCredentialRecord } from '@aries-framework/core' - -import { injectable, AgentContext } from '@aries-framework/core' - -import { OpenId4VcClientService } from './OpenId4VcClientService' -import { AuthFlowType } from './OpenId4VcClientServiceOptions' - -/** - * @public - */ -@injectable() -export class OpenId4VcClientApi { - private agentContext: AgentContext - private openId4VcClientService: OpenId4VcClientService - - public constructor(agentContext: AgentContext, openId4VcClientService: OpenId4VcClientService) { - this.agentContext = agentContext - this.openId4VcClientService = openId4VcClientService - } - - public async requestCredentialUsingPreAuthorizedCode( - options: PreAuthCodeFlowOptions - ): Promise { - // set defaults - const verifyRevocationState = options.verifyCredentialStatus ?? true - - return this.openId4VcClientService.requestCredential(this.agentContext, { - ...options, - verifyCredentialStatus: verifyRevocationState, - flowType: AuthFlowType.PreAuthorizedCodeFlow, - }) - } - - public async requestCredentialUsingAuthorizationCode(options: AuthCodeFlowOptions): Promise { - // set defaults - const checkRevocationState = options.verifyCredentialStatus ?? true - - return this.openId4VcClientService.requestCredential(this.agentContext, { - ...options, - verifyCredentialStatus: checkRevocationState, - flowType: AuthFlowType.AuthorizationCodeFlow, - }) - } - - public async generateAuthorizationUrl(options: GenerateAuthorizationUrlOptions) { - return this.openId4VcClientService.generateAuthorizationUrl(options) - } -} diff --git a/packages/openid4vc-client/src/OpenId4VcClientModule.ts b/packages/openid4vc-client/src/OpenId4VcClientModule.ts deleted file mode 100644 index 6eb598b3d3..0000000000 --- a/packages/openid4vc-client/src/OpenId4VcClientModule.ts +++ /dev/null @@ -1,34 +0,0 @@ -import type { DependencyManager, Module } from '@aries-framework/core' - -import { AgentConfig } from '@aries-framework/core' - -import { OpenId4VcClientApi } from './OpenId4VcClientApi' -import { OpenId4VcClientService } from './OpenId4VcClientService' -import { OpenId4VpClientService, PresentationExchangeService } from './presentations' - -/** - * @public - */ -export class OpenId4VcClientModule implements Module { - public readonly api = OpenId4VcClientApi - - /** - * Registers the dependencies of the question answer module on the dependency manager. - */ - public register(dependencyManager: DependencyManager) { - // Warn about experimental module - dependencyManager - .resolve(AgentConfig) - .logger.warn( - "The '@aries-framework/openid4vc-client' module is experimental and could have unexpected breaking changes. When using this module, make sure to use strict versions for all @aries-framework packages." - ) - - // Api - dependencyManager.registerContextScoped(OpenId4VcClientApi) - - // Services - dependencyManager.registerSingleton(OpenId4VcClientService) - dependencyManager.registerSingleton(OpenId4VpClientService) - dependencyManager.registerSingleton(PresentationExchangeService) - } -} diff --git a/packages/openid4vc-client/src/OpenId4VcClientService.ts b/packages/openid4vc-client/src/OpenId4VcClientService.ts deleted file mode 100644 index e8d7247e2f..0000000000 --- a/packages/openid4vc-client/src/OpenId4VcClientService.ts +++ /dev/null @@ -1,733 +0,0 @@ -import type { - GenerateAuthorizationUrlOptions, - RequestCredentialOptions, - ProofOfPossessionVerificationMethodResolver, - SupportedCredentialFormats, - ProofOfPossessionRequirements, -} from './OpenId4VcClientServiceOptions' -import type { OpenIdCredentialFormatProfile } from './utils' -import type { - AgentContext, - W3cVerifiableCredential, - VerificationMethod, - JwaSignatureAlgorithm, - W3cVerifyCredentialResult, -} from '@aries-framework/core' -import type { - CredentialOfferFormat, - CredentialOfferPayloadV1_0_08, - CredentialOfferRequestWithBaseUrl, - CredentialResponse, - CredentialSupported, - Jwt, - OpenIDResponse, - ProofOfPossessionCallbacks, -} from '@sphereon/oid4vci-common' - -import { - W3cCredentialRecord, - ClaimFormat, - getJwkClassFromJwaSignatureAlgorithm, - W3cJwtVerifiableCredential, - AriesFrameworkError, - getKeyFromVerificationMethod, - Hasher, - inject, - injectable, - InjectionSymbols, - JsonEncoder, - JsonTransformer, - TypedArrayEncoder, - W3cJsonLdVerifiableCredential, - getJwkFromKey, - getSupportedVerificationMethodTypesFromKeyType, - getJwkClassFromKeyType, - parseDid, - SignatureSuiteRegistry, - JwsService, - Logger, - W3cCredentialService, - W3cCredentialRepository, -} from '@aries-framework/core' -import { CredentialRequestClientBuilder, OpenID4VCIClient, ProofOfPossessionBuilder } from '@sphereon/oid4vci-client' -import { AuthzFlowType, CodeChallengeMethod, OpenId4VCIVersion } from '@sphereon/oid4vci-common' -import { randomStringForEntropy } from '@stablelib/random' - -import { supportedCredentialFormats, AuthFlowType } from './OpenId4VcClientServiceOptions' -import { setOpenId4VcCredentialMetadata, fromOpenIdCredentialFormatProfileToDifClaimFormat } from './utils' -import { getUniformFormat } from './utils/Formats' -import { getSupportedCredentials } from './utils/IssuerMetadataUtils' - -/** - * The type of a credential offer entry. For each item in `credentials` array, the type MUST be one of the following: - * - CredentialSupported, when the value is a string and points to a credential from the `credentials_supported` array. - * - InlineCredentialOffer, when the value is a JSON object that represents an inline credential offer. - */ -export enum OfferedCredentialType { - CredentialSupported = 'CredentialSupported', - InlineCredentialOffer = 'InlineCredentialOffer', -} - -export type OfferedCredentialsWithMetadata = - | { credentialSupported: CredentialSupported; type: OfferedCredentialType.CredentialSupported } - | { inlineCredentialOffer: CredentialOfferFormat; type: OfferedCredentialType.InlineCredentialOffer } - -const flowTypeMapping = { - [AuthFlowType.AuthorizationCodeFlow]: AuthzFlowType.AUTHORIZATION_CODE_FLOW, - [AuthFlowType.PreAuthorizedCodeFlow]: AuthzFlowType.PRE_AUTHORIZED_CODE_FLOW, -} - -/** - * @internal - */ -@injectable() -export class OpenId4VcClientService { - private logger: Logger - private w3cCredentialService: W3cCredentialService - private w3cCredentialRepository: W3cCredentialRepository - private jwsService: JwsService - - public constructor( - @inject(InjectionSymbols.Logger) logger: Logger, - w3cCredentialService: W3cCredentialService, - w3cCredentialRepository: W3cCredentialRepository, - jwsService: JwsService - ) { - this.w3cCredentialService = w3cCredentialService - this.w3cCredentialRepository = w3cCredentialRepository - this.jwsService = jwsService - this.logger = logger - } - - private generateCodeVerifier(): string { - return randomStringForEntropy(256) - } - - public async generateAuthorizationUrl(options: GenerateAuthorizationUrlOptions) { - this.logger.debug('Generating authorization url') - - if (!options.scope || options.scope.length === 0) { - throw new AriesFrameworkError( - 'Only scoped based authorization requests are supported at this time. Please provide at least one scope' - ) - } - - const client = await OpenID4VCIClient.fromURI({ - uri: options.initiationUri, - flowType: AuthzFlowType.AUTHORIZATION_CODE_FLOW, - }) - - const codeVerifier = this.generateCodeVerifier() - const codeVerifierSha256 = Hasher.hash(TypedArrayEncoder.fromString(codeVerifier), 'sha2-256') - const base64Url = TypedArrayEncoder.toBase64URL(codeVerifierSha256) - - this.logger.debug('Converted code_verifier to code_challenge', { - codeVerifier: codeVerifier, - sha256: codeVerifierSha256.toString(), - base64Url: base64Url, - }) - - const authorizationUrl = client.createAuthorizationRequestUrl({ - clientId: options.clientId, - codeChallengeMethod: CodeChallengeMethod.SHA256, - codeChallenge: base64Url, - redirectUri: options.redirectUri, - scope: options.scope?.join(' '), - }) - - return { - authorizationUrl, - codeVerifier, - } - } - - public async requestCredential(agentContext: AgentContext, options: RequestCredentialOptions) { - const receivedCredentials: W3cCredentialRecord[] = [] - const supportedJwaSignatureAlgorithms = this.getSupportedJwaSignatureAlgorithms(agentContext) - - const allowedProofOfPossessionSignatureAlgorithms = options.allowedProofOfPossessionSignatureAlgorithms - ? options.allowedProofOfPossessionSignatureAlgorithms.filter((algorithm) => - supportedJwaSignatureAlgorithms.includes(algorithm) - ) - : supportedJwaSignatureAlgorithms - - // Take the allowed credential formats from the options or use the default - const allowedCredentialFormats = options.allowedCredentialFormats ?? supportedCredentialFormats - - const flowType = flowTypeMapping[options.flowType] - if (!flowType) { - throw new AriesFrameworkError( - `Unsupported flowType ${options.flowType}. Valid values are ${Object.values(AuthFlowType).join(', ')}` - ) - } - - const client = await OpenID4VCIClient.fromURI({ - uri: options.issuerUri, - flowType, - retrieveServerMetadata: false, - }) - - const serverMetadata = await client.retrieveServerMetadata() - - this.logger.info('Fetched server metadata', { - issuer: serverMetadata.issuer, - credentialEndpoint: serverMetadata.credential_endpoint, - tokenEndpoint: serverMetadata.token_endpoint, - }) - - this.logger.debug('Full server metadata', serverMetadata) - - // acquire the access token - // NOTE: only scope based flow is supported for authorized flow. However there's not clear mapping between - // the scope property and which credential to request (this is out of scope of the spec), so it will still - // just request all credentials that have been offered in the credential offer. We may need to add some extra - // input properties that allows to define the credential type(s) to request. - const accessToken = - options.flowType === AuthFlowType.AuthorizationCodeFlow - ? await client.acquireAccessToken({ - clientId: options.clientId, - code: options.authorizationCode, - codeVerifier: options.codeVerifier, - redirectUri: options.redirectUri, - }) - : await client.acquireAccessToken({}) // TODO: PIN - - // Loop through all the credentialTypes in the credential offer - for (const offeredCredential of this.getOfferedCredentialsWithMetadata(client)) { - const format = ( - isInlineCredentialOffer(offeredCredential) - ? offeredCredential.inlineCredentialOffer.format - : offeredCredential.credentialSupported.format - ) as SupportedCredentialFormats // TODO: can we remove the cast? - - // TODO: support inline credential offers. Not clear to me how to determine the did method / alg, etc.. - if (offeredCredential.type === OfferedCredentialType.InlineCredentialOffer) { - // Check if the format is supported/allowed - if (!allowedCredentialFormats.includes(format)) continue - } else { - const supportedCredentialMetadata = offeredCredential.credentialSupported - - // FIXME - // TODO: that is not a must v11 could end in the same way - // If the credential id ends with the format, it is a v8 credential supported that has been - // split into multiple entries (each entry can now only have one format). For now we continue - // as assume there will be another entry with the correct format. - if (supportedCredentialMetadata.id?.endsWith(`-${supportedCredentialMetadata.format}`)) { - const uniformFormat = getUniformFormat(supportedCredentialMetadata.format) as SupportedCredentialFormats - if (!allowedCredentialFormats.includes(uniformFormat)) continue - } - } - - // Get all options for the credential request (such as which kid to use, the signature algorithm, etc) - const { verificationMethod, signatureAlgorithm } = await this.getCredentialRequestOptions(agentContext, { - allowedCredentialFormats, - allowedProofOfPossessionSignatureAlgorithms, - offeredCredentialWithMetadata: offeredCredential, - proofOfPossessionVerificationMethodResolver: options.proofOfPossessionVerificationMethodResolver, - }) - - const callbacks: ProofOfPossessionCallbacks = { - signCallback: this.signCallback(agentContext, verificationMethod), - // TODO: verify callback - } - - // Create the proof of possession - const proofInput = await ProofOfPossessionBuilder.fromAccessTokenResponse({ - accessTokenResponse: accessToken, - callbacks, - version: client.version(), - }) - .withEndpointMetadata(serverMetadata) - .withAlg(signatureAlgorithm) - .withClientId(verificationMethod.controller) - .withKid(verificationMethod.id) - .build() - - this.logger.debug('Generated JWS', proofInput) - - // Acquire the credential - const credentialRequestClient = // TODO: don't use the uri not actual anymore https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0-08.html - ( - await CredentialRequestClientBuilder.fromURI({ - uri: options.issuerUri, - metadata: serverMetadata, - }) - ) - .withTokenFromResponse(accessToken) - .build() - - let credentialResponse: OpenIDResponse - - if (isInlineCredentialOffer(offeredCredential)) { - credentialResponse = await credentialRequestClient.acquireCredentialsUsingProof({ - proofInput, - credentialTypes: offeredCredential.inlineCredentialOffer.types, - format: offeredCredential.inlineCredentialOffer.format, - }) - } else { - credentialResponse = await credentialRequestClient.acquireCredentialsUsingProof({ - proofInput, - credentialTypes: offeredCredential.type, - format: offeredCredential.credentialSupported.format, - }) - } - - const credential = await this.handleCredentialResponse(agentContext, credentialResponse, { - verifyCredentialStatus: options.verifyCredentialStatus, - }) - - // Create credential record, but we don't store it yet (only after the user has accepted the credential) - const credentialRecord = new W3cCredentialRecord({ - credential, - tags: { - expandedTypes: [], - }, - }) - this.logger.debug('Full credential', credentialRecord) - - if (!isInlineCredentialOffer(offeredCredential)) { - const issuerMetadata = client.endpointMetadata.credentialIssuerMetadata - if (!issuerMetadata) { - // TODO: this should not happen - throw new AriesFrameworkError('Issuer metadata not found') - } - const supportedCredentialMetadata = offeredCredential.credentialSupported - // Set the OpenId4Vc credential metadata and update record - setOpenId4VcCredentialMetadata(credentialRecord, supportedCredentialMetadata, serverMetadata, issuerMetadata) - } - - receivedCredentials.push(credentialRecord) - } - - return receivedCredentials - } - - /** - * Get the options for the credential request. Internally this will resolve the proof of possession - * requirements, and based on that it will call the proofOfPossessionVerificationMethodResolver to - * allow the caller to select the correct verification method based on the requirements for the proof - * of possession. - */ - private async getCredentialRequestOptions( - agentContext: AgentContext, - options: { - proofOfPossessionVerificationMethodResolver: ProofOfPossessionVerificationMethodResolver - allowedCredentialFormats: SupportedCredentialFormats[] - allowedProofOfPossessionSignatureAlgorithms: JwaSignatureAlgorithm[] - offeredCredentialWithMetadata: OfferedCredentialsWithMetadata - } - ) { - const { signatureAlgorithm, supportedDidMethods, supportsAllDidMethods } = this.getProofOfPossessionRequirements( - agentContext, - { - offeredCredentialWithMetadata: options.offeredCredentialWithMetadata, - allowedCredentialFormats: options.allowedCredentialFormats, - allowedProofOfPossessionSignatureAlgorithms: options.allowedProofOfPossessionSignatureAlgorithms, - } - ) - - const JwkClass = getJwkClassFromJwaSignatureAlgorithm(signatureAlgorithm) - - if (!JwkClass) { - throw new AriesFrameworkError( - `Could not determine JWK key type based on JWA signature algorithm '${signatureAlgorithm}'` - ) - } - - const supportedVerificationMethods = getSupportedVerificationMethodTypesFromKeyType(JwkClass.keyType) - - const format = isInlineCredentialOffer(options.offeredCredentialWithMetadata) - ? options.offeredCredentialWithMetadata.inlineCredentialOffer.format - : options.offeredCredentialWithMetadata.credentialSupported.format - - // Now we need to determine the did method and alg based on the cryptographic suite - const verificationMethod = await options.proofOfPossessionVerificationMethodResolver({ - credentialFormat: format as SupportedCredentialFormats, - proofOfPossessionSignatureAlgorithm: signatureAlgorithm, - supportedVerificationMethods, - keyType: JwkClass.keyType, - supportedCredentialId: !isInlineCredentialOffer(options.offeredCredentialWithMetadata) - ? options.offeredCredentialWithMetadata.credentialSupported.id - : undefined, - supportsAllDidMethods, - supportedDidMethods, - }) - - // Make sure the verification method uses a supported did method - if ( - !supportsAllDidMethods && - // If supportedDidMethods is undefined, it means the issuer didn't include the binding methods in the metadata - // The user can still select a verification method, but we can't validate it - supportedDidMethods !== undefined && - !supportedDidMethods.find((supportedDidMethod) => verificationMethod.id.startsWith(supportedDidMethod)) - ) { - const { method } = parseDid(verificationMethod.id) - const supportedDidMethodsString = supportedDidMethods.join(', ') - throw new AriesFrameworkError( - `Verification method uses did method '${method}', but issuer only supports '${supportedDidMethodsString}'` - ) - } - - // Make sure the verification method uses a supported verification method type - if (!supportedVerificationMethods.includes(verificationMethod.type)) { - const supportedVerificationMethodsString = supportedVerificationMethods.join(', ') - throw new AriesFrameworkError( - `Verification method uses verification method type '${verificationMethod.type}', but only '${supportedVerificationMethodsString}' verification methods are supported for key type '${JwkClass.keyType}'` - ) - } - - return { verificationMethod, signatureAlgorithm } - } - - // TODO: i cannot view this - // todo https://sphereon.atlassian.net/browse/VDX-184 - /** - * Returns all entries from the credential offer. This includes both 'id' entries that reference a supported credential in the issuer metadata, - * as well as inline credential offers that do not reference a supported credential in the issuer metadata. - */ - private getOfferedCredentials( - credentialOfferRequestWithBaseUrl: CredentialOfferRequestWithBaseUrl - ): Array { - if (credentialOfferRequestWithBaseUrl.version < OpenId4VCIVersion.VER_1_0_11) { - const credentialOffer = - credentialOfferRequestWithBaseUrl.original_credential_offer as CredentialOfferPayloadV1_0_08 - - return typeof credentialOffer.credential_type === 'string' - ? [credentialOffer.credential_type] - : credentialOffer.credential_type - } else { - return credentialOfferRequestWithBaseUrl.credential_offer.credentials - } - } - - /** - * Return a normalized version of the credentials supported by the issuer. Can optionally filter based on the credentials - * that were offered, or the type of credentials that are supported. - * - * - * NOTE: for v1_0-08, a single credential id in the issuer metadata could have multiple formats. When retrieving the - * supported credentials, for v1_0-08, the format is appended to the id if there are multiple formats supported for - * that credential id. E.g. if the issuer metadata for v1_0-08 contains an entry with key `OpenBadgeCredential` and - * the supported formats are `jwt_vc-jsonld` and `ldp_vc`, then the id in the credentials supported will be - * `OpenBadgeCredential-jwt_vc-jsonld` and `OpenBadgeCredential-ldp_vc`, even though the offered credential is simply - * `OpenBadgeCredential`. - * - * NOTE: this method only returns the credentials supported by the issuer metadata. It does not take into account the inline - * credentials offered. Use {@link getOfferedCredentialsWithMetadata} to get both the inline and referenced offered credentials. - */ - private getCredentialsSupported( - client: OpenID4VCIClient, - restrictToOfferIds: boolean, - credentialSupportedId?: string - ): CredentialSupported[] { - const offeredIds = this.getOfferedCredentials(client.credentialOffer).filter( - (c): c is string => typeof c === 'string' - ) - - const credentialSupportedIds = restrictToOfferIds ? offeredIds : undefined - - const credentialsSupported = getSupportedCredentials({ - issuerMetadata: client.endpointMetadata.credentialIssuerMetadata, - version: client.version(), - credentialSupportedIds, - }) - - return credentialSupportedId - ? credentialsSupported.filter( - (credentialSupported) => - credentialSupported.id === credentialSupportedId || - credentialSupported.id === `${credentialSupportedId}-${credentialSupported.format}` - ) - : credentialsSupported - } - - /** - * Returns all entries from the credential offer with the associated metadata resolved. For inline entries, the offered credential object - * is included directly. For 'id' entries, the associated `credentials_supported` object is resolved from the issuer metadata. - * - * NOTE: for v1_0-08, a single credential id in the issuer metadata could have multiple formats. This means that the returned value - * from this method could contain multiple entries for a single credential id, but with different formats. This is detectable as the - * id will be the `-`. - */ - private getOfferedCredentialsWithMetadata = (client: OpenID4VCIClient) => { - const offeredCredentials: Array = [] - - for (const offeredCredential of this.getOfferedCredentials(client.credentialOffer)) { - // If the offeredCredential is a string, it references a supported credential in the issuer metadata - if (typeof offeredCredential === 'string') { - const credentialsSupported = this.getCredentialsSupported(client, false, offeredCredential) - - // Make sure the issuer metadata includes the offered credential. - if (credentialsSupported.length === 0) { - throw new Error( - `Offered credential '${offeredCredential}' is not present in the credentials_supported of the issuer metadata` - ) - } - - offeredCredentials.push( - ...credentialsSupported.map((credentialSupported) => { - return { credentialSupported, type: OfferedCredentialType.CredentialSupported } as const - }) - ) - } - // Otherwise it's an inline credential offer that does not reference a supported credential in the issuer metadata - else { - // TODO: we could transform the inline offer to the `CredentialSupported` format, but we'll only be able to populate - // the `format`, `types` and `@context` fields. It's not really clear how to determine the supported did methods, - // signature suites, etc.. for these inline credentials. - // We should also add a property to indicate to the user that this is an inline credential offer. - // if (offeredCredential.format === 'jwt_vc_json') { - // const supported = { - // format: offeredCredential.format, - // types: offeredCredential.types, - // } satisfies CredentialSupportedJwtVcJson; - // } else if (offeredCredential.format === 'jwt_vc_json-ld' || offeredCredential.format === 'ldp_vc') { - // const supported = { - // format: offeredCredential.format, - // '@context': offeredCredential.credential_definition['@context'], - // types: offeredCredential.credential_definition.types, - // } satisfies CredentialSupported; - // } - offeredCredentials.push({ - inlineCredentialOffer: offeredCredential, - type: OfferedCredentialType.InlineCredentialOffer, - } as const) - } - } - - return offeredCredentials - } - - /** - * Get the requirements for creating the proof of possession. Based on the allowed - * credential formats, the allowed proof of possession signature algorithms, and the - * credential type, this method will select the best credential format and signature - * algorithm to use, based on the order of preference. - */ - private getProofOfPossessionRequirements( - agentContext: AgentContext, - options: { - allowedCredentialFormats: SupportedCredentialFormats[] - offeredCredentialWithMetadata: OfferedCredentialsWithMetadata - allowedProofOfPossessionSignatureAlgorithms: JwaSignatureAlgorithm[] - } - ): ProofOfPossessionRequirements { - const { offeredCredentialWithMetadata, allowedCredentialFormats } = options - - // Extract format from offer - let format = - offeredCredentialWithMetadata.type === OfferedCredentialType.InlineCredentialOffer - ? offeredCredentialWithMetadata.inlineCredentialOffer.format - : offeredCredentialWithMetadata.credentialSupported.format - - // Get uniform format, so we don't have to deal with the different spec versions - format = getUniformFormat(format) - - const credentialMetadata = - offeredCredentialWithMetadata.type === OfferedCredentialType.CredentialSupported - ? offeredCredentialWithMetadata.credentialSupported - : undefined - - const issuerSupportedCryptographicSuites = credentialMetadata?.cryptographic_suites_supported - const issuerSupportedBindingMethods = - credentialMetadata?.cryptographic_binding_methods_supported ?? - // FIXME: somehow the MATTR Launchpad returns binding_methods_supported instead of cryptographic_binding_methods_supported - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - (credentialMetadata?.binding_methods_supported as string[] | undefined) - - if (!isInlineCredentialOffer(offeredCredentialWithMetadata)) { - const credentialMetadata = offeredCredentialWithMetadata.credentialSupported - if (!allowedCredentialFormats.includes(format as SupportedCredentialFormats)) { - throw new AriesFrameworkError( - `Issuer only supports format '${format}' for credential type '${ - credentialMetadata.id as string - }', but the wallet only allows formats '${options.allowedCredentialFormats.join(', ')}'` - ) - } - } - - // For each of the supported algs, find the key types, then find the proof types - const signatureSuiteRegistry = agentContext.dependencyManager.resolve(SignatureSuiteRegistry) - - let potentialSignatureAlgorithm: JwaSignatureAlgorithm | undefined - - switch (format) { - case 'jwt_vc_json': - case 'jwt_vc_json-ld': - // If undefined, it means the issuer didn't include the cryptographic suites in the metadata - // We just guess that the first one is supported - if (issuerSupportedCryptographicSuites === undefined) { - potentialSignatureAlgorithm = options.allowedProofOfPossessionSignatureAlgorithms[0] - } else { - potentialSignatureAlgorithm = options.allowedProofOfPossessionSignatureAlgorithms.find((signatureAlgorithm) => - issuerSupportedCryptographicSuites.includes(signatureAlgorithm) - ) - } - break - case 'ldp_vc': - // If undefined, it means the issuer didn't include the cryptographic suites in the metadata - // We just guess that the first one is supported - if (issuerSupportedCryptographicSuites === undefined) { - potentialSignatureAlgorithm = options.allowedProofOfPossessionSignatureAlgorithms[0] - } else { - // We need to find it based on the JSON-LD proof type - potentialSignatureAlgorithm = options.allowedProofOfPossessionSignatureAlgorithms.find( - (signatureAlgorithm) => { - const JwkClass = getJwkClassFromJwaSignatureAlgorithm(signatureAlgorithm) - if (!JwkClass) return false - - // TODO: getByKeyType should return a list - const matchingSuite = signatureSuiteRegistry.getByKeyType(JwkClass.keyType) - if (!matchingSuite) return false - - return issuerSupportedCryptographicSuites.includes(matchingSuite.proofType) - } - ) - } - break - default: - throw new AriesFrameworkError( - `Unsupported requested credential format '${format}' with id ${ - credentialMetadata?.id ?? 'Inline credential offer' - }` - ) - } - - const supportsAllDidMethods = issuerSupportedBindingMethods?.includes('did') ?? false - const supportedDidMethods = issuerSupportedBindingMethods?.filter((method) => method.startsWith('did:')) - - if (!potentialSignatureAlgorithm) { - throw new AriesFrameworkError( - `Could not establish signature algorithm for format ${format} and id ${ - credentialMetadata?.id ?? 'Inline credential offer' - }` - ) - } - - return { - signatureAlgorithm: potentialSignatureAlgorithm, - supportedDidMethods, - supportsAllDidMethods, - } - } - - /** - * Returns the JWA Signature Algorithms that are supported by the wallet. - * - * This is an approximation based on the supported key types of the wallet. - * This is not 100% correct as a supporting a key type does not mean you support - * all the algorithms for that key type. However, this needs refactoring of the wallet - * that is planned for the 0.5.0 release. - */ - private getSupportedJwaSignatureAlgorithms(agentContext: AgentContext): JwaSignatureAlgorithm[] { - const supportedKeyTypes = agentContext.wallet.supportedKeyTypes - - // Extract the supported JWS algs based on the key types the wallet support. - const supportedJwaSignatureAlgorithms = supportedKeyTypes - // Map the supported key types to the supported JWK class - .map(getJwkClassFromKeyType) - // Filter out the undefined values - .filter((jwkClass): jwkClass is Exclude => jwkClass !== undefined) - // Extract the supported JWA signature algorithms from the JWK class - .map((jwkClass) => jwkClass.supportedSignatureAlgorithms) - // Flatten the array of arrays - .reduce((allAlgorithms, algorithms) => [...allAlgorithms, ...algorithms], []) - - return supportedJwaSignatureAlgorithms - } - - private async handleCredentialResponse( - agentContext: AgentContext, - credentialResponse: OpenIDResponse, - options: { verifyCredentialStatus: boolean } - ) { - this.logger.debug('Credential request response', credentialResponse) - - if (!credentialResponse.successBody) { - throw new AriesFrameworkError('Did not receive a successful credential response') - } - - const format = getUniformFormat(credentialResponse.successBody.format) - const difClaimFormat = fromOpenIdCredentialFormatProfileToDifClaimFormat(format as OpenIdCredentialFormatProfile) - - let credential: W3cVerifiableCredential - let result: W3cVerifyCredentialResult - if (difClaimFormat === ClaimFormat.LdpVc) { - credential = JsonTransformer.fromJSON(credentialResponse.successBody.credential, W3cJsonLdVerifiableCredential) - result = await this.w3cCredentialService.verifyCredential(agentContext, { - credential, - verifyCredentialStatus: options.verifyCredentialStatus, - }) - } else if (difClaimFormat === ClaimFormat.JwtVc) { - credential = W3cJwtVerifiableCredential.fromSerializedJwt(credentialResponse.successBody.credential as string) - result = await this.w3cCredentialService.verifyCredential(agentContext, { - credential, - verifyCredentialStatus: options.verifyCredentialStatus, - }) - } else { - throw new AriesFrameworkError(`Unsupported credential format ${credentialResponse.successBody.format}`) - } - - if (!result || !result.isValid) { - agentContext.config.logger.error('Failed to validate credential', { - result, - }) - throw new AriesFrameworkError(`Failed to validate credential, error = ${result.error?.message ?? 'Unknown'}`) - } - - return credential - } - - private signCallback(agentContext: AgentContext, verificationMethod: VerificationMethod) { - return async (jwt: Jwt, kid?: string) => { - if (!jwt.header) { - throw new AriesFrameworkError('No header present on JWT') - } - - if (!jwt.payload) { - throw new AriesFrameworkError('No payload present on JWT') - } - - if (!kid) { - throw new AriesFrameworkError('No KID is present in the callback') - } - - // We have determined the verification method before and already passed that when creating the callback, - // however we just want to make sure that the kid matches the verification method id - if (verificationMethod.id !== kid) { - throw new AriesFrameworkError(`kid ${kid} does not match verification method id ${verificationMethod.id}`) - } - - const key = getKeyFromVerificationMethod(verificationMethod) - const jwk = getJwkFromKey(key) - - const payload = JsonEncoder.toBuffer(jwt.payload) - if (!jwk.supportsSignatureAlgorithm(jwt.header.alg)) { - throw new AriesFrameworkError( - `kid ${kid} refers to a key of type '${jwk.keyType}', which does not support the JWS signature alg '${jwt.header.alg}'` - ) - } - - // We don't support these properties, remove them, so we can pass all other header properties to the JWS service - if (jwt.header.x5c || jwt.header.jwk) throw new AriesFrameworkError('x5c and jwk are not supported') - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { x5c: _x5c, jwk: _jwk, ...supportedHeaderOptions } = jwt.header - - const jws = await this.jwsService.createJwsCompact(agentContext, { - key, - payload, - protectedHeaderOptions: supportedHeaderOptions, - }) - - return jws - } - } -} - -function isInlineCredentialOffer(offeredCredential: OfferedCredentialsWithMetadata): offeredCredential is { - inlineCredentialOffer: CredentialOfferFormat - type: OfferedCredentialType.InlineCredentialOffer -} { - return offeredCredential.type === OfferedCredentialType.InlineCredentialOffer -} diff --git a/packages/openid4vc-client/src/OpenId4VcClientServiceOptions.ts b/packages/openid4vc-client/src/OpenId4VcClientServiceOptions.ts deleted file mode 100644 index ed03c68765..0000000000 --- a/packages/openid4vc-client/src/OpenId4VcClientServiceOptions.ts +++ /dev/null @@ -1,185 +0,0 @@ -import type { JwaSignatureAlgorithm, KeyType, VerificationMethod } from '@aries-framework/core' - -import { OpenIdCredentialFormatProfile } from './utils/claimFormatMapping' - -/** - * The credential formats that are supported by the openid4vc client - */ -export type SupportedCredentialFormats = OpenIdCredentialFormatProfile.JwtVcJson | OpenIdCredentialFormatProfile.LdpVc - -export const supportedCredentialFormats = [ - OpenIdCredentialFormatProfile.JwtVcJson, - OpenIdCredentialFormatProfile.LdpVc, -] satisfies OpenIdCredentialFormatProfile[] - -/** - * Options that are used for the pre-authorized code flow. - */ -export interface PreAuthCodeFlowOptions { - issuerUri: string - verifyCredentialStatus: boolean - - /** - * A list of allowed credential formats in order of preference. - * - * If the issuer supports one of the allowed formats, that first format that is supported - * from the list will be used. - * - * If the issuer doesn't support any of the allowed formats, an error is thrown - * and the request is aborted. - */ - allowedCredentialFormats?: SupportedCredentialFormats[] - - /** - * A list of allowed proof of possession signature algorithms in order of preference. - * - * Note that the signature algorithms must be supported by the wallet implementation. - * Signature algorithms that are not supported by the wallet will be ignored. - * - * The proof of possession (pop) signature algorithm is used in the credential request - * to bind the credential to a did. In most cases the JWA signature algorithm - * that is used in the pop will determine the cryptographic suite that is used - * for signing the credential, but this not a requirement for the spec. E.g. if the - * pop uses EdDsa, the credential will most commonly also use EdDsa, or Ed25519Signature2018/2020. - */ - allowedProofOfPossessionSignatureAlgorithms?: JwaSignatureAlgorithm[] - - /** - * A function that should resolve a verification method based on the options passed. - * This method will be called once for each of the credentials that are included - * in the credential offer. - * - * Based on the credential format, JWA signature algorithm, verification method types - * and did methods, the resolver must return a verification method that will be used - * for the proof of possession signature. - */ - proofOfPossessionVerificationMethodResolver: ProofOfPossessionVerificationMethodResolver -} - -/** - * Options that are used for the authorization code flow. - * Extends the pre-authorized code flow options. - */ -export interface AuthCodeFlowOptions extends PreAuthCodeFlowOptions { - clientId: string - authorizationCode: string - codeVerifier: string - redirectUri: string -} - -/** - * The options that are used to generate the authorization url. - * - * NOTE: The `code_challenge` property is omitted here - * because we assume it will always be SHA256 - * as clear text code challenges are unsafe. - */ -export interface GenerateAuthorizationUrlOptions { - initiationUri: string - clientId: string - redirectUri: string - scope?: string[] -} - -export interface ProofOfPossessionVerificationMethodResolverOptions { - /** - * The credential format that will be requested from the issuer. - * E.g. `jwt_vc` or `ldp_vc`. - */ - credentialFormat: SupportedCredentialFormats - - /** - * The JWA Signature Algorithm that will be used in the proof of possession. - * This is based on the `allowedProofOfPossessionSignatureAlgorithms` passed - * to the request credential method, and the supported signature algorithms. - */ - proofOfPossessionSignatureAlgorithm: JwaSignatureAlgorithm - - /** - * This is a list of verification methods types that are supported - * for creating the proof of possession signature. The returned - * verification method type must be of one of these types. - */ - supportedVerificationMethods: string[] - - /** - * The key type that will be used to create the proof of possession signature. - * This is related to the verification method and the signature algorithm, and - * is added for convenience. - */ - keyType: KeyType - - /** - * The credential type that will be requested from the issuer. This is - * based on the credential types that are included the credential offer. - * - * If the offered credential is an inline credential offer, the value - * will be `undefined`. - */ - // TODO: do we need credentialType here? - supportedCredentialId?: string - - /** - * Whether the issuer supports the `did` cryptographic binding method, - * indicating they support all did methods. In most cases, they do not - * support all did methods, and it means we have to make an assumption - * about the did methods they support. - * - * If this value is `false`, the `supportedDidMethods` property will - * contain a list of supported did methods. - */ - supportsAllDidMethods: boolean - - /** - * A list of supported did methods. This is only used if the `supportsAllDidMethods` - * property is `false`. When this array is populated, the returned verification method - * MUST be based on one of these did methods. - * - * The did methods are returned in the format `did:`, e.g. `did:web`. - * - * The value is undefined in the case the supported did methods could not be extracted. - * This is the case when an inline credential was used, or when the issuer didn't include - * the supported did methods in the issuer metadata. - * - * NOTE: an empty array (no did methods supported) has a different meaning from the value - * being undefined (the supported did methods could not be extracted). If `supportsAllDidMethods` - * is true, the value of this property MUST be ignored. - */ - supportedDidMethods?: string[] -} - -/** - * The proof of possession verification method resolver is a function that can be passed by the - * user of the framework and allows them to determine which verification method should be used - * for the proof of possession signature. - */ -export type ProofOfPossessionVerificationMethodResolver = ( - options: ProofOfPossessionVerificationMethodResolverOptions -) => Promise | VerificationMethod - -/** - * @internal - */ -export interface ProofOfPossessionRequirements { - signatureAlgorithm: JwaSignatureAlgorithm - supportedDidMethods?: string[] - supportsAllDidMethods: boolean -} - -/** - * @internal - */ -export enum AuthFlowType { - AuthorizationCodeFlow, - PreAuthorizedCodeFlow, -} - -type WithFlowType = Options & { flowType: FlowType } - -/** - * The options that are used to request a credential from an issuer. - * @internal - */ -export type RequestCredentialOptions = - | WithFlowType - | WithFlowType diff --git a/packages/openid4vc-client/src/index.ts b/packages/openid4vc-client/src/index.ts deleted file mode 100644 index f6e1e75c5d..0000000000 --- a/packages/openid4vc-client/src/index.ts +++ /dev/null @@ -1,22 +0,0 @@ -import 'fast-text-encoding' - -export * from './OpenId4VcClientApi' -export * from './OpenId4VcClientModule' -export * from './OpenId4VcClientService' -// Contains internal types, so we don't export everything -export { - AuthCodeFlowOptions, - GenerateAuthorizationUrlOptions, - PreAuthCodeFlowOptions, - ProofOfPossessionVerificationMethodResolver, - ProofOfPossessionVerificationMethodResolverOptions, - RequestCredentialOptions, - SupportedCredentialFormats, -} from './OpenId4VcClientServiceOptions' -export * from './presentations' -export { - getOpenId4VcCredentialMetadata, - OpenId4VcCredentialMetadata, - OpenIdCredentialFormatProfile, - setOpenId4VcCredentialMetadata, -} from './utils' diff --git a/packages/openid4vc-client/src/utils/Formats.ts b/packages/openid4vc-client/src/utils/Formats.ts deleted file mode 100644 index 7d13fa6ef9..0000000000 --- a/packages/openid4vc-client/src/utils/Formats.ts +++ /dev/null @@ -1,41 +0,0 @@ -import type { OID4VCICredentialFormat } from '@sphereon/oid4vci-common' -import type { CredentialFormat } from '@sphereon/ssi-types' - -import { OpenId4VCIVersion } from '@sphereon/oid4vci-common' - -// Base on https://github.com/Sphereon-Opensource/OID4VCI/pull/54/files - -const isUniformFormat = (format: string): format is OID4VCICredentialFormat => { - return ['jwt_vc_json', 'jwt_vc_json-ld', 'ldp_vc'].includes(format) -} - -export function getUniformFormat(format: string | OID4VCICredentialFormat | CredentialFormat): OID4VCICredentialFormat { - // Already valid format - if (isUniformFormat(format)) { - return format - } - - // Older formats - if (format === 'jwt_vc' || format === 'jwt') { - return 'jwt_vc_json' - } - if (format === 'ldp_vc' || format === 'ldp') { - return 'ldp_vc' - } - - throw new Error(`Invalid format: ${format}`) -} - -export function getFormatForVersion(format: string, version: OpenId4VCIVersion) { - const uniformFormat = isUniformFormat(format) ? format : getUniformFormat(format) - - if (version < OpenId4VCIVersion.VER_1_0_11) { - if (uniformFormat === 'jwt_vc_json') { - return 'jwt_vc' as const - } else if (uniformFormat === 'ldp_vc' || uniformFormat === 'jwt_vc_json-ld') { - return 'ldp_vc' as const - } - } - - return uniformFormat -} diff --git a/packages/openid4vc-client/src/utils/IssuerMetadataUtils.ts b/packages/openid4vc-client/src/utils/IssuerMetadataUtils.ts deleted file mode 100644 index 827cfdaa5e..0000000000 --- a/packages/openid4vc-client/src/utils/IssuerMetadataUtils.ts +++ /dev/null @@ -1,113 +0,0 @@ -import type { - CredentialIssuerMetadata, - CredentialSupported, - CredentialSupportedTypeV1_0_08, - CredentialSupportedV1_0_08, - IssuerMetadataV1_0_08, - MetadataDisplay, -} from '@sphereon/oid4vci-common' - -import { OpenId4VCIVersion } from '@sphereon/oid4vci-common' - -export function getSupportedCredentials(opts?: { - issuerMetadata?: CredentialIssuerMetadata | IssuerMetadataV1_0_08 - version: OpenId4VCIVersion - credentialSupportedIds?: string[] -}): CredentialSupported[] { - const { issuerMetadata } = opts ?? {} - let credentialsSupported: CredentialSupported[] - if (!issuerMetadata) { - return [] - } - const { version, credentialSupportedIds } = opts ?? { version: OpenId4VCIVersion.VER_1_0_11 } - - const usesTransformedCredentialsSupported = - version === OpenId4VCIVersion.VER_1_0_08 || !Array.isArray(issuerMetadata.credentials_supported) - if (usesTransformedCredentialsSupported) { - credentialsSupported = credentialsSupportedV8ToV11((issuerMetadata as IssuerMetadataV1_0_08).credentials_supported) - } else { - credentialsSupported = (issuerMetadata as CredentialIssuerMetadata).credentials_supported - } - - if (credentialsSupported === undefined || credentialsSupported.length === 0) { - return [] - } else if (!credentialSupportedIds || credentialSupportedIds.length === 0) { - return credentialsSupported - } - - const credentialSupportedOverlap: CredentialSupported[] = [] - for (const credentialSupportedId of credentialSupportedIds) { - if (typeof credentialSupportedId === 'string') { - const supported = credentialsSupported.find((sup) => { - // Match id to offerType - if (sup.id === credentialSupportedId) return true - - // If the credential was transformed and the v8 variant supported multiple formats for the id, we - // check if there is an id with the format - // see credentialsSupportedV8ToV11 - if (usesTransformedCredentialsSupported && sup.id === `${credentialSupportedId}-${sup.format}`) return true - - return false - }) - if (supported) { - credentialSupportedOverlap.push(supported) - } - } - } - - return credentialSupportedOverlap -} - -export function credentialsSupportedV8ToV11(supportedV8: CredentialSupportedTypeV1_0_08): CredentialSupported[] { - return Object.entries(supportedV8).flatMap((entry) => { - const type = entry[0] - const supportedV8 = entry[1] - return credentialSupportedV8ToV11(type, supportedV8) - }) -} - -export function credentialSupportedV8ToV11( - key: string, - supportedV8: CredentialSupportedV1_0_08 -): CredentialSupported[] { - const v8FormatEntries = Object.entries(supportedV8.formats) - - return v8FormatEntries.map((entry) => { - const format = entry[0] - const credentialSupportBrief = entry[1] - if (typeof format !== 'string') { - throw Error(`Unknown format received ${JSON.stringify(format)}`) - } - let credentialSupport: Partial = {} - - // v8 format included the credential type / id as the key of the object and it could contain multiple supported formats - // v11 format has an array where each entry only supports one format, and can only have an `id` property. We include the - // key from the v8 object as the id for the v11 object, but to prevent collisions (as multiple formats can be supported under - // one key), we append the format to the key IF there's more than one format supported under the key. - const id = v8FormatEntries.length > 1 ? `${key}-${format}` : key - - credentialSupport = { - format, - display: supportedV8.display, - ...credentialSupportBrief, - credentialSubject: supportedV8.claims, - id, - } - return credentialSupport as CredentialSupported - }) -} - -export function getIssuerDisplays( - metadata: CredentialIssuerMetadata | IssuerMetadataV1_0_08, - opts?: { prefLocales: string[] } -): MetadataDisplay[] { - const matchedDisplays = - metadata.display?.filter( - (item) => - !opts?.prefLocales || - opts.prefLocales.length === 0 || - (item.locale && opts.prefLocales.includes(item.locale)) || - !item.locale - ) ?? [] - return matchedDisplays.sort((item) => (item.locale ? opts?.prefLocales.indexOf(item.locale) ?? 1 : Number.MAX_VALUE)) -} diff --git a/packages/openid4vc-client/src/utils/__tests__/claimFormatMapping.test.ts b/packages/openid4vc-client/src/utils/__tests__/claimFormatMapping.test.ts deleted file mode 100644 index a8bcdd9633..0000000000 --- a/packages/openid4vc-client/src/utils/__tests__/claimFormatMapping.test.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { AriesFrameworkError, ClaimFormat } from '@aries-framework/core' - -import { - fromDifClaimFormatToOpenIdCredentialFormatProfile, - fromOpenIdCredentialFormatProfileToDifClaimFormat, - OpenIdCredentialFormatProfile, -} from '../claimFormatMapping' - -describe('claimFormatMapping', () => { - it('should convert from openid credential format profile to DIF claim format', () => { - expect(fromDifClaimFormatToOpenIdCredentialFormatProfile(ClaimFormat.LdpVc)).toStrictEqual( - OpenIdCredentialFormatProfile.LdpVc - ) - - expect(fromDifClaimFormatToOpenIdCredentialFormatProfile(ClaimFormat.JwtVc)).toStrictEqual( - OpenIdCredentialFormatProfile.JwtVcJson - ) - - expect(() => fromDifClaimFormatToOpenIdCredentialFormatProfile(ClaimFormat.Jwt)).toThrow(AriesFrameworkError) - - expect(() => fromDifClaimFormatToOpenIdCredentialFormatProfile(ClaimFormat.Ldp)).toThrow(AriesFrameworkError) - - expect(() => fromDifClaimFormatToOpenIdCredentialFormatProfile(ClaimFormat.JwtVp)).toThrow(AriesFrameworkError) - - expect(() => fromDifClaimFormatToOpenIdCredentialFormatProfile(ClaimFormat.LdpVp)).toThrow(AriesFrameworkError) - }) - - it('should convert from DIF claim format to openid credential format profile', () => { - expect(fromOpenIdCredentialFormatProfileToDifClaimFormat(OpenIdCredentialFormatProfile.JwtVcJson)).toStrictEqual( - ClaimFormat.JwtVc - ) - - expect(fromOpenIdCredentialFormatProfileToDifClaimFormat(OpenIdCredentialFormatProfile.JwtVcJsonLd)).toStrictEqual( - ClaimFormat.JwtVc - ) - - expect(fromOpenIdCredentialFormatProfileToDifClaimFormat(OpenIdCredentialFormatProfile.LdpVc)).toStrictEqual( - ClaimFormat.LdpVc - ) - - expect(() => fromOpenIdCredentialFormatProfileToDifClaimFormat(OpenIdCredentialFormatProfile.MsoMdoc)).toThrow( - AriesFrameworkError - ) - }) -}) diff --git a/packages/openid4vc-client/src/utils/claimFormatMapping.ts b/packages/openid4vc-client/src/utils/claimFormatMapping.ts deleted file mode 100644 index 3ab952f94f..0000000000 --- a/packages/openid4vc-client/src/utils/claimFormatMapping.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { AriesFrameworkError, ClaimFormat } from '@aries-framework/core' - -export enum OpenIdCredentialFormatProfile { - JwtVcJson = 'jwt_vc_json', - JwtVcJsonLd = 'jwt_vc_json-ld', - LdpVc = 'ldp_vc', - MsoMdoc = 'mso_mdoc', -} - -export const fromDifClaimFormatToOpenIdCredentialFormatProfile = ( - claimFormat: ClaimFormat -): OpenIdCredentialFormatProfile => { - switch (claimFormat) { - case ClaimFormat.JwtVc: - return OpenIdCredentialFormatProfile.JwtVcJson - case ClaimFormat.LdpVc: - return OpenIdCredentialFormatProfile.LdpVc - default: - throw new AriesFrameworkError( - `Unsupported DIF claim format, ${claimFormat}, to map to an openid credential format profile` - ) - } -} - -export const fromOpenIdCredentialFormatProfileToDifClaimFormat = ( - openidCredentialFormatProfile: OpenIdCredentialFormatProfile -): ClaimFormat => { - switch (openidCredentialFormatProfile) { - case OpenIdCredentialFormatProfile.JwtVcJson: - return ClaimFormat.JwtVc - case OpenIdCredentialFormatProfile.JwtVcJsonLd: - return ClaimFormat.JwtVc - case OpenIdCredentialFormatProfile.LdpVc: - return ClaimFormat.LdpVc - default: - throw new AriesFrameworkError( - `Unsupported openid credential format profile, ${openidCredentialFormatProfile}, to map to a DIF claim format` - ) - } -} diff --git a/packages/openid4vc-client/src/utils/index.ts b/packages/openid4vc-client/src/utils/index.ts deleted file mode 100644 index ee55f476ed..0000000000 --- a/packages/openid4vc-client/src/utils/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './claimFormatMapping' -export * from './metadata' diff --git a/packages/openid4vc-client/src/utils/metadata.ts b/packages/openid4vc-client/src/utils/metadata.ts deleted file mode 100644 index 2ae316632b..0000000000 --- a/packages/openid4vc-client/src/utils/metadata.ts +++ /dev/null @@ -1,69 +0,0 @@ -import type { W3cCredentialRecord } from '@aries-framework/core' -import type { - CredentialIssuerMetadata, - CredentialsSupportedDisplay, - CredentialSupported, - EndpointMetadata, - IssuerCredentialSubject, - IssuerMetadataV1_0_08, - MetadataDisplay, -} from '@sphereon/oid4vci-common' - -export interface OpenId4VcCredentialMetadata { - credential: { - display?: CredentialsSupportedDisplay[] - order?: string[] - credentialSubject: IssuerCredentialSubject - } - issuer: { - display?: MetadataDisplay[] - id: string - } -} - -// what does this mean -const openId4VcCredentialMetadataKey = '_paradym/openId4VcCredentialMetadata' - -function extractOpenId4VcCredentialMetadata( - credentialMetadata: CredentialSupported, - serverMetadata: EndpointMetadata, - serverMetadataResult: CredentialIssuerMetadata | IssuerMetadataV1_0_08 -) { - return { - credential: { - display: credentialMetadata.display, - order: credentialMetadata.order, - credentialSubject: credentialMetadata.credentialSubject, - }, - issuer: { - display: serverMetadataResult.credentialIssuerMetadata?.display, - id: serverMetadata.issuer, - }, - } -} - -/** - * Gets the OpenId4Vc credential metadata from the given W3C credential record. - */ -export function getOpenId4VcCredentialMetadata( - w3cCredentialRecord: W3cCredentialRecord -): OpenId4VcCredentialMetadata | null { - return w3cCredentialRecord.metadata.get(openId4VcCredentialMetadataKey) -} - -/** - * Sets the OpenId4Vc credential metadata on the given W3C credential record. - * - * NOTE: this does not save the record. - */ -export function setOpenId4VcCredentialMetadata( - w3cCredentialRecord: W3cCredentialRecord, - credentialMetadata: CredentialSupported, - serverMetadata: EndpointMetadata, - serverMetadataResult: CredentialIssuerMetadata | IssuerMetadataV1_0_08 -) { - w3cCredentialRecord.metadata.set( - openId4VcCredentialMetadataKey, - extractOpenId4VcCredentialMetadata(credentialMetadata, serverMetadata, serverMetadataResult) - ) -} diff --git a/packages/openid4vc-client/tests/OpenId4VcClientModule.test.ts b/packages/openid4vc-client/tests/OpenId4VcClientModule.test.ts deleted file mode 100644 index b3383d8d6c..0000000000 --- a/packages/openid4vc-client/tests/OpenId4VcClientModule.test.ts +++ /dev/null @@ -1,29 +0,0 @@ -/* eslint-disable @typescript-eslint/unbound-method */ -import type { DependencyManager } from '@aries-framework/core' - -import { OpenId4VcClientApi } from '../src/OpenId4VcClientApi' -import { OpenId4VcClientModule } from '../src/OpenId4VcClientModule' -import { OpenId4VcClientService } from '../src/OpenId4VcClientService' -import { OpenId4VpClientService, PresentationExchangeService } from '../src/presentations' - -const dependencyManager = { - registerInstance: jest.fn(), - registerSingleton: jest.fn(), - registerContextScoped: jest.fn(), - resolve: jest.fn().mockReturnValue({ logger: { warn: jest.fn() } }), -} as unknown as DependencyManager - -describe('OpenId4VcClientModule', () => { - test('registers dependencies on the dependency manager', () => { - const openId4VcClientModule = new OpenId4VcClientModule() - openId4VcClientModule.register(dependencyManager) - - expect(dependencyManager.registerContextScoped).toHaveBeenCalledTimes(1) - expect(dependencyManager.registerContextScoped).toHaveBeenCalledWith(OpenId4VcClientApi) - - expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(3) - expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(OpenId4VcClientService) - expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(OpenId4VpClientService) - expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(PresentationExchangeService) - }) -}) diff --git a/packages/openid4vc-client/tests/fixtures.ts b/packages/openid4vc-client/tests/fixtures.ts deleted file mode 100644 index 54e46bb496..0000000000 --- a/packages/openid4vc-client/tests/fixtures.ts +++ /dev/null @@ -1,460 +0,0 @@ -export const mattrLaunchpadJsonLd_draft_08 = { - credentialOffer: - 'openid-initiate-issuance://?issuer=https://launchpad.mattrlabs.com&credential_type=OpenBadgeCredential&pre-authorized_code=krBcsBIlye2T-G4-rHHnRZUCah9uzDKwohJK6ABNvL-', - getMetadataResponse: { - authorization_endpoint: 'https://launchpad.vii.electron.mattrlabs.io/oidc/v1/auth/authorize', - token_endpoint: 'https://launchpad.vii.electron.mattrlabs.io/oidc/v1/auth/token', - jwks_uri: 'https://launchpad.vii.electron.mattrlabs.io/oidc/v1/auth/jwks', - token_endpoint_auth_methods_supported: [ - 'none', - 'client_secret_basic', - 'client_secret_jwt', - 'client_secret_post', - 'private_key_jwt', - ], - code_challenge_methods_supported: ['S256'], - grant_types_supported: ['authorization_code', 'urn:ietf:params:oauth:grant-type:pre-authorized_code'], - response_modes_supported: ['form_post', 'fragment', 'query'], - response_types_supported: ['code id_token', 'code', 'id_token', 'none'], - scopes_supported: ['OpenBadgeCredential', 'AcademicAward', 'LearnerProfile', 'PermanentResidentCard'], - token_endpoint_auth_signing_alg_values_supported: ['HS256', 'RS256', 'PS256', 'ES256', 'EdDSA'], - credential_endpoint: 'https://launchpad.vii.electron.mattrlabs.io/oidc/v1/auth/credential', - credentials_supported: { - OpenBadgeCredential: { - formats: { - ldp_vc: { - name: 'JFF x vc-edu PlugFest 2', - description: "MATTR's submission for JFF Plugfest 2", - types: ['OpenBadgeCredential'], - binding_methods_supported: ['did'], - cryptographic_suites_supported: ['Ed25519Signature2018'], - }, - }, - }, - AcademicAward: { - formats: { - ldp_vc: { - name: 'Example Academic Award', - description: 'Microcredential from the MyCreds Network.', - types: ['AcademicAward'], - binding_methods_supported: ['did'], - cryptographic_suites_supported: ['Ed25519Signature2018'], - }, - }, - }, - LearnerProfile: { - formats: { - ldp_vc: { - name: 'Digitary Learner Profile', - description: 'Example', - types: ['LearnerProfile'], - binding_methods_supported: ['did'], - cryptographic_suites_supported: ['Ed25519Signature2018'], - }, - }, - }, - PermanentResidentCard: { - formats: { - ldp_vc: { - name: 'Permanent Resident Card', - description: 'Government of Kakapo', - types: ['PermanentResidentCard'], - binding_methods_supported: ['did'], - cryptographic_suites_supported: ['Ed25519Signature2018'], - }, - }, - }, - }, - }, - - acquireAccessTokenResponse: { - access_token: '7nikUotMQefxn7oRX56R7MDNE7KJTGfwGjOkHzGaUIG', - expires_in: 3600, - scope: 'OpenBadgeCredential', - token_type: 'Bearer', - }, - credentialResponse: { - format: 'ldp_vc', - credential: { - type: ['VerifiableCredential', 'VerifiableCredentialExtension', 'OpenBadgeCredential'], - issuer: { - id: 'did:web:launchpad.vii.electron.mattrlabs.io', - name: 'Jobs for the Future (JFF)', - iconUrl: 'https://w3c-ccg.github.io/vc-ed/plugfest-1-2022/images/JFF_LogoLockup.png', - image: 'https://w3c-ccg.github.io/vc-ed/plugfest-1-2022/images/JFF_LogoLockup.png', - }, - name: 'JFF x vc-edu PlugFest 2', - description: "MATTR's submission for JFF Plugfest 2", - credentialBranding: { - backgroundColor: '#464c49', - }, - issuanceDate: '2023-01-25T16:58:06.292Z', - credentialSubject: { - id: 'did:key:z6MkpGR4gs4Rc3Zph4vj8wRnjnAxgAPSxcR8MAVKutWspQzc', - type: ['AchievementSubject'], - achievement: { - id: 'urn:uuid:bd6d9316-f7ae-4073-a1e5-2f7f5bd22922', - name: 'JFF x vc-edu PlugFest 2 Interoperability', - type: ['Achievement'], - image: { - id: 'https://w3c-ccg.github.io/vc-ed/plugfest-2-2022/images/JFF-VC-EDU-PLUGFEST2-badge-image.png', - type: 'Image', - }, - criteria: { - type: 'Criteria', - narrative: - 'Solutions providers earned this badge by demonstrating interoperability between multiple providers based on the OBv3 candidate final standard, with some additional required fields. Credential issuers earning this badge successfully issued a credential into at least two wallets. Wallet implementers earning this badge successfully displayed credentials issued by at least two different credential issuers.', - }, - description: - 'This credential solution supports the use of OBv3 and w3c Verifiable Credentials and is interoperable with at least two other solutions. This was demonstrated successfully during JFF x vc-edu PlugFest 2.', - }, - }, - '@context': [ - 'https://www.w3.org/2018/credentials/v1', - { - '@vocab': 'https://w3id.org/security/undefinedTerm#', - }, - 'https://mattr.global/contexts/vc-extensions/v1', - 'https://purl.imsglobal.org/spec/ob/v3p0/context.json', - 'https://w3c-ccg.github.io/vc-status-rl-2020/contexts/vc-revocation-list-2020/v1.jsonld', - ], - credentialStatus: { - id: 'https://launchpad.vii.electron.mattrlabs.io/core/v1/revocation-lists/b4aa46a0-5539-4a6b-aa03-8f6791c22ce3#49', - type: 'RevocationList2020Status', - revocationListIndex: '49', - revocationListCredential: - 'https://launchpad.vii.electron.mattrlabs.io/core/v1/revocation-lists/b4aa46a0-5539-4a6b-aa03-8f6791c22ce3', - }, - proof: { - type: 'Ed25519Signature2018', - created: '2023-01-25T16:58:07Z', - jws: 'eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..PrpRKt60yXOzMNiQY5bELX40F6Svwm-FyQ-Jv02VJDfTTH8GPPByjtOb_n3YfWidQVgySfGQ_H7VmCGjvsU6Aw', - proofPurpose: 'assertionMethod', - verificationMethod: 'did:web:launchpad.vii.electron.mattrlabs.io#6BhFMCGTJg', - }, - }, - }, -} - -export const waltIdJffJwt_draft_08 = { - credentialOffer: - 'openid-initiate-issuance://?issuer=https%3A%2F%2Fjff.walt.id%2Fissuer-api%2Fdefault%2Foidc%2F&credential_type=VerifiableId&pre-authorized_code=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiI4YmI0NWZiNC0zNDc1LTQ5YzItODVjNy0wYjkxZjY4N2RhNDQiLCJwcmUtYXV0aG9yaXplZCI6dHJ1ZX0.R8nHseZJvU3uVL3Ox-97i1HUnvjZH6wKSWDO_i8D12I&user_pin_required=false', - getMetadataResponse: { - authorization_endpoint: 'https://jff.walt.id/issuer-api/default/oidc/fulfillPAR', - token_endpoint: 'https://jff.walt.id/issuer-api/default/oidc/token', - pushed_authorization_request_endpoint: 'https://jff.walt.id/issuer-api/default/oidc/par', - issuer: 'https://jff.walt.id/issuer-api/default', - jwks_uri: 'https://jff.walt.id/issuer-api/default/oidc', - grant_types_supported: ['authorization_code', 'urn:ietf:params:oauth:grant-type:pre-authorized_code'], - request_uri_parameter_supported: true, - credentials_supported: { - VerifiableId: { - display: [{ name: 'VerifiableId' }], - formats: { - ldp_vc: { - cryptographic_binding_methods_supported: ['did'], - cryptographic_suites_supported: [ - 'Ed25519Signature2018', - 'Ed25519Signature2020', - 'EcdsaSecp256k1Signature2019', - 'RsaSignature2018', - 'JsonWebSignature2020', - 'JcsEd25519Signature2020', - ], - types: ['VerifiableCredential', 'VerifiableAttestation', 'VerifiableId'], - }, - jwt_vc: { - cryptographic_binding_methods_supported: ['did'], - cryptographic_suites_supported: ['ES256', 'ES256K', 'EdDSA', 'RS256', 'PS256'], - types: ['VerifiableCredential', 'VerifiableAttestation', 'VerifiableId'], - }, - }, - }, - VerifiableDiploma: { - display: [{ name: 'VerifiableDiploma' }], - formats: { - ldp_vc: { - cryptographic_binding_methods_supported: ['did'], - cryptographic_suites_supported: [ - 'Ed25519Signature2018', - 'Ed25519Signature2020', - 'EcdsaSecp256k1Signature2019', - 'RsaSignature2018', - 'JsonWebSignature2020', - 'JcsEd25519Signature2020', - ], - types: ['VerifiableCredential', 'VerifiableAttestation', 'VerifiableDiploma'], - }, - jwt_vc: { - cryptographic_binding_methods_supported: ['did'], - cryptographic_suites_supported: ['ES256', 'ES256K', 'EdDSA', 'RS256', 'PS256'], - types: ['VerifiableCredential', 'VerifiableAttestation', 'VerifiableDiploma'], - }, - }, - }, - VerifiableVaccinationCertificate: { - display: [{ name: 'VerifiableVaccinationCertificate' }], - formats: { - ldp_vc: { - cryptographic_binding_methods_supported: ['did'], - cryptographic_suites_supported: [ - 'Ed25519Signature2018', - 'Ed25519Signature2020', - 'EcdsaSecp256k1Signature2019', - 'RsaSignature2018', - 'JsonWebSignature2020', - 'JcsEd25519Signature2020', - ], - types: ['VerifiableCredential', 'VerifiableAttestation', 'VerifiableVaccinationCertificate'], - }, - jwt_vc: { - cryptographic_binding_methods_supported: ['did'], - cryptographic_suites_supported: ['ES256', 'ES256K', 'EdDSA', 'RS256', 'PS256'], - types: ['VerifiableCredential', 'VerifiableAttestation', 'VerifiableVaccinationCertificate'], - }, - }, - }, - ProofOfResidence: { - display: [{ name: 'ProofOfResidence' }], - formats: { - ldp_vc: { - cryptographic_binding_methods_supported: ['did'], - cryptographic_suites_supported: [ - 'Ed25519Signature2018', - 'Ed25519Signature2020', - 'EcdsaSecp256k1Signature2019', - 'RsaSignature2018', - 'JsonWebSignature2020', - 'JcsEd25519Signature2020', - ], - types: ['VerifiableCredential', 'VerifiableAttestation', 'ProofOfResidence'], - }, - jwt_vc: { - cryptographic_binding_methods_supported: ['did'], - cryptographic_suites_supported: ['ES256', 'ES256K', 'EdDSA', 'RS256', 'PS256'], - types: ['VerifiableCredential', 'VerifiableAttestation', 'ProofOfResidence'], - }, - }, - }, - ParticipantCredential: { - display: [{ name: 'ParticipantCredential' }], - formats: { - ldp_vc: { - cryptographic_binding_methods_supported: ['did'], - cryptographic_suites_supported: [ - 'Ed25519Signature2018', - 'Ed25519Signature2020', - 'EcdsaSecp256k1Signature2019', - 'RsaSignature2018', - 'JsonWebSignature2020', - 'JcsEd25519Signature2020', - ], - types: ['VerifiableCredential', 'ParticipantCredential'], - }, - jwt_vc: { - cryptographic_binding_methods_supported: ['did'], - cryptographic_suites_supported: ['ES256', 'ES256K', 'EdDSA', 'RS256', 'PS256'], - types: ['VerifiableCredential', 'ParticipantCredential'], - }, - }, - }, - Europass: { - display: [{ name: 'Europass' }], - formats: { - ldp_vc: { - cryptographic_binding_methods_supported: ['did'], - cryptographic_suites_supported: [ - 'Ed25519Signature2018', - 'Ed25519Signature2020', - 'EcdsaSecp256k1Signature2019', - 'RsaSignature2018', - 'JsonWebSignature2020', - 'JcsEd25519Signature2020', - ], - types: ['VerifiableCredential', 'VerifiableAttestation', 'Europass'], - }, - jwt_vc: { - cryptographic_binding_methods_supported: ['did'], - cryptographic_suites_supported: ['ES256', 'ES256K', 'EdDSA', 'RS256', 'PS256'], - types: ['VerifiableCredential', 'VerifiableAttestation', 'Europass'], - }, - }, - }, - OpenBadgeCredential: { - display: [{ name: 'OpenBadgeCredential' }], - formats: { - ldp_vc: { - cryptographic_binding_methods_supported: ['did'], - cryptographic_suites_supported: [ - 'Ed25519Signature2018', - 'Ed25519Signature2020', - 'EcdsaSecp256k1Signature2019', - 'RsaSignature2018', - 'JsonWebSignature2020', - 'JcsEd25519Signature2020', - ], - types: ['VerifiableCredential', 'OpenBadgeCredential'], - }, - jwt_vc: { - cryptographic_binding_methods_supported: ['did'], - cryptographic_suites_supported: ['ES256', 'ES256K', 'EdDSA', 'RS256', 'PS256'], - types: ['VerifiableCredential', 'OpenBadgeCredential'], - }, - }, - }, - }, - credential_issuer: { - display: [{ locale: null, name: 'https://jff.walt.id/issuer-api/default' }], - }, - credential_endpoint: 'https://jff.walt.id/issuer-api/default/oidc/credential', - subject_types_supported: ['public'], - }, - - acquireAccessTokenResponse: { - access_token: '8bb45fb4-3475-49c2-85c7-0b91f687da44', - refresh_token: 'WEjORX8NZccRGtRN4yvXFdYE8MeAOaLLmmGlcRbutq4', - c_nonce: 'cbad6376-f882-44c5-ae88-19bccc0de124', - id_token: - 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiI4YmI0NWZiNC0zNDc1LTQ5YzItODVjNy0wYjkxZjY4N2RhNDQifQ.Mca0Ln1AvNlxBJftYc1PZKQBlGdBmrHsFRQSBDoCgD0', - token_type: 'Bearer', - expires_in: 300, - }, - - credentialResponse: { - credential: - 'eyJraWQiOiJkaWQ6andrOmV5SnJkSGtpT2lKUFMxQWlMQ0oxYzJVaU9pSnphV2NpTENKamNuWWlPaUpGWkRJMU5URTVJaXdpYTJsa0lqb2lOMlEyWTJKbU1qUTRPV0l6TkRJM05tSXhOekl4T1RBMU5EbGtNak01TVRnaUxDSjRJam9pUm01RlZWVmhkV1J0T1RsT016QmlPREJxY3poV2REUkJiazk0ZGxKM1dIUm5VbU5MY1ROblFrbDFPQ0lzSW1Gc1p5STZJa1ZrUkZOQkluMCMwIiwidHlwIjoiSldUIiwiYWxnIjoiRWREU0EifQ.eyJpc3MiOiJkaWQ6andrOmV5SnJkSGtpT2lKUFMxQWlMQ0oxYzJVaU9pSnphV2NpTENKamNuWWlPaUpGWkRJMU5URTVJaXdpYTJsa0lqb2lOMlEyWTJKbU1qUTRPV0l6TkRJM05tSXhOekl4T1RBMU5EbGtNak01TVRnaUxDSjRJam9pUm01RlZWVmhkV1J0T1RsT016QmlPREJxY3poV2REUkJiazk0ZGxKM1dIUm5VbU5MY1ROblFrbDFPQ0lzSW1Gc1p5STZJa1ZrUkZOQkluMCIsInN1YiI6ImRpZDprZXk6ekRuYWVpcFdnOURNWFB0OWpjbUFCcWFZUlZLYzE5dFgxeGZCUldGc0pTUG9VZE1udiIsIm5iZiI6MTY4NTM1MDc4OSwiaWF0IjoxNjg1MzUwNzg5LCJ2YyI6eyJ0eXBlIjpbIlZlcmlmaWFibGVDcmVkZW50aWFsIiwiVmVyaWZpYWJsZUF0dGVzdGF0aW9uIiwiVmVyaWZpYWJsZUlkIl0sIkBjb250ZXh0IjpbImh0dHBzOi8vd3d3LnczLm9yZy8yMDE4L2NyZWRlbnRpYWxzL3YxIl0sImlkIjoidXJuOnV1aWQ6NTljZTRhYzItZWM2NS00YjhmLThmOTYtZWE3ODUxMmRmOWQzIiwiaXNzdWVyIjoiZGlkOmp3azpleUpyZEhraU9pSlBTMUFpTENKMWMyVWlPaUp6YVdjaUxDSmpjbllpT2lKRlpESTFOVEU1SWl3aWEybGtJam9pTjJRMlkySm1NalE0T1dJek5ESTNObUl4TnpJeE9UQTFORGxrTWpNNU1UZ2lMQ0o0SWpvaVJtNUZWVlZoZFdSdE9UbE9NekJpT0RCcWN6aFdkRFJCYms5NGRsSjNXSFJuVW1OTGNUTm5Ra2wxT0NJc0ltRnNaeUk2SWtWa1JGTkJJbjAiLCJpc3N1YW5jZURhdGUiOiIyMDIzLTA1LTI5VDA4OjU5OjQ5WiIsImlzc3VlZCI6IjIwMjMtMDUtMjlUMDg6NTk6NDlaIiwidmFsaWRGcm9tIjoiMjAyMy0wNS0yOVQwODo1OTo0OVoiLCJjcmVkZW50aWFsU2NoZW1hIjp7ImlkIjoiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3dhbHQtaWQvd2FsdGlkLXNzaWtpdC12Y2xpYi9tYXN0ZXIvc3JjL3Rlc3QvcmVzb3VyY2VzL3NjaGVtYXMvVmVyaWZpYWJsZUlkLmpzb24iLCJ0eXBlIjoiRnVsbEpzb25TY2hlbWFWYWxpZGF0b3IyMDIxIn0sImNyZWRlbnRpYWxTdWJqZWN0Ijp7ImlkIjoiZGlkOmtleTp6RG5hZWlwV2c5RE1YUHQ5amNtQUJxYVlSVktjMTl0WDF4ZkJSV0ZzSlNQb1VkTW52IiwiY3VycmVudEFkZHJlc3MiOlsiMSBCb3VsZXZhcmQgZGUgbGEgTGliZXJ0w6ksIDU5ODAwIExpbGxlIl0sImRhdGVPZkJpcnRoIjoiMTk5My0wNC0wOCIsImZhbWlseU5hbWUiOiJET0UiLCJmaXJzdE5hbWUiOiJKYW5lIiwiZ2VuZGVyIjoiRkVNQUxFIiwibmFtZUFuZEZhbWlseU5hbWVBdEJpcnRoIjoiSmFuZSBET0UiLCJwZXJzb25hbElkZW50aWZpZXIiOiIwOTA0MDA4MDg0SCIsInBsYWNlT2ZCaXJ0aCI6IkxJTExFLCBGUkFOQ0UifSwiZXZpZGVuY2UiOlt7ImRvY3VtZW50UHJlc2VuY2UiOlsiUGh5c2ljYWwiXSwiZXZpZGVuY2VEb2N1bWVudCI6WyJQYXNzcG9ydCJdLCJzdWJqZWN0UHJlc2VuY2UiOiJQaHlzaWNhbCIsInR5cGUiOlsiRG9jdW1lbnRWZXJpZmljYXRpb24iXSwidmVyaWZpZXIiOiJkaWQ6ZWJzaToyQTlCWjlTVWU2QmF0YWNTcHZzMVY1Q2RqSHZMcFE3YkVzaTJKYjZMZEhLblF4YU4ifV19LCJqdGkiOiJ1cm46dXVpZDo1OWNlNGFjMi1lYzY1LTRiOGYtOGY5Ni1lYTc4NTEyZGY5ZDMifQ.6Wn8X2tEQJ9CmX3-meCxDuGmevRdtivnjVkGPXzfnJ-1M6AU4SFxxon0JmMjdmO_h4P9sCEe9RTtyTJou2yeCA', - format: 'jwt_vc', - }, -} - -// This object is MANUALLY converted and should be updated when we have actual test vectors -export const waltIdJffJwt_draft_11 = { - credentialOffer: - 'openid-credential-offer://?credential_offer=%7B%22credential_issuer%22%3A%22https%3A%2F%2Fjff.walt.id%2Fissuer-api%2Fdefault%2Foidc%22%2C%22credentials%22%3A%5B%22VerifiableId%22%5D%2C%22grants%22%3A%7B%22urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Apre-authorized_code%22%3A%7B%22pre-authorized_code%22%3A%22ABC%22%7D%7D%7D', - getMetadataResponse: { - authorization_endpoint: 'https://jff.walt.id/issuer-api/default/oidc/fulfillPAR', - token_endpoint: 'https://jff.walt.id/issuer-api/default/oidc/token', - pushed_authorization_request_endpoint: 'https://jff.walt.id/issuer-api/default/oidc/par', - credential_issuer: 'https://jff.walt.id/issuer-api/default', - jwks_uri: 'https://jff.walt.id/issuer-api/default/oidc', - credential_endpoint: 'https://jff.walt.id/issuer-api/default/oidc/credential', - subject_types_supported: ['public'], - grant_types_supported: ['authorization_code', 'urn:ietf:params:oauth:grant-type:pre-authorized_code'], - request_uri_parameter_supported: true, - credentials_supported: [ - { - id: 'VerifiableId', - format: 'jwt_vc_json', - cryptographic_binding_methods_supported: ['did'], - cryptographic_suites_supported: ['ES256', 'ES256K', 'EdDSA', 'RS256', 'PS256'], - types: ['VerifiableCredential', 'OpenBadgeCredential'], - }, - { - id: 'VerifiableDiploma', - display: [{ name: 'VerifiableDiploma' }], - format: 'ldp_vc', - cryptographic_binding_methods_supported: ['did'], - cryptographic_suites_supported: [ - 'Ed25519Signature2018', - 'Ed25519Signature2020', - 'EcdsaSecp256k1Signature2019', - 'RsaSignature2018', - 'JsonWebSignature2020', - 'JcsEd25519Signature2020', - ], - types: ['VerifiableCredential', 'VerifiableAttestation', 'VerifiableDiploma'], - }, - { - id: 'VerifiableVaccinationCertificate', - display: [{ name: 'VerifiableVaccinationCertificate' }], - format: 'ldp_vc', - cryptographic_binding_methods_supported: ['did'], - cryptographic_suites_supported: [ - 'Ed25519Signature2018', - 'Ed25519Signature2020', - 'EcdsaSecp256k1Signature2019', - 'RsaSignature2018', - 'JsonWebSignature2020', - 'JcsEd25519Signature2020', - ], - types: ['VerifiableCredential', 'VerifiableAttestation', 'VerifiableVaccinationCertificate'], - }, - { - id: 'ProofOfResidence', - display: [{ name: 'ProofOfResidence' }], - format: 'ldp_vc', - cryptographic_binding_methods_supported: ['did'], - cryptographic_suites_supported: [ - 'Ed25519Signature2018', - 'Ed25519Signature2020', - 'EcdsaSecp256k1Signature2019', - 'RsaSignature2018', - 'JsonWebSignature2020', - 'JcsEd25519Signature2020', - ], - types: ['VerifiableCredential', 'VerifiableAttestation', 'ProofOfResidence'], - }, - { - id: 'ParticipantCredential', - format: 'ldp_vc', - display: [{ name: 'ParticipantCredential' }], - cryptographic_binding_methods_supported: ['did'], - cryptographic_suites_supported: [ - 'Ed25519Signature2018', - 'Ed25519Signature2020', - 'EcdsaSecp256k1Signature2019', - 'RsaSignature2018', - 'JsonWebSignature2020', - 'JcsEd25519Signature2020', - ], - types: ['VerifiableCredential', 'ParticipantCredential'], - }, - { - id: 'Europass', - display: [{ name: 'Europass' }], - format: 'ldp_vc', - cryptographic_binding_methods_supported: ['did'], - cryptographic_suites_supported: [ - 'Ed25519Signature2018', - 'Ed25519Signature2020', - 'EcdsaSecp256k1Signature2019', - 'RsaSignature2018', - 'JsonWebSignature2020', - 'JcsEd25519Signature2020', - ], - types: ['VerifiableCredential', 'VerifiableAttestation', 'Europass'], - }, - { - id: 'OpenBadgeCredential', - display: [{ name: 'OpenBadgeCredential' }], - format: 'ldp_vc', - cryptographic_binding_methods_supported: ['did'], - cryptographic_suites_supported: [ - 'Ed25519Signature2018', - 'Ed25519Signature2020', - 'EcdsaSecp256k1Signature2019', - 'RsaSignature2018', - 'JsonWebSignature2020', - 'JcsEd25519Signature2020', - ], - types: ['VerifiableCredential', 'OpenBadgeCredential'], - }, - ], - }, - - acquireAccessTokenResponse: { - access_token: '8bb45fb4-3475-49c2-85c7-0b91f687da44', - refresh_token: 'WEjORX8NZccRGtRN4yvXFdYE8MeAOaLLmmGlcRbutq4', - c_nonce: 'cbad6376-f882-44c5-ae88-19bccc0de124', - id_token: - 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiI4YmI0NWZiNC0zNDc1LTQ5YzItODVjNy0wYjkxZjY4N2RhNDQifQ.Mca0Ln1AvNlxBJftYc1PZKQBlGdBmrHsFRQSBDoCgD0', - token_type: 'Bearer', - expires_in: 300, - }, - - credentialResponse: { - credential: - 'eyJraWQiOiJkaWQ6andrOmV5SnJkSGtpT2lKUFMxQWlMQ0oxYzJVaU9pSnphV2NpTENKamNuWWlPaUpGWkRJMU5URTVJaXdpYTJsa0lqb2lOMlEyWTJKbU1qUTRPV0l6TkRJM05tSXhOekl4T1RBMU5EbGtNak01TVRnaUxDSjRJam9pUm01RlZWVmhkV1J0T1RsT016QmlPREJxY3poV2REUkJiazk0ZGxKM1dIUm5VbU5MY1ROblFrbDFPQ0lzSW1Gc1p5STZJa1ZrUkZOQkluMCMwIiwidHlwIjoiSldUIiwiYWxnIjoiRWREU0EifQ.eyJpc3MiOiJkaWQ6andrOmV5SnJkSGtpT2lKUFMxQWlMQ0oxYzJVaU9pSnphV2NpTENKamNuWWlPaUpGWkRJMU5URTVJaXdpYTJsa0lqb2lOMlEyWTJKbU1qUTRPV0l6TkRJM05tSXhOekl4T1RBMU5EbGtNak01TVRnaUxDSjRJam9pUm01RlZWVmhkV1J0T1RsT016QmlPREJxY3poV2REUkJiazk0ZGxKM1dIUm5VbU5MY1ROblFrbDFPQ0lzSW1Gc1p5STZJa1ZrUkZOQkluMCIsInN1YiI6ImRpZDprZXk6ekRuYWVpcFdnOURNWFB0OWpjbUFCcWFZUlZLYzE5dFgxeGZCUldGc0pTUG9VZE1udiIsIm5iZiI6MTY4NTM1MDc4OSwiaWF0IjoxNjg1MzUwNzg5LCJ2YyI6eyJ0eXBlIjpbIlZlcmlmaWFibGVDcmVkZW50aWFsIiwiVmVyaWZpYWJsZUF0dGVzdGF0aW9uIiwiVmVyaWZpYWJsZUlkIl0sIkBjb250ZXh0IjpbImh0dHBzOi8vd3d3LnczLm9yZy8yMDE4L2NyZWRlbnRpYWxzL3YxIl0sImlkIjoidXJuOnV1aWQ6NTljZTRhYzItZWM2NS00YjhmLThmOTYtZWE3ODUxMmRmOWQzIiwiaXNzdWVyIjoiZGlkOmp3azpleUpyZEhraU9pSlBTMUFpTENKMWMyVWlPaUp6YVdjaUxDSmpjbllpT2lKRlpESTFOVEU1SWl3aWEybGtJam9pTjJRMlkySm1NalE0T1dJek5ESTNObUl4TnpJeE9UQTFORGxrTWpNNU1UZ2lMQ0o0SWpvaVJtNUZWVlZoZFdSdE9UbE9NekJpT0RCcWN6aFdkRFJCYms5NGRsSjNXSFJuVW1OTGNUTm5Ra2wxT0NJc0ltRnNaeUk2SWtWa1JGTkJJbjAiLCJpc3N1YW5jZURhdGUiOiIyMDIzLTA1LTI5VDA4OjU5OjQ5WiIsImlzc3VlZCI6IjIwMjMtMDUtMjlUMDg6NTk6NDlaIiwidmFsaWRGcm9tIjoiMjAyMy0wNS0yOVQwODo1OTo0OVoiLCJjcmVkZW50aWFsU2NoZW1hIjp7ImlkIjoiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3dhbHQtaWQvd2FsdGlkLXNzaWtpdC12Y2xpYi9tYXN0ZXIvc3JjL3Rlc3QvcmVzb3VyY2VzL3NjaGVtYXMvVmVyaWZpYWJsZUlkLmpzb24iLCJ0eXBlIjoiRnVsbEpzb25TY2hlbWFWYWxpZGF0b3IyMDIxIn0sImNyZWRlbnRpYWxTdWJqZWN0Ijp7ImlkIjoiZGlkOmtleTp6RG5hZWlwV2c5RE1YUHQ5amNtQUJxYVlSVktjMTl0WDF4ZkJSV0ZzSlNQb1VkTW52IiwiY3VycmVudEFkZHJlc3MiOlsiMSBCb3VsZXZhcmQgZGUgbGEgTGliZXJ0w6ksIDU5ODAwIExpbGxlIl0sImRhdGVPZkJpcnRoIjoiMTk5My0wNC0wOCIsImZhbWlseU5hbWUiOiJET0UiLCJmaXJzdE5hbWUiOiJKYW5lIiwiZ2VuZGVyIjoiRkVNQUxFIiwibmFtZUFuZEZhbWlseU5hbWVBdEJpcnRoIjoiSmFuZSBET0UiLCJwZXJzb25hbElkZW50aWZpZXIiOiIwOTA0MDA4MDg0SCIsInBsYWNlT2ZCaXJ0aCI6IkxJTExFLCBGUkFOQ0UifSwiZXZpZGVuY2UiOlt7ImRvY3VtZW50UHJlc2VuY2UiOlsiUGh5c2ljYWwiXSwiZXZpZGVuY2VEb2N1bWVudCI6WyJQYXNzcG9ydCJdLCJzdWJqZWN0UHJlc2VuY2UiOiJQaHlzaWNhbCIsInR5cGUiOlsiRG9jdW1lbnRWZXJpZmljYXRpb24iXSwidmVyaWZpZXIiOiJkaWQ6ZWJzaToyQTlCWjlTVWU2QmF0YWNTcHZzMVY1Q2RqSHZMcFE3YkVzaTJKYjZMZEhLblF4YU4ifV19LCJqdGkiOiJ1cm46dXVpZDo1OWNlNGFjMi1lYzY1LTRiOGYtOGY5Ni1lYTc4NTEyZGY5ZDMifQ.6Wn8X2tEQJ9CmX3-meCxDuGmevRdtivnjVkGPXzfnJ-1M6AU4SFxxon0JmMjdmO_h4P9sCEe9RTtyTJou2yeCA', - format: 'jwt_vc', - }, -} diff --git a/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts b/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts deleted file mode 100644 index 55fe329317..0000000000 --- a/packages/openid4vc-client/tests/openid4vc-client.e2e.test.ts +++ /dev/null @@ -1,483 +0,0 @@ -import type { KeyDidCreateOptions } from '@aries-framework/core' - -import { AskarModule } from '@aries-framework/askar' -import { - JwaSignatureAlgorithm, - Agent, - KeyType, - TypedArrayEncoder, - W3cCredentialRecord, - DidKey, -} from '@aries-framework/core' -import { agentDependencies } from '@aries-framework/node' -import { ariesAskar } from '@hyperledger/aries-askar-nodejs' -import nock, { cleanAll, enableNetConnect } from 'nock' - -import { OpenId4VcClientModule } from '../src' -import { OpenIdCredentialFormatProfile } from '../src/utils/claimFormatMapping' - -import { - mattrLaunchpadJsonLd_draft_08, - // FIXME: we need a custom document loader for this, which is only present in AFJ core - // mattrLaunchpadJsonLd_draft_08, - waltIdJffJwt_draft_08, - waltIdJffJwt_draft_11, -} from './fixtures' - -const modules = { - openId4VcClient: new OpenId4VcClientModule(), - askar: new AskarModule({ - ariesAskar, - }), -} - -describe('OpenId4VcClient', () => { - let agent: Agent - - beforeEach(async () => { - agent = new Agent({ - config: { - label: 'OpenId4VcClient Test', - walletConfig: { - id: 'openid4vc-client-test', - key: 'openid4vc-client-test', - }, - }, - dependencies: agentDependencies, - modules, - }) - - await agent.initialize() - }) - - afterEach(async () => { - await agent.shutdown() - await agent.wallet.delete() - }) - - describe('[DRAFT 08]: Pre-authorized flow', () => { - afterEach(() => { - cleanAll() - enableNetConnect() - }) - - xit('[DRAFT 08]: Should successfully execute the pre-authorized flow using a did:key Ed25519 subject and JSON-LD credential', async () => { - const fixture = mattrLaunchpadJsonLd_draft_08 - /** - * Below we're setting up some mock HTTP responses. - * These responses are based on the openid-initiate-issuance URI above - * */ - - // setup temporary redirect mock - nock('https://launchpad.mattrlabs.com') - .get('/.well-known/openid-credential-issuer') - .reply(307, undefined, { - Location: 'https://launchpad.vii.electron.mattrlabs.io/.well-known/openid-credential-issuer', - }) - - .get('/.well-known/openid-configuration') - .reply(404) - - .get('/.well-known/oauth-authorization-server') - .reply(404) - - // setup server metadata response - nock('https://launchpad.vii.electron.mattrlabs.io') - .get('/.well-known/openid-credential-issuer') - .reply(200, fixture.getMetadataResponse) - - // setup access token response - .post('/oidc/v1/auth/token') - .reply(200, fixture.acquireAccessTokenResponse) - - // setup credential request response - .post('/oidc/v1/auth/credential') - .reply(200, fixture.credentialResponse) - - const did = await agent.dids.create({ - method: 'key', - options: { - keyType: KeyType.Ed25519, - }, - secret: { - privateKey: TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c7a0fd969598e'), - }, - }) - - const didKey = DidKey.fromDid(did.didState.did as string) - const kid = `${did.didState.did as string}#${didKey.key.fingerprint}` - const verificationMethod = did.didState.didDocument?.dereferenceKey(kid, ['authentication']) - if (!verificationMethod) throw new Error('No verification method found') - - const w3cCredentialRecords = await agent.modules.openId4VcClient.requestCredentialUsingPreAuthorizedCode({ - issuerUri: fixture.credentialOffer, - verifyCredentialStatus: false, - // We only allow EdDSa, as we've created a did with keyType ed25519. If we create - // or determine the did dynamically we could use any signature algorithm - allowedProofOfPossessionSignatureAlgorithms: [JwaSignatureAlgorithm.EdDSA], - proofOfPossessionVerificationMethodResolver: () => verificationMethod, - }) - - expect(w3cCredentialRecords).toHaveLength(1) - const w3cCredentialRecord = w3cCredentialRecords[0] as W3cCredentialRecord - expect(w3cCredentialRecord).toBeInstanceOf(W3cCredentialRecord) - - expect(w3cCredentialRecord.credential.type).toEqual([ - 'VerifiableCredential', - 'VerifiableCredentialExtension', - 'OpenBadgeCredential', - ]) - - expect(w3cCredentialRecord.credential.credentialSubjectIds[0]).toEqual(did.didState.did) - }) - - it('[DRAFT 08]: Should successfully execute the pre-authorized flow using a did:key P256 subject and JWT credential', async () => { - const fixture = waltIdJffJwt_draft_08 - - nock('https://jff.walt.id/issuer-api/default/oidc') - // metadata - .get('/.well-known/openid-credential-issuer') - .reply(200, fixture.getMetadataResponse) - .get('/.well-known/openid-configuration') - .reply(404) - .get('/.well-known/oauth-authorization-server') - .reply(404) - - // setup access token response - .post('/token') - .reply(200, fixture.credentialResponse) - - // setup credential request response - .post('/credential') - .reply(200, fixture.credentialResponse) - - const did = await agent.dids.create({ - method: 'key', - options: { - keyType: KeyType.P256, - }, - secret: { - privateKey: TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c7a0fd969598e'), - }, - }) - - const didKey = DidKey.fromDid(did.didState.did as string) - const kid = `${didKey.did}#${didKey.key.fingerprint}` - const verificationMethod = did.didState.didDocument?.dereferenceKey(kid, ['authentication']) - if (!verificationMethod) throw new Error('No verification method found') - - const w3cCredentialRecords = await agent.modules.openId4VcClient.requestCredentialUsingPreAuthorizedCode({ - issuerUri: fixture.credentialOffer, - allowedCredentialFormats: [OpenIdCredentialFormatProfile.JwtVcJson], - allowedProofOfPossessionSignatureAlgorithms: [JwaSignatureAlgorithm.ES256], - proofOfPossessionVerificationMethodResolver: () => verificationMethod, - verifyCredentialStatus: false, - }) - - expect(w3cCredentialRecords[0]).toBeInstanceOf(W3cCredentialRecord) - const w3cCredentialRecord = w3cCredentialRecords[0] as W3cCredentialRecord - - expect(w3cCredentialRecord.credential.type).toEqual([ - 'VerifiableCredential', - 'VerifiableAttestation', - 'VerifiableId', - ]) - - expect(w3cCredentialRecord.credential.credentialSubjectIds[0]).toEqual(did.didState.did) - }) - }) - - describe('[DRAFT 08]: Authorization flow', () => { - afterAll(() => { - cleanAll() - enableNetConnect() - }) - - it('[DRAFT 08]: should generate a valid authorization url', async () => { - const fixture = mattrLaunchpadJsonLd_draft_08 - - // setup temporary redirect mock - nock('https://launchpad.mattrlabs.com') - .get('/.well-known/openid-credential-issuer') - .reply(307, undefined, { - Location: 'https://launchpad.vii.electron.mattrlabs.io/.well-known/openid-credential-issuer', - }) - .get('/.well-known/openid-configuration') - .reply(404) - .get('/.well-known/oauth-authorization-server') - .reply(404) - - // setup server metadata response - nock('https://launchpad.vii.electron.mattrlabs.io') - .get('/.well-known/openid-credential-issuer') - .reply(200, fixture.getMetadataResponse) - - // setup access token response - .post('/oidc/v1/auth/token') - .reply(200, fixture.acquireAccessTokenResponse) - - // setup credential request response - .post('/oidc/v1/auth/credential') - .reply(200, fixture.credentialResponse) - - const clientId = 'test-client' - - const redirectUri = 'https://example.com/cb' - const scope = ['TestCredential'] - const initiationUri = - 'openid-initiate-issuance://?issuer=https://launchpad.mattrlabs.com&credential_type=OpenBadgeCredential' - const { authorizationUrl } = await agent.modules.openId4VcClient.generateAuthorizationUrl({ - clientId, - redirectUri, - scope, - initiationUri, - }) - - const parsedUrl = new URL(authorizationUrl) - expect(authorizationUrl.startsWith('https://launchpad.vii.electron.mattrlabs.io/oidc/v1/auth/authorize')).toBe( - true - ) - expect(parsedUrl.searchParams.get('response_type')).toBe('code') - expect(parsedUrl.searchParams.get('client_id')).toBe(clientId) - expect(parsedUrl.searchParams.get('code_challenge_method')).toBe('S256') - expect(parsedUrl.searchParams.get('redirect_uri')).toBe(redirectUri) - }) - - it('[DRAFT 08]: should throw if no scope is provided', async () => { - const fixture = mattrLaunchpadJsonLd_draft_08 - - // setup temporary redirect mock - nock('https://launchpad.mattrlabs.com').get('/.well-known/openid-credential-issuer').reply(307, undefined, { - Location: 'https://launchpad.vii.electron.mattrlabs.io/.well-known/openid-credential-issuer', - }) - - // setup server metadata response - nock('https://launchpad.vii.electron.mattrlabs.io') - .get('/.well-known/openid-credential-issuer') - .reply(200, fixture.getMetadataResponse) - .get('/.well-known/openid-configuration') - .reply(404) - .get('/.well-known/oauth-authorization-server') - .reply(404) - - // setup access token response - .post('/oidc/v1/auth/token') - .reply(200, fixture.acquireAccessTokenResponse) - - // setup credential request response - .post('/oidc/v1/auth/credential') - .reply(200, fixture.credentialResponse) - - // setup server metadata response - nock('https://launchpad.vii.electron.mattrlabs.io') - .get('/.well-known/openid-credential-issuer') - .reply(200, fixture.getMetadataResponse) - - const clientId = 'test-client' - const redirectUri = 'https://example.com/cb' - const initiationUri = - 'openid-initiate-issuance://?issuer=https://launchpad.mattrlabs.com&credential_type=OpenBadgeCredential' - await expect( - agent.modules.openId4VcClient.generateAuthorizationUrl({ - clientId, - redirectUri, - scope: [], - initiationUri, - }) - ).rejects.toThrow() - }) - - // Need custom document loader for this - xit('[DRAFT 08]: should successfully execute request a credential', async () => { - const fixture = mattrLaunchpadJsonLd_draft_08 - - // setup temporary redirect mock - nock('https://launchpad.mattrlabs.com').get('/.well-known/openid-credential-issuer').reply(307, undefined, { - Location: 'https://launchpad.vii.electron.mattrlabs.io/.well-known/openid-credential-issuer', - }) - - // setup server metadata response - nock('https://launchpad.vii.electron.mattrlabs.io') - .get('/.well-known/openid-credential-issuer') - .reply(200, fixture.getMetadataResponse) - .get('/.well-known/openid-configuration') - .reply(404) - .get('/.well-known/oauth-authorization-server') - .reply(404) - - // setup access token response - .post('/oidc/v1/auth/token') - .reply(200, fixture.acquireAccessTokenResponse) - - // setup credential request response - .post('/oidc/v1/auth/credential') - .reply(200, fixture.credentialResponse) - - const did = await agent.dids.create({ - method: 'key', - options: { - keyType: KeyType.Ed25519, - }, - secret: { - privateKey: TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c7a0fd969598e'), - }, - }) - - const didKey = DidKey.fromDid(did.didState.did as string) - const kid = `${did.didState.did as string}#${didKey.key.fingerprint}` - const verificationMethod = did.didState.didDocument?.dereferenceKey(kid, ['authentication']) - if (!verificationMethod) throw new Error('No verification method found') - - const clientId = 'test-client' - - const redirectUri = 'https://example.com/cb' - const initiationUri = - 'openid-initiate-issuance://?issuer=https://launchpad.mattrlabs.com&credential_type=OpenBadgeCredential' - - const scope = ['TestCredential'] - const { codeVerifier } = await agent.modules.openId4VcClient.generateAuthorizationUrl({ - clientId, - redirectUri, - scope, - initiationUri, - }) - const w3cCredentialRecords = await agent.modules.openId4VcClient.requestCredentialUsingAuthorizationCode({ - clientId: clientId, - authorizationCode: 'test-code', - codeVerifier: codeVerifier, - verifyCredentialStatus: false, - proofOfPossessionVerificationMethodResolver: () => verificationMethod, - allowedProofOfPossessionSignatureAlgorithms: [JwaSignatureAlgorithm.EdDSA], - issuerUri: initiationUri, // TODO - redirectUri: redirectUri, - }) - - expect(w3cCredentialRecords).toHaveLength(1) - const w3cCredentialRecord = w3cCredentialRecords[0] as W3cCredentialRecord - expect(w3cCredentialRecord).toBeInstanceOf(W3cCredentialRecord) - - expect(w3cCredentialRecord.credential.type).toEqual([ - 'VerifiableCredential', - 'VerifiableCredentialExtension', - 'OpenBadgeCredential', - ]) - - expect(w3cCredentialRecord.credential.credentialSubjectIds[0]).toEqual(did.didState.did) - }) - }) - - describe('[DRAFT 11]: Pre-authorized flow', () => { - afterEach(() => { - cleanAll() - enableNetConnect() - }) - - // it('[DRAFT 11]: Should successfully execute the pre-authorized flow using a did:key Ed25519 subject and JSON-LD credential', async () => { - // const fixture = waltIdJffJwt_draft_11 - - // nock('https://jff.walt.id/issuer-api/default/oidc') - // .get('/.well-known/openid-credential-issuer') - // .reply(200, fixture.getMetadataResponse) - - // // setup access token response - // .post('/token') - // .reply(200, fixture.credentialResponse) - - // // setup credential request response - // .post('/credential') - // .reply(200, fixture.credentialResponse) - - // const did = await agent.dids.create({ - // method: 'key', - // options: { - // keyType: KeyType.Ed25519, - // }, - // secret: { - // privateKey: TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c7a0fd969598e'), - // }, - // }) - - // const didKey = DidKey.fromDid(did.didState.did as string) - // const kid = `${did.didState.did as string}#${didKey.key.fingerprint}` - // const verificationMethod = did.didState.didDocument?.dereferenceKey(kid, ['authentication']) - // if (!verificationMethod) throw new Error('No verification method found') - - // const w3cCredentialRecords = await agent.modules.openId4VcClient.requestCredentialUsingPreAuthorizedCode({ - // uri: fixture.credentialOffer, - // verifyCredentialStatus: false, - // // We only allow EdDSa, as we've created a did with keyType ed25519. If we create - // // or determine the did dynamically we could use any signature algorithm - // allowedProofOfPossessionSignatureAlgorithms: [JwaSignatureAlgorithm.EdDSA], - // proofOfPossessionVerificationMethodResolver: () => verificationMethod, - // }) - - // expect(w3cCredentialRecords).toHaveLength(1) - // const w3cCredentialRecord = w3cCredentialRecords[0] - // expect(w3cCredentialRecord).toBeInstanceOf(W3cCredentialRecord) - - // expect(w3cCredentialRecord.credential.type).toEqual([ - // 'VerifiableCredential', - // 'VerifiableAttestation', - // 'VerifiableId', - // ]) - - // expect(w3cCredentialRecord.credential.credentialSubjectIds[0]).toEqual(did.didState.did) - // }) - - it('[DRAFT 11]: Should successfully execute the pre-authorized flow using a did:key P256 subject and JWT credential', async () => { - const fixture = waltIdJffJwt_draft_11 - - /** - * Below we're setting up some mock HTTP responses. - * These responses are based on the openid-initiate-issuance URI above - */ - // setup server metadata response - const httpMock = nock('https://jff.walt.id/issuer-api/default/oidc') - .get('/.well-known/openid-credential-issuer') - .reply(200, fixture.getMetadataResponse) - .get('/.well-known/openid-configuration') - .reply(404) - .get('/.well-known/oauth-authorization-server') - .reply(404) - - // setup access token response - httpMock.post('/token').reply(200, fixture.credentialResponse) - // setup credential request response - httpMock.post('/credential').reply(200, fixture.credentialResponse) - - const did = await agent.dids.create({ - method: 'key', - options: { - keyType: KeyType.P256, - }, - secret: { - privateKey: TypedArrayEncoder.fromString('96213c3d7fc8d4d6754c7a0fd969598e'), - }, - }) - - const didKey = DidKey.fromDid(did.didState.did as string) - const kid = `${didKey.did}#${didKey.key.fingerprint}` - const verificationMethod = did.didState.didDocument?.dereferenceKey(kid, ['authentication']) - if (!verificationMethod) throw new Error('No verification method found') - - const w3cCredentialRecords = await agent.modules.openId4VcClient.requestCredentialUsingPreAuthorizedCode({ - issuerUri: fixture.credentialOffer, - allowedCredentialFormats: [OpenIdCredentialFormatProfile.JwtVcJson], - allowedProofOfPossessionSignatureAlgorithms: [JwaSignatureAlgorithm.ES256], - proofOfPossessionVerificationMethodResolver: () => verificationMethod, - verifyCredentialStatus: false, - }) - - expect(w3cCredentialRecords[0]).toBeInstanceOf(W3cCredentialRecord) - const w3cCredentialRecord = w3cCredentialRecords[0] as W3cCredentialRecord - - expect(w3cCredentialRecord.credential.type).toEqual([ - 'VerifiableCredential', - 'VerifiableAttestation', - 'VerifiableId', - ]) - - expect(w3cCredentialRecord.credential.credentialSubjectIds[0]).toEqual(did.didState.did) - }) - }) -}) diff --git a/packages/openid4vc-holder/package.json b/packages/openid4vc-holder/package.json index 3eac40e571..2f773d6aa2 100644 --- a/packages/openid4vc-holder/package.json +++ b/packages/openid4vc-holder/package.json @@ -35,7 +35,6 @@ "@aries-framework/askar": "0.4.2", "@aries-framework/node": "0.4.2", "@hyperledger/aries-askar-nodejs": "^0.1.0", - "@types/jsonpath": "^0.2.0", "nock": "^13.3.0", "rimraf": "^4.4.0", "typescript": "~4.9.5" diff --git a/packages/openid4vc-holder/src/OpenId4VcHolderModule.ts b/packages/openid4vc-holder/src/OpenId4VcHolderModule.ts index 6468aa07e3..b45615624f 100644 --- a/packages/openid4vc-holder/src/OpenId4VcHolderModule.ts +++ b/packages/openid4vc-holder/src/OpenId4VcHolderModule.ts @@ -4,6 +4,8 @@ import { AgentConfig } from '@aries-framework/core' import { OpenId4VcHolderApi } from './OpenId4VcHolderApi' import { OpenId4VcHolderService } from './OpenId4VcHolderService' +import { PresentationExchangeService } from './presentations' +import { OpenId4VpHolderService } from './presentations/OpenId4VpHolderService' /** * @public @@ -25,7 +27,10 @@ export class OpenId4VcHolderModule implements Module { // Api dependencyManager.registerContextScoped(OpenId4VcHolderApi) + // Services // Services dependencyManager.registerSingleton(OpenId4VcHolderService) + dependencyManager.registerSingleton(OpenId4VpHolderService) + dependencyManager.registerSingleton(PresentationExchangeService) } } diff --git a/packages/openid4vc-holder/src/index.ts b/packages/openid4vc-holder/src/index.ts index c81821681c..b345b161b0 100644 --- a/packages/openid4vc-holder/src/index.ts +++ b/packages/openid4vc-holder/src/index.ts @@ -13,6 +13,7 @@ export { RequestCredentialOptions, SupportedCredentialFormats, } from './OpenId4VcHolderServiceOptions' +export * from './presentations' export { getOpenId4VcCredentialMetadata, OpenId4VcCredentialMetadata, diff --git a/packages/openid4vc-client/src/presentations/OpenId4VpClientService.ts b/packages/openid4vc-holder/src/presentations/OpenId4VpHolderService.ts similarity index 99% rename from packages/openid4vc-client/src/presentations/OpenId4VpClientService.ts rename to packages/openid4vc-holder/src/presentations/OpenId4VpHolderService.ts index 0c8d98dbcd..ef5628bb96 100644 --- a/packages/openid4vc-client/src/presentations/OpenId4VpClientService.ts +++ b/packages/openid4vc-holder/src/presentations/OpenId4VpHolderService.ts @@ -43,7 +43,7 @@ function isVerifiedAuthorizationRequestWithPresentationDefinition( } @injectable() -export class OpenId4VpClientService { +export class OpenId4VpHolderService { public constructor(private presentationExchangeService: PresentationExchangeService) {} private getOp(agentContext: AgentContext) { diff --git a/packages/openid4vc-client/src/presentations/PresentationExchangeService.ts b/packages/openid4vc-holder/src/presentations/PresentationExchangeService.ts similarity index 100% rename from packages/openid4vc-client/src/presentations/PresentationExchangeService.ts rename to packages/openid4vc-holder/src/presentations/PresentationExchangeService.ts diff --git a/packages/openid4vc-client/src/presentations/example.md b/packages/openid4vc-holder/src/presentations/example.md similarity index 100% rename from packages/openid4vc-client/src/presentations/example.md rename to packages/openid4vc-holder/src/presentations/example.md diff --git a/packages/openid4vc-client/src/presentations/fixtures.ts b/packages/openid4vc-holder/src/presentations/fixtures.ts similarity index 100% rename from packages/openid4vc-client/src/presentations/fixtures.ts rename to packages/openid4vc-holder/src/presentations/fixtures.ts diff --git a/packages/openid4vc-client/src/presentations/index.ts b/packages/openid4vc-holder/src/presentations/index.ts similarity index 78% rename from packages/openid4vc-client/src/presentations/index.ts rename to packages/openid4vc-holder/src/presentations/index.ts index 84d9845844..7624253db7 100644 --- a/packages/openid4vc-client/src/presentations/index.ts +++ b/packages/openid4vc-holder/src/presentations/index.ts @@ -1,6 +1,6 @@ export { - OpenId4VpClientService, + OpenId4VpHolderService, VerifiedAuthorizationRequestWithPresentationDefinition, -} from './OpenId4VpClientService' +} from './OpenId4VpHolderService' export { PresentationExchangeService } from './PresentationExchangeService' export { PresentationSubmission, SubmissionEntry } from './selection' diff --git a/packages/openid4vc-client/src/presentations/selection/PexCredentialSelection.ts b/packages/openid4vc-holder/src/presentations/selection/PexCredentialSelection.ts similarity index 100% rename from packages/openid4vc-client/src/presentations/selection/PexCredentialSelection.ts rename to packages/openid4vc-holder/src/presentations/selection/PexCredentialSelection.ts diff --git a/packages/openid4vc-client/src/presentations/selection/index.ts b/packages/openid4vc-holder/src/presentations/selection/index.ts similarity index 100% rename from packages/openid4vc-client/src/presentations/selection/index.ts rename to packages/openid4vc-holder/src/presentations/selection/index.ts diff --git a/packages/openid4vc-client/src/presentations/selection/types.ts b/packages/openid4vc-holder/src/presentations/selection/types.ts similarity index 100% rename from packages/openid4vc-client/src/presentations/selection/types.ts rename to packages/openid4vc-holder/src/presentations/selection/types.ts diff --git a/packages/openid4vc-client/src/presentations/transform.ts b/packages/openid4vc-holder/src/presentations/transform.ts similarity index 100% rename from packages/openid4vc-client/src/presentations/transform.ts rename to packages/openid4vc-holder/src/presentations/transform.ts diff --git a/packages/openid4vc-holder/tests/OpenId4VcClientModule.test.ts b/packages/openid4vc-holder/tests/OpenId4VcClientModule.test.ts index 46d2bb7308..f67cdbc991 100644 --- a/packages/openid4vc-holder/tests/OpenId4VcClientModule.test.ts +++ b/packages/openid4vc-holder/tests/OpenId4VcClientModule.test.ts @@ -4,6 +4,7 @@ import type { DependencyManager } from '@aries-framework/core' import { OpenId4VcHolderApi } from '../src/OpenId4VcHolderApi' import { OpenId4VcHolderModule } from '../src/OpenId4VcHolderModule' import { OpenId4VcHolderService } from '../src/OpenId4VcHolderService' +import { OpenId4VpHolderService, PresentationExchangeService } from '../src/presentations' const dependencyManager = { registerInstance: jest.fn(), @@ -20,7 +21,9 @@ describe('OpenId4VcClientModule', () => { expect(dependencyManager.registerContextScoped).toHaveBeenCalledTimes(1) expect(dependencyManager.registerContextScoped).toHaveBeenCalledWith(OpenId4VcHolderApi) - expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(1) + expect(dependencyManager.registerSingleton).toHaveBeenCalledTimes(3) expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(OpenId4VcHolderService) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(OpenId4VpHolderService) + expect(dependencyManager.registerSingleton).toHaveBeenCalledWith(PresentationExchangeService) }) }) diff --git a/packages/openid4vc-issuer/README.md b/packages/openid4vc-issuer/README.md new file mode 100644 index 0000000000..1a62c933de --- /dev/null +++ b/packages/openid4vc-issuer/README.md @@ -0,0 +1,68 @@ +

+
+ Hyperledger Aries logo +

+

Aries Framework JavaScript Open ID Connect For Verifiable Credentials Client Module

+

+ License + typescript + @aries-framework/openid4vc-issuer version + +

+
+ +Open ID Connect For Verifiable Credentials Issuer Module for [Aries Framework JavaScript](https://github.com/hyperledger/aries-framework-javascript). + +### Installation + +Make sure you have set up the correct version of Aries Framework JavaScript according to the AFJ repository. + +```sh +yarn add @aries-framework/openid4vc-issuer +``` + +### Quick start + +#### Requirements + +#### Module registration + +In order to get this module to work, we need to inject it into the agent. This makes the module's functionality accessible through the agent's `modules` api. + +```ts +import { OpenId4VcIssuerModule } from '@aries-framework/openid4vc-issuer' + +const agent = new Agent({ + config: { + /* config */ + }, + dependencies: agentDependencies, + modules: { + openId4VcIssuer: new OpenId4VcIssuerModule(), + /* other custom modules */ + }, +}) + +await agent.initialize() +``` + +How the module is injected and the agent has been initialized, you can access the module's functionality through `agent.modules.openId4VcIssuer`. + +#### Preparing a DID diff --git a/packages/openid4vc-client/jest.config.ts b/packages/openid4vc-issuer/jest.config.ts similarity index 100% rename from packages/openid4vc-client/jest.config.ts rename to packages/openid4vc-issuer/jest.config.ts diff --git a/packages/openid4vc-client/package.json b/packages/openid4vc-issuer/package.json similarity index 69% rename from packages/openid4vc-client/package.json rename to packages/openid4vc-issuer/package.json index 9c50ae7100..3023b6998b 100644 --- a/packages/openid4vc-client/package.json +++ b/packages/openid4vc-issuer/package.json @@ -1,5 +1,5 @@ { - "name": "@aries-framework/openid4vc-client", + "name": "@aries-framework/openid4vc-issuer", "main": "build/index", "types": "build/index", "version": "0.4.2", @@ -10,11 +10,11 @@ "publishConfig": { "access": "public" }, - "homepage": "https://github.com/hyperledger/aries-framework-javascript/tree/main/packages/openid4vc-client", + "homepage": "https://github.com/hyperledger/aries-framework-javascript/tree/main/packages/openid4vc-issuer", "repository": { "type": "git", "url": "https://github.com/hyperledger/aries-framework-javascript", - "directory": "packages/openid4vc-client" + "directory": "packages/openid4vc-issuer" }, "scripts": { "build": "yarn run clean && yarn run compile", @@ -25,22 +25,16 @@ }, "dependencies": { "@aries-framework/core": "0.4.2", - "@sphereon/did-auth-siop": "^0.4.2", - "@sphereon/oid4vci-client": "^0.7.3", + "@sphereon/oid4vci-issuer": "^0.7.3", "@sphereon/oid4vci-common": "^0.7.3", - "@sphereon/pex": "^2.1.3-unstable.6", - "@sphereon/pex-models": "^2.1.1", "@sphereon/ssi-types": "^0.17.5", "@stablelib/random": "^1.0.2", - "fast-text-encoding": "^1.0.6", - "jsonpath": "^1.1.1", - "sha.js": "^2.4.11" + "fast-text-encoding": "^1.0.6" }, "devDependencies": { "@aries-framework/askar": "0.4.2", "@aries-framework/node": "0.4.2", "@hyperledger/aries-askar-nodejs": "^0.1.0", - "@types/jsonpath": "^0.2.0", "nock": "^13.3.0", "rimraf": "^4.4.0", "typescript": "~4.9.5" diff --git a/packages/openid4vc-issuer/src/OpenId4VcIssuerApi.ts b/packages/openid4vc-issuer/src/OpenId4VcIssuerApi.ts new file mode 100644 index 0000000000..feaff0ba8e --- /dev/null +++ b/packages/openid4vc-issuer/src/OpenId4VcIssuerApi.ts @@ -0,0 +1,27 @@ +import type { IssueCredentialOptions, SendCredentialOfferOptions } from './OpenId4VcIssuerServiceOptions' + +import { injectable, AgentContext } from '@aries-framework/core' + +import { OpenId4VcIssuerService } from './OpenId4VcIssuerService' + +/** + * @public + */ +@injectable() +export class OpenId4VcIssuerApi { + private agentContext: AgentContext + private openId4VcIssuerService: OpenId4VcIssuerService + + public constructor(agentContext: AgentContext, openId4VcIssuerService: OpenId4VcIssuerService) { + this.agentContext = agentContext + this.openId4VcIssuerService = openId4VcIssuerService + } + + public sendCredentialOffer(options: SendCredentialOfferOptions) { + // TODO: Implement + } + + public issueCredential(options: IssueCredentialOptions) { + // TODO: Implement + } +} diff --git a/packages/openid4vc-issuer/src/OpenId4VcIssuerModule.ts b/packages/openid4vc-issuer/src/OpenId4VcIssuerModule.ts new file mode 100644 index 0000000000..26a65a88ad --- /dev/null +++ b/packages/openid4vc-issuer/src/OpenId4VcIssuerModule.ts @@ -0,0 +1,31 @@ +import type { DependencyManager, Module } from '@aries-framework/core' + +import { AgentConfig } from '@aries-framework/core' + +import { OpenId4VcIssuerApi } from './OpenId4VcIssuerApi' +import { OpenId4VcIssuerService } from './OpenId4VcIssuerService' + +/** + * @public + */ +export class OpenId4VcIssuerModule implements Module { + public readonly api = OpenId4VcIssuerApi + + /** + * Registers the dependencies of the question answer module on the dependency manager. + */ + public register(dependencyManager: DependencyManager) { + // Warn about experimental module + dependencyManager + .resolve(AgentConfig) + .logger.warn( + "The '@aries-framework/openid4vc-issuer' module is experimental and could have unexpected breaking changes. When using this module, make sure to use strict versions for all @aries-framework packages." + ) + + // Api + dependencyManager.registerContextScoped(OpenId4VcIssuerApi) + + // Services + dependencyManager.registerSingleton(OpenId4VcIssuerService) + } +} diff --git a/packages/openid4vc-issuer/src/OpenId4VcIssuerService.ts b/packages/openid4vc-issuer/src/OpenId4VcIssuerService.ts new file mode 100644 index 0000000000..7a50ba6366 --- /dev/null +++ b/packages/openid4vc-issuer/src/OpenId4VcIssuerService.ts @@ -0,0 +1,32 @@ +import { + InjectionSymbols, + JwsService, + Logger, + W3cCredentialRepository, + W3cCredentialService, + inject, + injectable, +} from '@aries-framework/core' + +/** + * @internal + */ +@injectable() +export class OpenId4VcIssuerService { + private logger: Logger + private w3cCredentialService: W3cCredentialService + private w3cCredentialRepository: W3cCredentialRepository + private jwsService: JwsService + + public constructor( + @inject(InjectionSymbols.Logger) logger: Logger, + w3cCredentialService: W3cCredentialService, + w3cCredentialRepository: W3cCredentialRepository, + jwsService: JwsService + ) { + this.w3cCredentialService = w3cCredentialService + this.w3cCredentialRepository = w3cCredentialRepository + this.jwsService = jwsService + this.logger = logger + } +} diff --git a/packages/openid4vc-issuer/src/OpenId4VcIssuerServiceOptions.ts b/packages/openid4vc-issuer/src/OpenId4VcIssuerServiceOptions.ts new file mode 100644 index 0000000000..0de5ea82f2 --- /dev/null +++ b/packages/openid4vc-issuer/src/OpenId4VcIssuerServiceOptions.ts @@ -0,0 +1,7 @@ +export interface IssueCredentialOptions { + tobedefined: true +} + +export interface SendCredentialOfferOptions { + tobedefined: true +} diff --git a/packages/openid4vc-issuer/src/index.ts b/packages/openid4vc-issuer/src/index.ts new file mode 100644 index 0000000000..3be8888448 --- /dev/null +++ b/packages/openid4vc-issuer/src/index.ts @@ -0,0 +1,8 @@ +import 'fast-text-encoding' + +export * from './OpenId4VcIssuerApi' +export * from './OpenId4VcIssuerModule' +export * from './OpenId4VcIssuerService' + +// Contains internal types, so we don't export everything +export {} from './OpenId4VcIssuerServiceOptions' diff --git a/packages/openid4vc-issuer/tests/openid4vc-issuer.e2e.test.ts b/packages/openid4vc-issuer/tests/openid4vc-issuer.e2e.test.ts new file mode 100644 index 0000000000..2aa4596409 --- /dev/null +++ b/packages/openid4vc-issuer/tests/openid4vc-issuer.e2e.test.ts @@ -0,0 +1,59 @@ +import type { KeyDidCreateOptions } from '@aries-framework/core' + +import { AskarModule } from '@aries-framework/askar' +import { + JwaSignatureAlgorithm, + Agent, + KeyType, + TypedArrayEncoder, + W3cCredentialRecord, + DidKey, +} from '@aries-framework/core' +import { agentDependencies } from '@aries-framework/node' +import { ariesAskar } from '@hyperledger/aries-askar-nodejs' +import nock, { cleanAll, enableNetConnect } from 'nock' + +import { OpenId4VcIssuerModule } from '../src' + +const modules = { + openId4VcHolder: new OpenId4VcIssuerModule(), + askar: new AskarModule({ + ariesAskar, + }), +} + +describe('OpenId4VcIssuer', () => { + let agent: Agent + + beforeEach(async () => { + agent = new Agent({ + config: { + label: 'OpenId4VcIssuer Test', + walletConfig: { + id: 'openid4vc-Issuer-test', + key: 'openid4vc-Issuer-test', + }, + }, + dependencies: agentDependencies, + modules, + }) + + await agent.initialize() + }) + + afterEach(async () => { + await agent.shutdown() + await agent.wallet.delete() + }) + + describe('[DRAFT 08]: Pre-authorized flow', () => { + afterEach(() => { + cleanAll() + enableNetConnect() + }) + + it('test', async () => { + expect(true).toBe(true) + }) + }) +}) diff --git a/packages/openid4vc-client/tests/setup.ts b/packages/openid4vc-issuer/tests/setup.ts similarity index 100% rename from packages/openid4vc-client/tests/setup.ts rename to packages/openid4vc-issuer/tests/setup.ts diff --git a/packages/openid4vc-client/tsconfig.build.json b/packages/openid4vc-issuer/tsconfig.build.json similarity index 100% rename from packages/openid4vc-client/tsconfig.build.json rename to packages/openid4vc-issuer/tsconfig.build.json diff --git a/packages/openid4vc-client/tsconfig.json b/packages/openid4vc-issuer/tsconfig.json similarity index 100% rename from packages/openid4vc-client/tsconfig.json rename to packages/openid4vc-issuer/tsconfig.json diff --git a/packages/openid4vc-verifier/README.md b/packages/openid4vc-verifier/README.md new file mode 100644 index 0000000000..02adb4c47a --- /dev/null +++ b/packages/openid4vc-verifier/README.md @@ -0,0 +1,68 @@ +

+
+ Hyperledger Aries logo +

+

Aries Framework JavaScript Open ID Connect For Verifiable Credentials Client Module

+

+ License + typescript + @aries-framework/openid4vc-verifier version + +

+
+ +Open ID Connect For Verifiable Credentials Verifier Module for [Aries Framework JavaScript](https://github.com/hyperledger/aries-framework-javascript). + +### Installation + +Make sure you have set up the correct version of Aries Framework JavaScript according to the AFJ repository. + +```sh +yarn add @aries-framework/openid4vc-verifier +``` + +### Quick start + +#### Requirements + +#### Module registration + +In order to get this module to work, we need to inject it into the agent. This makes the module's functionality accessible through the agent's `modules` api. + +```ts +import { OpenId4VcVerifierModule } from '@aries-framework/openid4vc-verifier' + +const agent = new Agent({ + config: { + /* config */ + }, + dependencies: agentDependencies, + modules: { + openId4VcVerifier: new OpenId4VcVerifierModule(), + /* other custom modules */ + }, +}) + +await agent.initialize() +``` + +How the module is injected and the agent has been initialized, you can access the module's functionality through `agent.modules.openId4VcVerifier`. + +#### Preparing a DID diff --git a/packages/openid4vc-verifier/jest.config.ts b/packages/openid4vc-verifier/jest.config.ts new file mode 100644 index 0000000000..8641cf4d67 --- /dev/null +++ b/packages/openid4vc-verifier/jest.config.ts @@ -0,0 +1,14 @@ +import type { Config } from '@jest/types' + +import base from '../../jest.config.base' + +import packageJson from './package.json' + +const config: Config.InitialOptions = { + ...base, + + displayName: packageJson.name, + setupFilesAfterEnv: ['./tests/setup.ts'], +} + +export default config diff --git a/packages/openid4vc-verifier/package.json b/packages/openid4vc-verifier/package.json new file mode 100644 index 0000000000..aba1ebdfef --- /dev/null +++ b/packages/openid4vc-verifier/package.json @@ -0,0 +1,41 @@ +{ + "name": "@aries-framework/openid4vc-verifier", + "main": "build/index", + "types": "build/index", + "version": "0.4.2", + "files": [ + "build" + ], + "license": "Apache-2.0", + "publishConfig": { + "access": "public" + }, + "homepage": "https://github.com/hyperledger/aries-framework-javascript/tree/main/packages/openid4vc-verifier", + "repository": { + "type": "git", + "url": "https://github.com/hyperledger/aries-framework-javascript", + "directory": "packages/openid4vc-verifier" + }, + "scripts": { + "build": "yarn run clean && yarn run compile", + "clean": "rimraf ./build", + "compile": "tsc -p tsconfig.build.json", + "prepublishOnly": "yarn run build", + "test": "jest" + }, + "dependencies": { + "@aries-framework/core": "0.4.2", + "@sphereon/did-auth-siop": "^0.4.2", + "@sphereon/ssi-types": "^0.17.5", + "@stablelib/random": "^1.0.2", + "fast-text-encoding": "^1.0.6" + }, + "devDependencies": { + "@aries-framework/askar": "0.4.2", + "@aries-framework/node": "0.4.2", + "@hyperledger/aries-askar-nodejs": "^0.1.0", + "nock": "^13.3.0", + "rimraf": "^4.4.0", + "typescript": "~4.9.5" + } +} diff --git a/packages/openid4vc-verifier/src/OpenId4VcVerifierApi.ts b/packages/openid4vc-verifier/src/OpenId4VcVerifierApi.ts new file mode 100644 index 0000000000..b80f1f0c75 --- /dev/null +++ b/packages/openid4vc-verifier/src/OpenId4VcVerifierApi.ts @@ -0,0 +1,27 @@ +import type { IssueCredentialOptions, SendCredentialOfferOptions } from './OpenId4VcVerifierServiceOptions' + +import { injectable, AgentContext } from '@aries-framework/core' + +import { OpenId4VcVerifierService } from './OpenId4VcVerifierService' + +/** + * @public + */ +@injectable() +export class OpenId4VcVerifierApi { + private agentContext: AgentContext + private openId4VcVerifierService: OpenId4VcVerifierService + + public constructor(agentContext: AgentContext, openId4VcVerifierService: OpenId4VcVerifierService) { + this.agentContext = agentContext + this.openId4VcVerifierService = openId4VcVerifierService + } + + public sendCredentialOffer(options: SendCredentialOfferOptions) { + // TODO: Implement + } + + public issueCredential(options: IssueCredentialOptions) { + // TODO: Implement + } +} diff --git a/packages/openid4vc-verifier/src/OpenId4VcVerifierModule.ts b/packages/openid4vc-verifier/src/OpenId4VcVerifierModule.ts new file mode 100644 index 0000000000..38b68df2ea --- /dev/null +++ b/packages/openid4vc-verifier/src/OpenId4VcVerifierModule.ts @@ -0,0 +1,31 @@ +import type { DependencyManager, Module } from '@aries-framework/core' + +import { AgentConfig } from '@aries-framework/core' + +import { OpenId4VcVerifierApi } from './OpenId4VcVerifierApi' +import { OpenId4VcVerifierService } from './OpenId4VcVerifierService' + +/** + * @public + */ +export class OpenId4VcVerifierModule implements Module { + public readonly api = OpenId4VcVerifierApi + + /** + * Registers the dependencies of the question answer module on the dependency manager. + */ + public register(dependencyManager: DependencyManager) { + // Warn about experimental module + dependencyManager + .resolve(AgentConfig) + .logger.warn( + "The '@aries-framework/openid4vc-verifier' module is experimental and could have unexpected breaking changes. When using this module, make sure to use strict versions for all @aries-framework packages." + ) + + // Api + dependencyManager.registerContextScoped(OpenId4VcVerifierApi) + + // Services + dependencyManager.registerSingleton(OpenId4VcVerifierService) + } +} diff --git a/packages/openid4vc-verifier/src/OpenId4VcVerifierService.ts b/packages/openid4vc-verifier/src/OpenId4VcVerifierService.ts new file mode 100644 index 0000000000..4c5f2891d5 --- /dev/null +++ b/packages/openid4vc-verifier/src/OpenId4VcVerifierService.ts @@ -0,0 +1,32 @@ +import { + InjectionSymbols, + JwsService, + Logger, + W3cCredentialRepository, + W3cCredentialService, + inject, + injectable, +} from '@aries-framework/core' + +/** + * @internal + */ +@injectable() +export class OpenId4VcVerifierService { + private logger: Logger + private w3cCredentialService: W3cCredentialService + private w3cCredentialRepository: W3cCredentialRepository + private jwsService: JwsService + + public constructor( + @inject(InjectionSymbols.Logger) logger: Logger, + w3cCredentialService: W3cCredentialService, + w3cCredentialRepository: W3cCredentialRepository, + jwsService: JwsService + ) { + this.w3cCredentialService = w3cCredentialService + this.w3cCredentialRepository = w3cCredentialRepository + this.jwsService = jwsService + this.logger = logger + } +} diff --git a/packages/openid4vc-verifier/src/OpenId4VcVerifierServiceOptions.ts b/packages/openid4vc-verifier/src/OpenId4VcVerifierServiceOptions.ts new file mode 100644 index 0000000000..0de5ea82f2 --- /dev/null +++ b/packages/openid4vc-verifier/src/OpenId4VcVerifierServiceOptions.ts @@ -0,0 +1,7 @@ +export interface IssueCredentialOptions { + tobedefined: true +} + +export interface SendCredentialOfferOptions { + tobedefined: true +} diff --git a/packages/openid4vc-verifier/src/index.ts b/packages/openid4vc-verifier/src/index.ts new file mode 100644 index 0000000000..e13c769e11 --- /dev/null +++ b/packages/openid4vc-verifier/src/index.ts @@ -0,0 +1,8 @@ +import 'fast-text-encoding' + +export * from './OpenId4VcVerifierApi' +export * from './OpenId4VcVerifierModule' +export * from './OpenId4VcVerifierService' + +// Contains internal types, so we don't export everything +export {} from './OpenId4VcVerifierServiceOptions' diff --git a/packages/openid4vc-verifier/tests/openid4vc-verifier.e2e.test.ts b/packages/openid4vc-verifier/tests/openid4vc-verifier.e2e.test.ts new file mode 100644 index 0000000000..fbc8310e2f --- /dev/null +++ b/packages/openid4vc-verifier/tests/openid4vc-verifier.e2e.test.ts @@ -0,0 +1,59 @@ +import type { KeyDidCreateOptions } from '@aries-framework/core' + +import { AskarModule } from '@aries-framework/askar' +import { + JwaSignatureAlgorithm, + Agent, + KeyType, + TypedArrayEncoder, + W3cCredentialRecord, + DidKey, +} from '@aries-framework/core' +import { agentDependencies } from '@aries-framework/node' +import { ariesAskar } from '@hyperledger/aries-askar-nodejs' +import nock, { cleanAll, enableNetConnect } from 'nock' + +import { OpenId4VcVerifierModule } from '../src' + +const modules = { + openId4VcHolder: new OpenId4VcVerifierModule(), + askar: new AskarModule({ + ariesAskar, + }), +} + +describe('OpenId4VcVerifier', () => { + let agent: Agent + + beforeEach(async () => { + agent = new Agent({ + config: { + label: 'OpenId4VcVerifier Test', + walletConfig: { + id: 'openid4vc-Verifier-test', + key: 'openid4vc-Verifier-test', + }, + }, + dependencies: agentDependencies, + modules, + }) + + await agent.initialize() + }) + + afterEach(async () => { + await agent.shutdown() + await agent.wallet.delete() + }) + + describe('[DRAFT 08]: Pre-authorized flow', () => { + afterEach(() => { + cleanAll() + enableNetConnect() + }) + + it('test', async () => { + expect(true).toBe(true) + }) + }) +}) diff --git a/packages/openid4vc-verifier/tests/setup.ts b/packages/openid4vc-verifier/tests/setup.ts new file mode 100644 index 0000000000..34e38c9705 --- /dev/null +++ b/packages/openid4vc-verifier/tests/setup.ts @@ -0,0 +1 @@ +jest.setTimeout(120000) diff --git a/packages/openid4vc-verifier/tsconfig.build.json b/packages/openid4vc-verifier/tsconfig.build.json new file mode 100644 index 0000000000..2b075bbd85 --- /dev/null +++ b/packages/openid4vc-verifier/tsconfig.build.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.build.json", + "compilerOptions": { + "outDir": "./build", + "skipLibCheck": true + }, + "include": ["src/**/*"] +} diff --git a/packages/openid4vc-verifier/tsconfig.json b/packages/openid4vc-verifier/tsconfig.json new file mode 100644 index 0000000000..c1aca0e050 --- /dev/null +++ b/packages/openid4vc-verifier/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "types": ["jest"], + "skipLibCheck": true + } +}