UNPKG

@sphereon/oid4vci-issuer

Version:

OpenID 4 Verifiable Credential Issuance issuer REST endpoints

234 lines (202 loc) • 8.65 kB
import { AuthorizationServerMetadata, ClientMetadata, ClientResponseType, CNonceState, CredentialConfigurationSupportedV1_0_13, CredentialIssuerMetadataOptsV1_0_13, CredentialOfferSession, IssuerMetadata, IssuerMetadataV1_0_13, IStateManager, JWTVerifyCallback, MetadataDisplay, TokenErrorResponse, TxCode, URIState, } from '@sphereon/oid4vci-common' import { VcIssuer } from '../VcIssuer' import { oidcAccessTokenVerifyCallback } from '../functions' import { MemoryStates } from '../state-manager' import { CredentialDataSupplier, CredentialSignerCallback } from '../types' import { IssuerMetadataBuilderV1_13 } from './IssuerMetadataBuilderV1_13' export class VcIssuerBuilder { issuerMetadataBuilder?: IssuerMetadataBuilderV1_13 issuerMetadata: Partial<CredentialIssuerMetadataOptsV1_0_13> = {} authorizationServerMetadata: Partial<AuthorizationServerMetadata> = {} asClientOpts?: ClientMetadata txCode?: TxCode defaultCredentialOfferBaseUri?: string userPinRequired?: boolean cNonceExpiresIn?: number credentialOfferStateManager?: IStateManager<CredentialOfferSession> credentialOfferURIManager?: IStateManager<URIState> cNonceStateManager?: IStateManager<CNonceState> credentialSignerCallback?: CredentialSignerCallback jwtVerifyCallback?: JWTVerifyCallback credentialDataSupplier?: CredentialDataSupplier public withIssuerMetadata(issuerMetadata: IssuerMetadata) { if (!issuerMetadata.credential_configurations_supported) { throw new Error('IssuerMetadata should be from type v1_0_13 or higher.') } this.issuerMetadata = issuerMetadata as IssuerMetadataV1_0_13 return this } public withASClientMetadata(clientMetadata: ClientMetadata): this { this.asClientOpts = clientMetadata return this } public withASClientMetadataParams({ client_id, client_secret, redirect_uris, response_types, ...other }: { client_id: string; client_secret?: string; redirect_uris?: string[]; response_types?: ClientResponseType[] } & ClientMetadata): this { this.asClientOpts = { ...other, client_id, client_secret, redirect_uris, response_types } return this } public withAuthorizationMetadata(authorizationServerMetadata: AuthorizationServerMetadata) { this.authorizationServerMetadata = authorizationServerMetadata return this } public withIssuerMetadataBuilder(builder: IssuerMetadataBuilderV1_13) { this.issuerMetadataBuilder = builder return this } public withDefaultCredentialOfferBaseUri(baseUri: string) { this.defaultCredentialOfferBaseUri = baseUri return this } public withCredentialIssuer(issuer: string): this { this.issuerMetadata.credential_issuer = issuer return this } public withAuthorizationServers(authorizationServers: string | string[]): this { this.issuerMetadata.authorization_servers = typeof authorizationServers === 'string' ? [authorizationServers] : authorizationServers return this } public withCredentialEndpoint(credentialEndpoint: string): this { this.issuerMetadata.credential_endpoint = credentialEndpoint return this } public withBatchCredentialEndpoint(batchCredentialEndpoint: string): this { this.issuerMetadata.batch_credential_endpoint = batchCredentialEndpoint throw Error('Not implemented yet') // return this } public withTokenEndpoint(tokenEndpoint: string): this { this.issuerMetadata.token_endpoint = tokenEndpoint return this } public withIssuerDisplay(issuerDisplay: MetadataDisplay[] | MetadataDisplay): this { this.issuerMetadata.display = Array.isArray(issuerDisplay) ? issuerDisplay : [issuerDisplay] return this } public addIssuerDisplay(issuerDisplay: MetadataDisplay): this { this.issuerMetadata.display = [...(this.issuerMetadata.display ?? []), issuerDisplay] return this } public withCredentialConfigurationsSupported(credentialConfigurationsSupported: Record<string, CredentialConfigurationSupportedV1_0_13>) { this.issuerMetadata.credential_configurations_supported = credentialConfigurationsSupported return this } public addCredentialConfigurationsSupported(id: string, supportedCredential: CredentialConfigurationSupportedV1_0_13) { if (!this.issuerMetadata.credential_configurations_supported) { this.issuerMetadata.credential_configurations_supported = {} } this.issuerMetadata.credential_configurations_supported[id] = supportedCredential return this } public withTXCode(txCode: TxCode): this { this.txCode = txCode return this } public withCredentialOfferURIStateManager(credentialOfferURIManager: IStateManager<URIState>): this { this.credentialOfferURIManager = credentialOfferURIManager return this } public withInMemoryCredentialOfferURIState(): this { this.withCredentialOfferURIStateManager(new MemoryStates<URIState>()) return this } public withCredentialOfferStateManager(credentialOfferManager: IStateManager<CredentialOfferSession>): this { this.credentialOfferStateManager = credentialOfferManager return this } public withInMemoryCredentialOfferState(): this { this.withCredentialOfferStateManager(new MemoryStates<CredentialOfferSession>()) return this } public withCNonceStateManager(cNonceManager: IStateManager<CNonceState>): this { this.cNonceStateManager = cNonceManager return this } public withInMemoryCNonceState(): this { this.withCNonceStateManager(new MemoryStates()) return this } public withCNonceExpiresIn(cNonceExpiresIn: number): this { this.cNonceExpiresIn = cNonceExpiresIn return this } public withCredentialSignerCallback(cb: CredentialSignerCallback): this { this.credentialSignerCallback = cb return this } public withJWTVerifyCallback(verifyCallback: JWTVerifyCallback): this { this.jwtVerifyCallback = verifyCallback return this } public withCredentialDataSupplier(credentialDataSupplier: CredentialDataSupplier): this { this.credentialDataSupplier = credentialDataSupplier return this } public build(): VcIssuer { if (!this.credentialOfferStateManager) { throw new Error(TokenErrorResponse.invalid_request) } if (!this.cNonceStateManager) { throw new Error(TokenErrorResponse.invalid_request) } if (Object.keys(this.issuerMetadata).length === 0) { throw new Error('issuerMetadata not set') } if (Object.keys(this.authorizationServerMetadata).length === 0) { throw new Error('authorizationServerMetadata not set') } const builder = this.issuerMetadataBuilder?.build() const metadata: Partial<IssuerMetadataV1_0_13> = { ...this.issuerMetadata, ...builder } // Let's make sure these get merged correctly: metadata.credential_configurations_supported = this.issuerMetadata.credential_configurations_supported metadata.display = [...(this.issuerMetadata.display ?? []), ...(builder?.display ?? [])] if (!metadata.credential_endpoint || !metadata.credential_issuer || !this.issuerMetadata.credential_configurations_supported) { throw new Error(TokenErrorResponse.invalid_request) } if (this.asClientOpts && typeof this.jwtVerifyCallback !== 'function') { if (!this.issuerMetadata.credential_issuer) { throw Error('issuerMetadata.credential_issuer is required when using asClientOpts') } else if (!this.issuerMetadata.authorization_servers) { throw Error('issuerMetadata.authorization_servers is required when using asClientOpts') } this.jwtVerifyCallback = oidcAccessTokenVerifyCallback({ clientMetadata: this.asClientOpts, credentialIssuer: this.issuerMetadata.credential_issuer, authorizationServer: this.issuerMetadata.authorization_servers[0], }) } return new VcIssuer(metadata as IssuerMetadataV1_0_13, this.authorizationServerMetadata as AuthorizationServerMetadata, { //TODO: discuss this with Niels. I did not find this in the spec. but I think we should somehow communicate this ...(this.txCode && { txCode: this.txCode }), defaultCredentialOfferBaseUri: this.defaultCredentialOfferBaseUri, credentialSignerCallback: this.credentialSignerCallback, jwtVerifyCallback: this.jwtVerifyCallback, credentialDataSupplier: this.credentialDataSupplier, credentialOfferSessions: this.credentialOfferStateManager, cNonces: this.cNonceStateManager, cNonceExpiresIn: this.cNonceExpiresIn, uris: this.credentialOfferURIManager, asClientOpts: this.asClientOpts, }) } }