@sphereon/openid4vci-client
Version:
OpenID for Verifiable Credential Issuance (OpenID4VCI) client
202 lines • 18.8 kB
JavaScript
;
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.AccessTokenClient = void 0;
const ssi_types_1 = require("@sphereon/ssi-types");
const debug_1 = __importDefault(require("debug"));
const MetadataClient_1 = require("./MetadataClient");
const functions_1 = require("./functions");
const types_1 = require("./types");
const debug = (0, debug_1.default)('sphereon:openid4vci:token');
class AccessTokenClient {
acquireAccessTokenUsingIssuanceInitiation({ issuanceInitiation, asOpts, pin, codeVerifier, code, redirectUri, metadata, }) {
return __awaiter(this, void 0, void 0, function* () {
const { issuanceInitiationRequest } = issuanceInitiation;
const isPinRequired = this.isPinRequiredValue(issuanceInitiationRequest);
const issuerOpts = { issuer: issuanceInitiationRequest.issuer };
return yield this.acquireAccessTokenUsingRequest({
accessTokenRequest: yield this.createAccessTokenRequest({
issuanceInitiation,
asOpts,
codeVerifier,
code,
redirectUri,
pin,
}),
isPinRequired,
metadata,
asOpts,
issuerOpts,
});
});
}
acquireAccessTokenUsingRequest({ accessTokenRequest, isPinRequired, metadata, asOpts, issuerOpts, }) {
return __awaiter(this, void 0, void 0, function* () {
this.validate(accessTokenRequest, isPinRequired);
const requestTokenURL = AccessTokenClient.determineTokenURL({
asOpts,
issuerOpts,
metadata: metadata
? metadata
: (issuerOpts === null || issuerOpts === void 0 ? void 0 : issuerOpts.fetchMetadata)
? yield MetadataClient_1.MetadataClient.retrieveAllMetadata(issuerOpts.issuer, { errorOnNotFound: false })
: undefined,
});
return this.sendAuthCode(requestTokenURL, accessTokenRequest);
});
}
createAccessTokenRequest({ issuanceInitiation, asOpts, pin, codeVerifier, code, redirectUri, }) {
return __awaiter(this, void 0, void 0, function* () {
const issuanceInitiationRequest = issuanceInitiation.issuanceInitiationRequest;
issuanceInitiationRequest;
const request = {};
if (asOpts === null || asOpts === void 0 ? void 0 : asOpts.clientId) {
request.client_id = asOpts.clientId;
}
this.assertNumericPin(this.isPinRequiredValue(issuanceInitiationRequest), pin);
request.user_pin = pin;
if (issuanceInitiationRequest[types_1.PRE_AUTH_CODE_LITERAL]) {
if (codeVerifier) {
throw new Error('Cannot pass a code_verifier when flow type is pre-authorized');
}
request.grant_type = types_1.GrantTypes.PRE_AUTHORIZED_CODE;
request[types_1.PRE_AUTH_CODE_LITERAL] = issuanceInitiationRequest[types_1.PRE_AUTH_CODE_LITERAL];
}
if (issuanceInitiationRequest.op_state) {
this.throwNotSupportedFlow();
request.grant_type = types_1.GrantTypes.AUTHORIZATION_CODE;
}
if (codeVerifier) {
request.code_verifier = codeVerifier;
request.code = code;
request.redirect_uri = redirectUri;
request.grant_type = types_1.GrantTypes.AUTHORIZATION_CODE;
}
if (request.grant_type === types_1.GrantTypes.AUTHORIZATION_CODE && issuanceInitiationRequest[types_1.PRE_AUTH_CODE_LITERAL]) {
throw Error('A pre_authorized_code flow cannot have an op_state in the initiation request');
}
return request;
});
}
assertPreAuthorizedGrantType(grantType) {
if (types_1.GrantTypes.PRE_AUTHORIZED_CODE !== grantType) {
throw new Error("grant type must be 'urn:ietf:params:oauth:grant-type:pre-authorized_code'");
}
}
assertAuthorizationGrantType(grantType) {
if (types_1.GrantTypes.AUTHORIZATION_CODE !== grantType) {
throw new Error("grant type must be 'authorization_code'");
}
}
isPinRequiredValue(issuanceInitiationRequest) {
let isPinRequired = false;
if (issuanceInitiationRequest !== undefined) {
if (typeof issuanceInitiationRequest.user_pin_required === 'string') {
isPinRequired = issuanceInitiationRequest.user_pin_required.toLowerCase() === 'true';
}
else if (typeof issuanceInitiationRequest.user_pin_required === 'boolean') {
isPinRequired = issuanceInitiationRequest.user_pin_required;
}
}
debug(`Pin required for issuer ${issuanceInitiationRequest.issuer}: ${isPinRequired}`);
return isPinRequired;
}
assertNumericPin(isPinRequired, pin) {
if (isPinRequired) {
if (!pin || !/^\d{1,8}$/.test(pin)) {
debug(`Pin is not 1 to 8 digits long`);
throw new Error('A valid pin consisting of maximal 8 numeric characters must be present.');
}
}
else if (pin) {
debug(`Pin set, whilst not required`);
throw new Error('Cannot set a pin, when the pin is not required.');
}
}
assertNonEmptyPreAuthorizedCode(accessTokenRequest) {
if (!accessTokenRequest[types_1.PRE_AUTH_CODE_LITERAL]) {
debug(`No pre-authorized code present, whilst it is required`);
throw new Error('Pre-authorization must be proven by presenting the pre-authorized code. Code must be present.');
}
}
assertNonEmptyCodeVerifier(accessTokenRequest) {
if (!accessTokenRequest.code_verifier) {
debug('No code_verifier present, whilst it is required');
throw new Error('Authorization flow requires the code_verifier to be present');
}
}
assertNonEmptyCode(accessTokenRequest) {
if (!accessTokenRequest.code) {
debug('No code present, whilst it is required');
throw new Error('Authorization flow requires the code to be present');
}
}
assertNonEmptyRedirectUri(accessTokenRequest) {
if (!accessTokenRequest.redirect_uri) {
debug('No redirect_uri present, whilst it is required');
throw new Error('Authorization flow requires the redirect_uri to be present');
}
}
validate(accessTokenRequest, isPinRequired) {
if (accessTokenRequest.grant_type === types_1.GrantTypes.PRE_AUTHORIZED_CODE) {
this.assertPreAuthorizedGrantType(accessTokenRequest.grant_type);
this.assertNonEmptyPreAuthorizedCode(accessTokenRequest);
this.assertNumericPin(isPinRequired, accessTokenRequest.user_pin);
}
else if (accessTokenRequest.grant_type === types_1.GrantTypes.AUTHORIZATION_CODE) {
this.assertAuthorizationGrantType(accessTokenRequest.grant_type);
this.assertNonEmptyCodeVerifier(accessTokenRequest);
this.assertNonEmptyCode(accessTokenRequest);
this.assertNonEmptyRedirectUri(accessTokenRequest);
}
else {
this.throwNotSupportedFlow;
}
}
sendAuthCode(requestTokenURL, accessTokenRequest) {
return __awaiter(this, void 0, void 0, function* () {
return yield (0, functions_1.formPost)(requestTokenURL, (0, functions_1.convertJsonToURI)(accessTokenRequest));
});
}
static determineTokenURL({ asOpts, issuerOpts, metadata, }) {
if (!asOpts && !(metadata === null || metadata === void 0 ? void 0 : metadata.token_endpoint) && !issuerOpts) {
throw new Error('Cannot determine token URL if no issuer, metadata and no Authorization Server values are present');
}
const url = asOpts && asOpts.as
? this.creatTokenURLFromURL(asOpts.as, asOpts === null || asOpts === void 0 ? void 0 : asOpts.allowInsecureEndpoints, asOpts.tokenEndpoint)
: (metadata === null || metadata === void 0 ? void 0 : metadata.token_endpoint)
? metadata.token_endpoint
: this.creatTokenURLFromURL(issuerOpts.issuer, asOpts === null || asOpts === void 0 ? void 0 : asOpts.allowInsecureEndpoints, issuerOpts.tokenEndpoint);
if (!url || !ssi_types_1.ObjectUtils.isString(url)) {
throw new Error('No authorization server token URL present. Cannot acquire access token');
}
debug(`Token endpoint determined to be ${url}`);
return url;
}
static creatTokenURLFromURL(url, allowInsecureEndpoints, tokenEndpoint) {
if (allowInsecureEndpoints !== true && url.startsWith('http://')) {
throw Error(`Unprotected token endpoints are not allowed ${url}`);
}
const hostname = url.replace(/https?:\/\//, '').replace(/\/$/, '');
const endpoint = tokenEndpoint ? (tokenEndpoint.startsWith('/') ? tokenEndpoint : tokenEndpoint.substring(1)) : '/token';
// We always require https
return `https://${hostname}${endpoint}`;
}
throwNotSupportedFlow() {
debug(`Only pre-authorized flow supported.`);
throw new Error('Only pre-authorized-code flow is supported');
}
}
exports.AccessTokenClient = AccessTokenClient;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQWNjZXNzVG9rZW5DbGllbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9saWIvQWNjZXNzVG9rZW5DbGllbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7O0FBQUEsbURBQWtEO0FBQ2xELGtEQUEwQjtBQUUxQixxREFBa0Q7QUFDbEQsMkNBQXlEO0FBQ3pELG1DQVdpQjtBQUVqQixNQUFNLEtBQUssR0FBRyxJQUFBLGVBQUssRUFBQywyQkFBMkIsQ0FBQyxDQUFDO0FBRWpELE1BQWEsaUJBQWlCO0lBQ2YseUNBQXlDLENBQUMsRUFDckQsa0JBQWtCLEVBQ2xCLE1BQU0sRUFDTixHQUFHLEVBQ0gsWUFBWSxFQUNaLElBQUksRUFDSixXQUFXLEVBQ1gsUUFBUSxHQUNlOztZQUN2QixNQUFNLEVBQUUseUJBQXlCLEVBQUUsR0FBRyxrQkFBa0IsQ0FBQztZQUV6RCxNQUFNLGFBQWEsR0FBRyxJQUFJLENBQUMsa0JBQWtCLENBQUMseUJBQXlCLENBQUMsQ0FBQztZQUN6RSxNQUFNLFVBQVUsR0FBRyxFQUFFLE1BQU0sRUFBRSx5QkFBeUIsQ0FBQyxNQUFNLEVBQUUsQ0FBQztZQUVoRSxPQUFPLE1BQU0sSUFBSSxDQUFDLDhCQUE4QixDQUFDO2dCQUMvQyxrQkFBa0IsRUFBRSxNQUFNLElBQUksQ0FBQyx3QkFBd0IsQ0FBQztvQkFDdEQsa0JBQWtCO29CQUNsQixNQUFNO29CQUNOLFlBQVk7b0JBQ1osSUFBSTtvQkFDSixXQUFXO29CQUNYLEdBQUc7aUJBQ0osQ0FBQztnQkFDRixhQUFhO2dCQUNiLFFBQVE7Z0JBQ1IsTUFBTTtnQkFDTixVQUFVO2FBQ1gsQ0FBQyxDQUFDO1FBQ0wsQ0FBQztLQUFBO0lBRVksOEJBQThCLENBQUMsRUFDMUMsa0JBQWtCLEVBQ2xCLGFBQWEsRUFDYixRQUFRLEVBQ1IsTUFBTSxFQUNOLFVBQVUsR0FPWDs7WUFDQyxJQUFJLENBQUMsUUFBUSxDQUFDLGtCQUFrQixFQUFFLGFB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