UNPKG

@sphereon/oid4vci-client

Version:

OpenID for Verifiable Credential Issuance (OpenID4VCI) client

237 lines • 13.8 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.CredentialRequestClient = void 0; exports.buildProof = buildProof; const oid4vc_common_1 = require("@sphereon/oid4vc-common"); const oid4vci_common_1 = require("@sphereon/oid4vci-common"); const debug_1 = __importDefault(require("debug")); const ProofOfPossessionBuilder_1 = require("./ProofOfPossessionBuilder"); const dpopUtil_1 = require("./functions/dpopUtil"); const debug = (0, debug_1.default)('sphereon:oid4vci:credential'); function buildProof(proofInput, opts) { return __awaiter(this, void 0, void 0, function* () { if ('proof_type' in proofInput) { if (opts.cNonce) { throw Error(`Cnonce param is only supported when using a Proof of possession builder`); } return yield ProofOfPossessionBuilder_1.ProofOfPossessionBuilder.fromProof(proofInput, opts.version).build(); } if (opts.cNonce) { proofInput.withAccessTokenNonce(opts.cNonce); } return yield proofInput.build(); }); } class CredentialRequestClient { get credentialRequestOpts() { return this._credentialRequestOpts; } isDeferred() { return this._isDeferred; } getCredentialEndpoint() { return this.credentialRequestOpts.credentialEndpoint; } getDeferredCredentialEndpoint() { return this.credentialRequestOpts.deferredCredentialEndpoint; } constructor(builder) { this._isDeferred = false; this._credentialRequestOpts = Object.assign({}, builder); } /** * Typically you should not use this method, as it omits a proof from the request. * There are certain issuers that in specific circumstances can do without this proof, because they have other means of user binding * like using DPoP together with an authorization code flow. These are however rare, so you should be using the acquireCredentialsUsingProof normally * @param opts */ acquireCredentialsWithoutProof(opts) { return __awaiter(this, void 0, void 0, function* () { const { credentialIdentifier, credentialTypes, format, context, subjectIssuance } = opts; const request = yield this.createCredentialRequestWithoutProof({ credentialTypes, context, format, version: this.version(), credentialIdentifier, subjectIssuance, }); return yield this.acquireCredentialsUsingRequestWithoutProof(request, opts.createDPoPOpts); }); } acquireCredentialsUsingProof(opts) { return __awaiter(this, void 0, void 0, function* () { const { credentialIdentifier, credentialTypes, proofInput, format, context, subjectIssuance } = opts; const request = yield this.createCredentialRequest({ proofInput, credentialTypes, context, format, version: this.version(), credentialIdentifier, subjectIssuance, }); return yield this.acquireCredentialsUsingRequest(request, opts.createDPoPOpts); }); } acquireCredentialsUsingRequestWithoutProof(uniformRequest, createDPoPOpts) { return __awaiter(this, void 0, void 0, function* () { return yield this.acquireCredentialsUsingRequestImpl(uniformRequest, createDPoPOpts); }); } acquireCredentialsUsingRequest(uniformRequest, createDPoPOpts) { return __awaiter(this, void 0, void 0, function* () { return yield this.acquireCredentialsUsingRequestImpl(uniformRequest, createDPoPOpts); }); } acquireCredentialsUsingRequestImpl(uniformRequest, createDPoPOpts) { return __awaiter(this, void 0, void 0, function* () { var _a, _b; if (this.version() < oid4vci_common_1.OpenId4VCIVersion.VER_1_0_13) { throw new Error('Versions below v1.0.13 (draft 13) are not supported by the V13 credential request client.'); } const request = (0, oid4vci_common_1.getCredentialRequestForVersion)(uniformRequest, this.version()); const credentialEndpoint = this.credentialRequestOpts.credentialEndpoint; if (!(0, oid4vci_common_1.isValidURL)(credentialEndpoint)) { debug(`Invalid credential endpoint: ${credentialEndpoint}`); throw new Error(oid4vci_common_1.URL_NOT_VALID); } debug(`Acquiring credential(s) from: ${credentialEndpoint}`); debug(`request\n: ${JSON.stringify(request, null, 2)}`); const requestToken = this.credentialRequestOpts.token; let dPoP = createDPoPOpts ? yield (0, oid4vc_common_1.createDPoP)((0, oid4vc_common_1.getCreateDPoPOptions)(createDPoPOpts, credentialEndpoint, { accessToken: requestToken })) : undefined; let response = (yield (0, oid4vci_common_1.post)(credentialEndpoint, JSON.stringify(request), Object.assign({ bearerToken: requestToken }, (dPoP && { customHeaders: { dpop: dPoP } })))); let nextDPoPNonce = createDPoPOpts === null || createDPoPOpts === void 0 ? void 0 : createDPoPOpts.jwtPayloadProps.nonce; const retryWithNonce = (0, dpopUtil_1.shouldRetryResourceRequestWithDPoPNonce)(response); if (retryWithNonce.ok && createDPoPOpts) { createDPoPOpts.jwtPayloadProps.nonce = retryWithNonce.dpopNonce; dPoP = yield (0, oid4vc_common_1.createDPoP)((0, oid4vc_common_1.getCreateDPoPOptions)(createDPoPOpts, credentialEndpoint, { accessToken: requestToken })); response = (yield (0, oid4vci_common_1.post)(credentialEndpoint, JSON.stringify(request), Object.assign({ bearerToken: requestToken }, (createDPoPOpts && { customHeaders: { dpop: dPoP } })))); const successDPoPNonce = response.origResponse.headers.get('DPoP-Nonce'); nextDPoPNonce = successDPoPNonce !== null && successDPoPNonce !== void 0 ? successDPoPNonce : retryWithNonce.dpopNonce; } this._isDeferred = (0, oid4vci_common_1.isDeferredCredentialResponse)(response); if (this.isDeferred() && this.credentialRequestOpts.deferredCredentialAwait && response.successBody) { response = yield this.acquireDeferredCredential(response.successBody, { bearerToken: this.credentialRequestOpts.token }); } response.access_token = requestToken; if ((uniformRequest.credential_subject_issuance && response.successBody) || ((_a = response.successBody) === null || _a === void 0 ? void 0 : _a.credential_subject_issuance)) { if (JSON.stringify(uniformRequest.credential_subject_issuance) !== JSON.stringify((_b = response.successBody) === null || _b === void 0 ? void 0 : _b.credential_subject_issuance)) { throw Error('Subject signing was requested, but issuer did not provide the options in its response'); } } debug(`Credential endpoint ${credentialEndpoint} response:\r\n${JSON.stringify(response, null, 2)}`); return Object.assign(Object.assign({}, response), (nextDPoPNonce && { params: { dpop: { dpopNonce: nextDPoPNonce } } })); }); } acquireDeferredCredential(response, opts) { return __awaiter(this, void 0, void 0, function* () { var _a; const transactionId = response.transaction_id; const bearerToken = (_a = response.acceptance_token) !== null && _a !== void 0 ? _a : opts === null || opts === void 0 ? void 0 : opts.bearerToken; const deferredCredentialEndpoint = this.getDeferredCredentialEndpoint(); if (!deferredCredentialEndpoint) { throw Error(`No deferred credential endpoint supplied.`); } else if (!bearerToken) { throw Error(`No bearer token present and refresh for defered endpoint not supported yet`); // todo updated bearer token with new c_nonce } return yield (0, oid4vci_common_1.acquireDeferredCredential)({ bearerToken, transactionId, deferredCredentialEndpoint, deferredCredentialAwait: this.credentialRequestOpts.deferredCredentialAwait, deferredCredentialIntervalInMS: this.credentialRequestOpts.deferredCredentialIntervalInMS, }); }); } createCredentialRequestWithoutProof(opts) { return __awaiter(this, void 0, void 0, function* () { return yield this.createCredentialRequestImpl(opts); }); } createCredentialRequest(opts) { return __awaiter(this, void 0, void 0, function* () { return yield this.createCredentialRequestImpl(opts); }); } createCredentialRequestImpl(opts) { return __awaiter(this, void 0, void 0, function* () { var _a; const { proofInput, credentialIdentifier: credential_identifier } = opts; let proof = undefined; if (proofInput) { proof = yield buildProof(proofInput, opts); } if (credential_identifier) { if (opts.format || opts.credentialTypes || opts.context) { throw Error(`You cannot mix credential_identifier with format, credential types and/or context`); } return Object.assign({ credential_identifier }, (proof && { proof })); } const formatSelection = (_a = opts.format) !== null && _a !== void 0 ? _a : this.credentialRequestOpts.format; if (!formatSelection) { throw Error(`Format of credential to be issued is missing`); } const format = (0, oid4vci_common_1.getUniformFormat)(formatSelection); const typesSelection = (opts === null || opts === void 0 ? void 0 : opts.credentialTypes) && (typeof opts.credentialTypes === 'string' || opts.credentialTypes.length > 0) ? opts.credentialTypes : this.credentialRequestOpts.credentialTypes; if (!typesSelection) { throw Error(`Credential type(s) need to be provided`); } const types = Array.isArray(typesSelection) ? typesSelection : [typesSelection]; if (types.length === 0) { throw Error(`Credential type(s) need to be provided`); } const issuer_state = this.credentialRequestOpts.issuerState; // TODO: we should move format specific logic if (format === 'jwt_vc_json' || format === 'jwt_vc') { return Object.assign(Object.assign(Object.assign({ credential_definition: { type: types, }, format }, (issuer_state && { issuer_state })), (proof && { proof })), opts.subjectIssuance); } else if (format === 'jwt_vc_json-ld' || format === 'ldp_vc') { if (this.version() >= oid4vci_common_1.OpenId4VCIVersion.VER_1_0_12 && !opts.context) { throw Error('No @context value present, but it is required'); } return Object.assign(Object.assign(Object.assign(Object.assign({ format }, (issuer_state && { issuer_state })), (proof && { proof })), opts.subjectIssuance), { credential_definition: { type: types, '@context': opts.context, } }); } else if (format === 'vc+sd-jwt') { if (types.length > 1) { throw Error(`Only a single credential type is supported for ${format}`); } return Object.assign(Object.assign(Object.assign(Object.assign({ format }, (issuer_state && { issuer_state })), (proof && { proof })), { vct: types[0] }), opts.subjectIssuance); } else if (format === 'mso_mdoc') { if (types.length > 1) { throw Error(`Only a single credential type is supported for ${format}`); } return Object.assign(Object.assign(Object.assign(Object.assign({ format }, (issuer_state && { issuer_state })), (proof && { proof })), { doctype: types[0] }), opts.subjectIssuance); } throw new Error(`Unsupported credential format: ${format}`); }); } version() { var _a, _b; return (_b = (_a = this.credentialRequestOpts) === null || _a === void 0 ? void 0 : _a.version) !== null && _b !== void 0 ? _b : oid4vci_common_1.OpenId4VCIVersion.VER_1_0_13; } } exports.CredentialRequestClient = CredentialRequestClient; //# sourceMappingURL=CredentialRequestClient.js.map