@sphereon/openid4vci-client
Version:
OpenID for Verifiable Credential Issuance (OpenID4VCI) client
230 lines • 19.3 kB
JavaScript
"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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