UNPKG

@sphereon/openid4vci-client

Version:

OpenID for Verifiable Credential Issuance (OpenID4VCI) client

230 lines 19.3 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.OpenID4VCIClient = void 0; const debug_1 = __importDefault(require("debug")); const AccessTokenClient_1 = require("./AccessTokenClient"); const CredentialRequestClientBuilder_1 = require("./CredentialRequestClientBuilder"); const IssuanceInitiation_1 = require("./IssuanceInitiation"); const MetadataClient_1 = require("./MetadataClient"); const ProofOfPossessionBuilder_1 = require("./ProofOfPossessionBuilder"); const functions_1 = require("./functions"); const types_1 = require("./types"); const debug = (0, debug_1.default)('sphereon:openid4vci:flow'); class OpenID4VCIClient { constructor(initiation, flowType, kid, alg, clientId) { this._flowType = flowType; this._initiation = initiation; this._kid = kid; this._alg = alg; this._clientId = clientId; } static initiateFromURI({ issuanceInitiationURI, flowType, kid, alg, retrieveServerMetadata, clientId, }) { return __awaiter(this, void 0, void 0, function* () { const flow = new OpenID4VCIClient(IssuanceInitiation_1.IssuanceInitiation.fromURI(issuanceInitiationURI), flowType, kid, alg, clientId); if (retrieveServerMetadata !== false) { yield flow.retrieveServerMetadata(); } return flow; }); } retrieveServerMetadata() { return __awaiter(this, void 0, void 0, function* () { this.assertInitiation(); if (!this._serverMetadata) { this._serverMetadata = yield MetadataClient_1.MetadataClient.retrieveAllMetadataFromInitiation(this._initiation); } return this._serverMetadata; }); } createAuthorizationRequestUrl({ clientId, codeChallengeMethod, codeChallenge, redirectUri, scope }) { if (!scope) { throw Error('Please provide a scope. authorization_details based requests are not supported at this time'); } if (!this._serverMetadata.openid4vci_metadata.authorization_endpoint) { throw Error('Server metadata does not contain authorization endpoint'); } // add 'openid' scope if not present if (!scope.includes('openid')) { scope = `openid ${scope}`; } const queryObj = { response_type: types_1.ResponseType.AUTH_CODE, client_id: clientId, code_challenge_method: codeChallengeMethod, code_challenge: codeChallenge, redirect_uri: redirectUri, scope: scope, }; const authRequestUrl = (0, functions_1.convertJsonToURI)(queryObj, { baseUrl: this._serverMetadata.openid4vci_metadata.authorization_endpoint, uriTypeProperties: ['redirect_uri', 'scope'], }); return authRequestUrl; } acquireAccessToken({ pin, clientId, codeVerifier, code, redirectUri, }) { return __awaiter(this, void 0, void 0, function* () { this.assertInitiation(); if (clientId) { this._clientId = clientId; } if (!this._accessTokenResponse) { const accessTokenClient = new AccessTokenClient_1.AccessTokenClient(); const response = yield accessTokenClient.acquireAccessTokenUsingIssuanceInitiation({ issuanceInitiation: this._initiation, metadata: this._serverMetadata, pin, codeVerifier, code, redirectUri, asOpts: { clientId: this.clientId }, }); if (response.errorBody) { debug(`Access token error:\r\n${response.errorBody}`); throw Error(`Retrieving an access token from ${this._serverMetadata.token_endpoint} for issuer ${this._initiation.issuanceInitiationRequest.issuer} failed with status: ${response.origResponse.status}`); } this._accessTokenResponse = response.successBody; } return this._accessTokenResponse; }); } acquireCredentials({ credentialType, proofCallbacks, format, kid, alg, jti, }) { var _a; return __awaiter(this, void 0, void 0, function* () { if (alg) { this._alg = alg; } if (kid) { this._kid = kid; } const requestBuilder = CredentialRequestClientBuilder_1.CredentialRequestClientBuilder.fromIssuanceInitiation({ initiation: this.initiation, metadata: this.serverMetadata, }); requestBuilder.withToken(this.accessTokenResponse.access_token); if ((_a = this.serverMetadata) === null || _a === void 0 ? void 0 : _a.openid4vci_metadata) { const metadata = this.serverMetadata.openid4vci_metadata; const types = Array.isArray(credentialType) ? credentialType : [credentialType]; if (types.some((type) => !metadata.credentials_supported || !metadata.credentials_supported[type])) { throw Error(`Not all credential types ${JSON.stringify(credentialType)} are supported by issuer ${this.getIssuer()}`); } // todo: Format check? We might end up with some disjoint type / format combinations supported by the server } const credentialRequestClient = requestBuilder.build(); const proofBuilder = ProofOfPossessionBuilder_1.ProofOfPossessionBuilder.fromAccessTokenResponse({ accessTokenResponse: this.accessTokenResponse, callbacks: proofCallbacks, }) .withIssuer(this.getIssuer()) .withAlg(this.alg) .withJti(jti) .withClientId(this.clientId) .withKid(this.kid); const response = yield credentialRequestClient.acquireCredentialsUsingProof({ proofInput: proofBuilder, credentialType, format, }); if (response.errorBody) { debug(`Access token error:\r\n${response.errorBody}`); throw Error(`Retrieving a credential from ${this._serverMetadata.credential_endpoint} for issuer ${this._initiation.issuanceInitiationRequest.issuer} failed with status: ${response.origResponse.status}`); } return response.successBody; }); } getCredentialsSupported(restrictToInitiationTypes) { const credentialsSupported = this.serverMetadata.openid4vci_metadata.credentials_supported; if (restrictToInitiationTypes === false) { return credentialsSupported; } const initiationTypes = this.getCredentialTypesFromInitiation(); const supported = {}; for (const [key, value] of Object.entries(credentialsSupported)) { if (initiationTypes.includes(key)) { supported[key] = value; } } return supported; } getCredentialMetadata(type) { return this.getCredentialsSupported(false)[type]; } getCredentialTypesFromInitiation() { return typeof this.initiation.issuanceInitiationRequest.credential_type === 'string' ? [this.initiation.issuanceInitiationRequest.credential_type] : this.initiation.issuanceInitiationRequest.credential_type; } get flowType() { return this._flowType; } get initiation() { return this._initiation; } get serverMetadata() { this.assertServerMetadata(); return this._serverMetadata; } get kid() { this.assertInitiation(); if (!this._kid) { throw new Error('No value for kid is supplied'); } return this._kid; } get alg() { this.assertInitiation(); if (!this._alg) { throw new Error('No value for alg is supplied'); } return this._alg; } get clientId() { return this._clientId; } get accessTokenResponse() { this.assertAccessToken(); return this._accessTokenResponse; } getIssuer() { this.assertInitiation(); return this._serverMetadata ? this.serverMetadata.issuer : this.initiation.issuanceInitiationRequest.issuer; } getAccessTokenEndpoint() { this.assertInitiation(); return this.serverMetadata ? this.serverMetadata.token_endpoint : AccessTokenClient_1.AccessTokenClient.determineTokenURL({ issuerOpts: { issuer: this.getIssuer() } }); } getCredentialEndpoint() { this.assertInitiation(); return this.serverMetadata ? this.serverMetadata.credential_endpoint : `${this.getIssuer()}/credential`; } assertInitiation() { if (!this._initiation) { throw Error(`No issuance initiation present`); } } assertServerMetadata() { if (!this._serverMetadata) { throw Error('No server metadata'); } } assertAccessToken() { if (!this._accessTokenResponse) { throw Error(`No access token present`); } } } exports.OpenID4VCIClient = OpenID4VCIClient; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiT3BlbklENFZDSUNsaWVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL2xpYi9PcGVuSUQ0VkNJQ2xpZW50LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7OztBQUNBLGtEQUEwQjtBQUUxQiwyREFBd0Q7QUFDeEQscUZBQWtGO0FBQ2xGLDZEQUEwRDtBQUMxRCxxREFBa0Q7QUFDbEQseUVBQXNFO0FBQ3RFLDJDQUErQztBQUMvQyxtQ0FhaUI7QUFFakIsTUFBTSxLQUFLLEdBQUcsSUFBQSxlQUFLLEVBQUMsMEJBQTBCLENBQUMsQ0FBQztBQUVoRCxNQUFhLGdCQUFnQjtJQVMzQixZQUFvQixVQUF5QyxFQUFFLFFBQXVCLEVBQUUsR0FBWSxFQUFFLEdBQWtCLEVBQUUsUUFBaUI7UUFDekksSUFBSSxDQUFDLFNBQVMsR0FBRyxRQUFRLENBQUM7UUFDMUIsSUFBSSxDQUFDLFdBQVcsR0FBRyxVQUFVLENBQUM7UUFDOUIsSUFBSSxDQUFDLElBQUksR0FBRyxHQUFHLENBQUM7UUFDaEIsSUFBSSxDQUFDLElBQUksR0FBRyxHQUFHLENBQUM7UUFDaEIsSUFBSSxDQUFDLFNBQVMsR0FBRyxRQUFRLENBQUM7SUFDNUIsQ0FBQztJQUVNLE1BQU0sQ0FBTyxlQUFlLENBQUMsRUFDbEMscUJBQXFCLEVBQ3JCLFFBQVEsRUFDUixHQUFHLEVBQ0gsR0FBRyxFQUNILHNCQUFzQixFQUN0QixRQUFRLEdBUVQ7O1lBQ0MsTUFBTSxJQUFJLEdBQUcsSUFBSSxnQkFBZ0IsQ0FBQyx1Q0FBa0IsQ0FBQyxPQUFPLENBQUMscUJBQXFCLENBQUMsRUFBRSxRQUFRLEVBQUUsR0FBRyxFQUFFLEdBQUcsRUFBRSxRQUFRLENBQUMsQ0FBQztZQUNuSCxJQUFJLHNCQUFzQixLQUFLLEtBQUssRUFBRTtnQkFDcEMsTUFBTSxJQUFJLENBQUMsc0JBQXNCLEVBQUUsQ0FBQzthQUNyQztZQUNELE9BQU8sSUFBSSxDQUFDO1FBQ2QsQ0FBQztLQUFB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