UNPKG

@sphereon/did-auth-siop

Version:

Self Issued OpenID V2 (SIOPv2) and OpenID 4 Verifiable Presentations (OID4VP)

392 lines (345 loc) 13.4 kB
import { EventEmitter } from 'events' import { IPresentationDefinition } from '@sphereon/pex' import { Hasher } from '@sphereon/ssi-types' import { DcqlQuery } from 'dcql' import { PropertyTarget, PropertyTargets } from '../authorization-request' import { PresentationVerificationCallback } from '../authorization-response' import { AuthorizationRequestPayload, ClientIdScheme, ClientMetadataOpts, CreateJwtCallback, ObjectBy, PassBy, RequestAud, RequestObjectPayload, ResponseIss, ResponseMode, ResponseType, RevocationVerification, RevocationVerificationCallback, SupportedVersion, VerifyJwtCallback, } from '../types' import { assignIfAuth, assignIfRequestObject, isTarget, isTargetOrNoTargets } from './Opts' import { RP } from './RP' import { IRPSessionManager } from './types' export class RPBuilder { requestObjectBy: ObjectBy createJwtCallback?: CreateJwtCallback verifyJwtCallback?: VerifyJwtCallback revocationVerification?: RevocationVerification revocationVerificationCallback?: RevocationVerificationCallback presentationVerificationCallback?: PresentationVerificationCallback supportedVersions: SupportedVersion[] eventEmitter?: EventEmitter sessionManager?: IRPSessionManager _responseRedirectUri?: string private _authorizationRequestPayload: Partial<AuthorizationRequestPayload> = {} private _requestObjectPayload: Partial<RequestObjectPayload> = {} clientMetadata?: ClientMetadataOpts = undefined clientId: string entityId: string clientIdScheme: string hasher: Hasher private constructor(supportedRequestVersion?: SupportedVersion) { if (supportedRequestVersion) { this.addSupportedVersion(supportedRequestVersion) } } withScope(scope: string, targets?: PropertyTargets): RPBuilder { this._authorizationRequestPayload.scope = assignIfAuth({ propertyValue: scope, targets }, false) this._requestObjectPayload.scope = assignIfRequestObject({ propertyValue: scope, targets }, true) return this } withResponseType(responseType: ResponseType | ResponseType[] | string, targets?: PropertyTargets): RPBuilder { const propertyValue = Array.isArray(responseType) ? responseType.join(' ').trim() : responseType this._authorizationRequestPayload.response_type = assignIfAuth({ propertyValue, targets }, false) this._requestObjectPayload.response_type = assignIfRequestObject({ propertyValue, targets }, true) return this } withHasher(hasher: Hasher): RPBuilder { this.hasher = hasher return this } withClientId(clientId: string, targets?: PropertyTargets): RPBuilder { this._authorizationRequestPayload.client_id = assignIfAuth({ propertyValue: clientId, targets }, false) this._requestObjectPayload.client_id = assignIfRequestObject({ propertyValue: clientId, targets }, true) this.clientId = clientId return this } withClientIdScheme(clientIdScheme: ClientIdScheme, targets?: PropertyTargets): RPBuilder { this._authorizationRequestPayload.client_id_scheme = assignIfAuth({ propertyValue: clientIdScheme, targets }, false) this._requestObjectPayload.client_id_scheme = assignIfRequestObject({ propertyValue: clientIdScheme, targets }, true) this.clientIdScheme = clientIdScheme return this } withEntityId(entityId: string, targets?: PropertyTargets): RPBuilder { this._authorizationRequestPayload.entity_id = assignIfAuth({ propertyValue: entityId, targets }, false) this._requestObjectPayload.entity_id = assignIfRequestObject({ propertyValue: entityId, targets }, true) this.entityId = entityId return this } withIssuer(issuer: ResponseIss, targets?: PropertyTargets): RPBuilder { this._authorizationRequestPayload.iss = assignIfAuth({ propertyValue: issuer, targets }, false) this._requestObjectPayload.iss = assignIfRequestObject({ propertyValue: issuer, targets }, true) return this } withAudience(issuer: RequestAud, targets?: PropertyTargets): RPBuilder { this._authorizationRequestPayload.aud = assignIfAuth({ propertyValue: issuer, targets }, false) this._requestObjectPayload.aud = assignIfRequestObject({ propertyValue: issuer, targets }, true) return this } withPresentationVerification(presentationVerificationCallback: PresentationVerificationCallback): RPBuilder { this.presentationVerificationCallback = presentationVerificationCallback return this } withRevocationVerification(mode: RevocationVerification): RPBuilder { this.revocationVerification = mode return this } withRevocationVerificationCallback(callback: RevocationVerificationCallback): RPBuilder { this.revocationVerificationCallback = callback return this } withAuthorizationEndpoint(authorizationEndpoint: string, targets?: PropertyTargets): RPBuilder { this._authorizationRequestPayload.authorization_endpoint = assignIfAuth( { propertyValue: authorizationEndpoint, targets, }, false, ) this._requestObjectPayload.authorization_endpoint = assignIfRequestObject( { propertyValue: authorizationEndpoint, targets, }, true, ) return this } withRedirectUri(redirectUri: string, targets?: PropertyTargets): RPBuilder { this._authorizationRequestPayload.redirect_uri = assignIfAuth({ propertyValue: redirectUri, targets }, false) this._requestObjectPayload.redirect_uri = assignIfRequestObject({ propertyValue: redirectUri, targets }, true) return this } withResponseRedirectUri(responseRedirectUri: string): RPBuilder { this._responseRedirectUri = responseRedirectUri return this } withResponseUri(redirectUri: string, targets?: PropertyTargets): RPBuilder { this._authorizationRequestPayload.response_uri = assignIfAuth({ propertyValue: redirectUri, targets }, false) this._requestObjectPayload.response_uri = assignIfRequestObject({ propertyValue: redirectUri, targets }, true) return this } withRequestByReference(referenceUri: string): RPBuilder { return this.withRequestBy(PassBy.REFERENCE, referenceUri /*, PropertyTarget.AUTHORIZATION_REQUEST*/) } withRequestByValue(): RPBuilder { return this.withRequestBy(PassBy.VALUE, undefined /*, PropertyTarget.AUTHORIZATION_REQUEST*/) } withRequestBy(passBy: PassBy, referenceUri?: string /*, targets?: PropertyTargets*/): RPBuilder { if (passBy === PassBy.REFERENCE && !referenceUri) { throw Error('Cannot use pass by reference without a reference URI') } this.requestObjectBy = { passBy, reference_uri: referenceUri, targets: PropertyTarget.AUTHORIZATION_REQUEST, } return this } withResponseMode(responseMode: ResponseMode, targets?: PropertyTargets): RPBuilder { this._authorizationRequestPayload.response_mode = assignIfAuth({ propertyValue: responseMode, targets }, false) this._requestObjectPayload.response_mode = assignIfRequestObject({ propertyValue: responseMode, targets }, true) return this } withClientMetadata(clientMetadata: ClientMetadataOpts, targets?: PropertyTargets): RPBuilder { clientMetadata.targets = targets if (this.getSupportedRequestVersion() < SupportedVersion.SIOPv2_D11) { this._authorizationRequestPayload.registration = assignIfAuth( { propertyValue: clientMetadata, targets, }, false, ) this._requestObjectPayload.registration = assignIfRequestObject( { propertyValue: clientMetadata, targets, }, true, ) } else { this._authorizationRequestPayload.client_metadata = assignIfAuth( { propertyValue: clientMetadata, targets, }, false, ) this._requestObjectPayload.client_metadata = assignIfRequestObject( { propertyValue: clientMetadata, targets, }, true, ) } this.clientMetadata = clientMetadata //fixme: Add URL return this } withCreateJwtCallback(createJwtCallback: CreateJwtCallback): RPBuilder { this.createJwtCallback = createJwtCallback return this } withVerifyJwtCallback(verifyJwtCallback: VerifyJwtCallback): RPBuilder { this.verifyJwtCallback = verifyJwtCallback return this } withDcqlQuery(dcqlQuery: DcqlQuery | string, targets?: PropertyTargets): RPBuilder { if (this.getSupportedRequestVersion() >= SupportedVersion.SIOPv2_D12_OID4VP_D20) { this._authorizationRequestPayload.dcql_query = assignIfAuth( { propertyValue: typeof dcqlQuery === 'string' ? dcqlQuery : JSON.stringify(dcqlQuery), targets, }, false, ) this._requestObjectPayload.dcql_query = assignIfRequestObject( { propertyValue: typeof dcqlQuery === 'string' ? dcqlQuery : JSON.stringify(dcqlQuery), targets, }, true, ) // FIXME SPRIND-144 we need to find a way in the config to select dcql vs PD without breaking OID4VC-DEMO this._authorizationRequestPayload.presentation_definition = undefined this._authorizationRequestPayload.presentation_definition_uri = undefined this._requestObjectPayload.presentation_definition = undefined this._requestObjectPayload.presentation_definition_uri = undefined } return this } withPresentationDefinition( definitionOpts: { definition: IPresentationDefinition definitionUri?: string }, targets?: PropertyTargets, ): RPBuilder { if (this._authorizationRequestPayload.dcql_query) { return this } const { definition, definitionUri } = definitionOpts if (this.getSupportedRequestVersion() < SupportedVersion.SIOPv2_D11) { const definitionProperties = { presentation_definition: definition, presentation_definition_uri: definitionUri, } const vp_token = { ...definitionProperties } if (isTarget(PropertyTarget.AUTHORIZATION_REQUEST, targets)) { this._authorizationRequestPayload.claims = { ...(this._authorizationRequestPayload.claims ? this._authorizationRequestPayload.claims : {}), vp_token: vp_token, } } if (isTargetOrNoTargets(PropertyTarget.REQUEST_OBJECT, targets)) { this._requestObjectPayload.claims = { ...(this._requestObjectPayload.claims ? this._requestObjectPayload.claims : {}), vp_token: vp_token, } } } else { this._authorizationRequestPayload.presentation_definition = assignIfAuth( { propertyValue: definition, targets, }, false, ) this._authorizationRequestPayload.presentation_definition_uri = assignIfAuth( { propertyValue: definitionUri, targets, }, true, ) this._requestObjectPayload.presentation_definition = assignIfRequestObject( { propertyValue: definition, targets, }, true, ) this._requestObjectPayload.presentation_definition_uri = assignIfRequestObject( { propertyValue: definitionUri, targets, }, true, ) } return this } private initSupportedVersions() { if (!this.supportedVersions) { this.supportedVersions = [] } } addSupportedVersion(supportedVersion: SupportedVersion): RPBuilder { this.initSupportedVersions() if (!this.supportedVersions.includes(supportedVersion)) { this.supportedVersions.push(supportedVersion) } return this } withSupportedVersions(supportedVersion: SupportedVersion[] | SupportedVersion): RPBuilder { const versions = Array.isArray(supportedVersion) ? supportedVersion : [supportedVersion] for (const version of versions) { this.addSupportedVersion(version) } return this } withEventEmitter(eventEmitter?: EventEmitter): RPBuilder { this.eventEmitter = eventEmitter ?? new EventEmitter() return this } withSessionManager(sessionManager: IRPSessionManager): RPBuilder { this.sessionManager = sessionManager return this } public getSupportedRequestVersion(requireVersion?: boolean): SupportedVersion | undefined { if (!this.supportedVersions || this.supportedVersions.length === 0) { if (requireVersion !== false) { throw Error('No supported version supplied/available') } return undefined } return this.supportedVersions[0] } public static newInstance(supportedVersion?: SupportedVersion) { return new RPBuilder(supportedVersion) } build(): RP { if (this.sessionManager && !this.eventEmitter) { throw Error('Please enable the event emitter on the RP when using a replay registry') } // We do not want others to directly use the RP class // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore return new RP({ builder: this }) } get authorizationRequestPayload(): Partial<AuthorizationRequestPayload> { return this._authorizationRequestPayload } get requestObjectPayload(): Partial<RequestObjectPayload> { return this._requestObjectPayload } /* public mergedPayload(): Partial<AuthorizationRequestPayload> { return { ...this.authorizationRequestPayload, ...this.requestObjectPayload }; }*/ }