@sphereon/oid4vci-client
Version:
OpenID for Verifiable Credential Issuance (OpenID4VCI) client
206 lines • 12.6 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.MetadataClient = void 0;
const oid4vci_common_1 = require("@sphereon/oid4vci-common");
const debug_1 = __importDefault(require("debug"));
const MetadataClientV1_0_11_1 = require("./MetadataClientV1_0_11");
const MetadataClientV1_0_13_1 = require("./MetadataClientV1_0_13");
const functions_1 = require("./functions");
const debug = (0, debug_1.default)('sphereon:oid4vci:metadata');
class MetadataClient {
/**
* Retrieve metadata using the Initiation obtained from a previous step
*
* @param credentialOffer
*/
static retrieveAllMetadataFromCredentialOffer(credentialOffer) {
return __awaiter(this, void 0, void 0, function* () {
if ((0, oid4vci_common_1.determineSpecVersionFromOffer)(credentialOffer.credential_offer) >= oid4vci_common_1.OpenId4VCIVersion.VER_1_0_13) {
return yield MetadataClientV1_0_13_1.MetadataClientV1_0_13.retrieveAllMetadataFromCredentialOffer(credentialOffer);
}
else {
return yield MetadataClientV1_0_11_1.MetadataClientV1_0_11.retrieveAllMetadataFromCredentialOffer(credentialOffer);
}
});
}
/**
* Retrieve the metada using the initiation request obtained from a previous step
* @param request
*/
static retrieveAllMetadataFromCredentialOfferRequest(request) {
return __awaiter(this, void 0, void 0, function* () {
const issuer = (0, oid4vci_common_1.getIssuerFromCredentialOfferPayload)(request);
if (issuer) {
if ((0, oid4vci_common_1.determineSpecVersionFromOffer)(request) >= oid4vci_common_1.OpenId4VCIVersion.VER_1_0_13) {
return MetadataClientV1_0_13_1.MetadataClientV1_0_13.retrieveAllMetadataFromCredentialOfferRequest(request);
}
else {
return MetadataClientV1_0_11_1.MetadataClientV1_0_11.retrieveAllMetadataFromCredentialOfferRequest(request);
}
}
throw new Error("can't retrieve metadata from CredentialOfferRequest. No issuer field is present");
});
}
/**
* Retrieve all metadata from an issuer
* @param issuer The issuer URL
* @param opts
*/
static retrieveAllMetadata(issuer, opts) {
return __awaiter(this, void 0, void 0, function* () {
let token_endpoint;
let credential_endpoint;
let deferred_credential_endpoint;
let authorization_endpoint;
let authorization_challenge_endpoint;
let authorizationServerType = 'OID4VCI';
let authorization_servers = [issuer];
let authorization_server = undefined;
const oid4vciResponse = yield MetadataClient.retrieveOpenID4VCIServerMetadata(issuer, { errorOnNotFound: false }); // We will handle errors later, given we will also try other metadata locations
let credentialIssuerMetadata = oid4vciResponse === null || oid4vciResponse === void 0 ? void 0 : oid4vciResponse.successBody;
if (credentialIssuerMetadata) {
debug(`Issuer ${issuer} OID4VCI well-known server metadata\r\n${JSON.stringify(credentialIssuerMetadata)}`);
credential_endpoint = credentialIssuerMetadata.credential_endpoint;
deferred_credential_endpoint = credentialIssuerMetadata.deferred_credential_endpoint
? credentialIssuerMetadata.deferred_credential_endpoint
: undefined;
if (credentialIssuerMetadata.token_endpoint) {
token_endpoint = credentialIssuerMetadata.token_endpoint;
}
authorization_challenge_endpoint = credentialIssuerMetadata.authorization_challenge_endpoint;
if (credentialIssuerMetadata.authorization_servers) {
authorization_servers = credentialIssuerMetadata.authorization_servers;
}
else if (credentialIssuerMetadata.authorization_server) {
authorization_server = credentialIssuerMetadata.authorization_server;
authorization_servers = [authorization_server];
}
}
// No specific OID4VCI endpoint. Either can be an OAuth2 AS or an OIDC IDP. Let's start with OIDC first
// TODO: for now we're taking just the first one
let response = yield (0, functions_1.retrieveWellknown)(authorization_servers[0], oid4vci_common_1.WellKnownEndpoints.OPENID_CONFIGURATION, {
errorOnNotFound: false,
});
let authMetadata = response.successBody;
if (authMetadata) {
debug(`Issuer ${issuer} has OpenID Connect Server metadata in well-known location`);
authorizationServerType = 'OIDC';
}
else {
// Now let's do OAuth2
// TODO: for now we're taking just the first one
response = yield (0, functions_1.retrieveWellknown)(authorization_servers[0], oid4vci_common_1.WellKnownEndpoints.OAUTH_AS, { errorOnNotFound: false });
authMetadata = response.successBody;
}
if (!authMetadata) {
// We will always throw an error, no matter whether the user provided the option not to, because this is bad.
if (!authorization_servers.includes(issuer)) {
throw Error(`Issuer ${issuer} provided a separate authorization server ${authorization_servers}, but that server did not provide metadata`);
}
}
else {
if (!authorizationServerType) {
authorizationServerType = 'OAuth 2.0';
}
debug(`Issuer ${issuer} has ${authorizationServerType} Server metadata in well-known location`);
if (!authMetadata.authorization_endpoint) {
console.warn(`Issuer ${issuer} of type ${authorizationServerType} has no authorization_endpoint! Will use ${authorization_endpoint}. This only works for pre-authorized flows`);
}
else if (authorization_endpoint && authMetadata.authorization_endpoint !== authorization_endpoint) {
throw Error(`Credential issuer has a different authorization_endpoint (${authorization_endpoint}) from the Authorization Server (${authMetadata.authorization_endpoint})`);
}
authorization_endpoint = authMetadata.authorization_endpoint;
if (authorization_challenge_endpoint && authMetadata.authorization_challenge_endpoint !== authorization_challenge_endpoint) {
throw Error(`Credential issuer has a different authorization_challenge_endpoint (${authorization_challenge_endpoint}) from the Authorization Server (${authMetadata.authorization_challenge_endpoint})`);
}
authorization_challenge_endpoint = authMetadata.authorization_challenge_endpoint;
if (!authMetadata.token_endpoint) {
throw Error(`Authorization Server ${authorization_servers} did not provide a token_endpoint`);
}
else if (token_endpoint && authMetadata.token_endpoint !== token_endpoint) {
throw Error(`Credential issuer has a different token_endpoint (${token_endpoint}) from the Authorization Server (${authMetadata.token_endpoint})`);
}
token_endpoint = authMetadata.token_endpoint;
if (authMetadata.credential_endpoint) {
if (credential_endpoint && authMetadata.credential_endpoint !== credential_endpoint) {
debug(`Credential issuer has a different credential_endpoint (${credential_endpoint}) from the Authorization Server (${authMetadata.credential_endpoint}). Will use the issuer value`);
}
else {
credential_endpoint = authMetadata.credential_endpoint;
}
}
if (authMetadata.deferred_credential_endpoint) {
if (deferred_credential_endpoint && authMetadata.deferred_credential_endpoint !== deferred_credential_endpoint) {
debug(`Credential issuer has a different deferred_credential_endpoint (${deferred_credential_endpoint}) from the Authorization Server (${authMetadata.deferred_credential_endpoint}). Will use the issuer value`);
}
else {
deferred_credential_endpoint = authMetadata.deferred_credential_endpoint;
}
}
}
if (!authorization_endpoint) {
debug(`Issuer ${issuer} does not expose authorization_endpoint, so only pre-auth will be supported`);
}
if (!token_endpoint) {
debug(`Issuer ${issuer} does not have a token_endpoint listed in well-known locations!`);
if (opts === null || opts === void 0 ? void 0 : opts.errorOnNotFound) {
throw Error(`Could not deduce the token_endpoint for ${issuer}`);
}
else {
token_endpoint = `${issuer}${issuer.endsWith('/') ? 'token' : '/token'}`;
}
}
if (!credential_endpoint) {
debug(`Issuer ${issuer} does not have a credential_endpoint listed in well-known locations!`);
if (opts === null || opts === void 0 ? void 0 : opts.errorOnNotFound) {
throw Error(`Could not deduce the credential endpoint for ${issuer}`);
}
else {
credential_endpoint = `${issuer}${issuer.endsWith('/') ? 'credential' : '/credential'}`;
}
}
if (!credentialIssuerMetadata && authMetadata) {
// Apparently everything worked out and the issuer is exposing everything in oAuth2/OIDC well-knowns. Spec is vague about this situation, but we can support it
credentialIssuerMetadata = authorization_server
? authMetadata
: authMetadata;
}
debug(`Issuer ${issuer} token endpoint ${token_endpoint}, credential endpoint ${credential_endpoint}`);
return Object.assign(Object.assign({ issuer,
token_endpoint,
credential_endpoint,
deferred_credential_endpoint }, (authorization_server ? { authorization_server } : { authorization_servers: authorization_servers })), { authorization_endpoint,
authorization_challenge_endpoint,
authorizationServerType, credentialIssuerMetadata: authorization_server
? credentialIssuerMetadata
: credentialIssuerMetadata, authorizationServerMetadata: authMetadata });
});
}
/**
* Retrieve only the OID4VCI metadata for the issuer. So no OIDC/OAuth2 metadata
*
* @param issuerHost The issuer hostname
* @param opts
*/
static retrieveOpenID4VCIServerMetadata(issuerHost, opts) {
return __awaiter(this, void 0, void 0, function* () {
return (0, functions_1.retrieveWellknown)(issuerHost, oid4vci_common_1.WellKnownEndpoints.OPENID4VCI_ISSUER, {
errorOnNotFound: (opts === null || opts === void 0 ? void 0 : opts.errorOnNotFound) === undefined ? true : opts.errorOnNotFound,
});
});
}
}
exports.MetadataClient = MetadataClient;
//# sourceMappingURL=MetadataClient.js.map