@sphereon/ssi-sdk.siopv2-oid4vp-rp-rest-api
Version:
474 lines (470 loc) • 20.8 kB
JavaScript
;
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var index_exports = {};
__export(index_exports, {
SIOPv2RPApiServer: () => SIOPv2RPApiServer,
authStatusWebappEndpoint: () => authStatusWebappEndpoint,
createAuthRequestWebappEndpoint: () => createAuthRequestWebappEndpoint,
getAuthRequestSIOPv2Endpoint: () => getAuthRequestSIOPv2Endpoint,
getDefinitionsEndpoint: () => getDefinitionsEndpoint,
removeAuthRequestStateWebappEndpoint: () => removeAuthRequestStateWebappEndpoint,
verifyAuthResponseSIOPv2Endpoint: () => verifyAuthResponseSIOPv2Endpoint
});
module.exports = __toCommonJS(index_exports);
// src/siop-api-functions.ts
var import_did_auth_siop = require("@sphereon/did-auth-siop");
var import_ssi_express_support = require("@sphereon/ssi-express-support");
var import_ssi_types = require("@sphereon/ssi-types");
var parseAuthorizationResponse = /* @__PURE__ */ __name((request) => {
const contentType = request.header("content-type");
if (contentType === "application/json") {
const payload = typeof request.body === "string" ? JSON.parse(request.body) : request.body;
return payload;
}
if (contentType === "application/x-www-form-urlencoded") {
const payload = request.body;
if (typeof payload.presentation_submission === "string") {
console.log(`Supplied presentation_submission was a string instead of JSON. Correcting, but external party should fix their implementation!`);
payload.presentation_submission = JSON.parse(payload.presentation_submission);
}
if (typeof payload.vp_token === "string") {
const { vp_token } = payload;
if (vp_token.startsWith("[") && vp_token.endsWith("]") || import_ssi_types.CredentialMapper.isJsonLdAsString(vp_token)) {
payload.vp_token = JSON.parse(vp_token);
}
}
return payload;
}
throw new Error(`Unsupported content type: ${contentType}. Currently only application/x-www-form-urlencoded and application/json (for direct_post) are supported`);
}, "parseAuthorizationResponse");
function verifyAuthResponseSIOPv2Endpoint(router, context, opts) {
if (opts?.enabled === false) {
console.log(`verifyAuthResponse SIOP endpoint is disabled`);
return;
}
const path = opts?.path ?? "/siop/definitions/:definitionId/auth-responses/:correlationId";
router.post(path, (0, import_ssi_express_support.checkAuth)(opts?.endpoint), async (request, response) => {
try {
const { correlationId, definitionId, tenantId, version } = request.params;
if (!correlationId || !definitionId) {
console.log(`No authorization request could be found for the given url. correlationId: ${correlationId}, definitionId: ${definitionId}`);
return (0, import_ssi_express_support.sendErrorResponse)(response, 404, "No authorization request could be found");
}
console.log("Authorization Response (siop-sessions");
console.log(JSON.stringify(request.body, null, 2));
const definitionItems = await context.agent.pdmGetDefinitions({
filter: [
{
definitionId,
tenantId,
version
}
]
});
if (definitionItems.length === 0) {
console.log(`Could not get definition ${definitionId} from agent. Will return 404`);
response.statusCode = 404;
response.statusMessage = `No definition ${definitionId}`;
return response.send();
}
const authorizationResponse = parseAuthorizationResponse(request);
console.log(`URI: ${JSON.stringify(authorizationResponse)}`);
const definitionItem = definitionItems[0];
const verifiedResponse = await context.agent.siopVerifyAuthResponse({
authorizationResponse,
correlationId,
definitionId,
presentationDefinitions: [
{
location: opts?.presentationDefinitionLocation ?? import_did_auth_siop.PresentationDefinitionLocation.TOPLEVEL_PRESENTATION_DEF,
definition: definitionItem.definitionPayload
}
],
dcqlQuery: definitionItem.dcqlPayload
});
const wrappedPresentation = verifiedResponse?.oid4vpSubmission?.presentations[0];
if (wrappedPresentation) {
console.log("PRESENTATION:" + JSON.stringify(wrappedPresentation.presentation, null, 2));
response.statusCode = 200;
const authorizationChallengeValidationResponse = {
presentation_during_issuance_session: verifiedResponse.correlationId
};
if (authorizationResponse.is_first_party) {
response.setHeader("Content-Type", "application/json");
return response.send(JSON.stringify(authorizationChallengeValidationResponse));
}
const responseRedirectURI = await context.agent.siopGetRedirectURI({
correlationId,
definitionId,
state: verifiedResponse.state
});
if (responseRedirectURI) {
response.setHeader("Content-Type", "application/json");
return response.send(JSON.stringify({
redirect_uri: responseRedirectURI
}));
}
} else {
console.log("Missing Presentation (Verifiable Credentials)");
response.statusCode = 500;
response.statusMessage = "Missing Presentation (Verifiable Credentials)";
}
return response.send();
} catch (error) {
console.error(error);
return (0, import_ssi_express_support.sendErrorResponse)(response, 500, "Could not verify auth status", error);
}
});
}
__name(verifyAuthResponseSIOPv2Endpoint, "verifyAuthResponseSIOPv2Endpoint");
function getAuthRequestSIOPv2Endpoint(router, context, opts) {
if (opts?.enabled === false) {
console.log(`getAuthRequest SIOP endpoint is disabled`);
return;
}
const path = opts?.path ?? "/siop/definitions/:definitionId/auth-requests/:correlationId";
router.get(path, (0, import_ssi_express_support.checkAuth)(opts?.endpoint), async (request, response) => {
try {
const correlationId = request.params.correlationId;
const definitionId = request.params.definitionId;
if (!correlationId || !definitionId) {
console.log(`No authorization request could be found for the given url. correlationId: ${correlationId}, definitionId: ${definitionId}`);
return (0, import_ssi_express_support.sendErrorResponse)(response, 404, "No authorization request could be found");
}
const requestState = await context.agent.siopGetAuthRequestState({
correlationId,
definitionId,
errorOnNotFound: false
});
if (!requestState) {
console.log(`No authorization request could be found for the given url in the state manager. correlationId: ${correlationId}, definitionId: ${definitionId}`);
return (0, import_ssi_express_support.sendErrorResponse)(response, 404, `No authorization request could be found`);
}
const requestObject = await requestState.request?.requestObject?.toJwt();
console.log("JWT Request object:");
console.log(requestObject);
let error;
try {
response.statusCode = 200;
response.setHeader("Content-Type", "application/jwt");
return response.send(requestObject);
} catch (e) {
error = typeof e === "string" ? e : e instanceof Error ? e.message : void 0;
return (0, import_ssi_express_support.sendErrorResponse)(response, 500, "Could not get authorization request", e);
} finally {
await context.agent.siopUpdateAuthRequestState({
correlationId,
definitionId,
state: "sent",
error
});
}
} catch (error) {
return (0, import_ssi_express_support.sendErrorResponse)(response, 500, "Could not get authorization request", error);
}
});
}
__name(getAuthRequestSIOPv2Endpoint, "getAuthRequestSIOPv2Endpoint");
// src/webapp-api-functions.ts
var import_did_auth_siop2 = require("@sphereon/did-auth-siop");
var import_ssi_express_support2 = require("@sphereon/ssi-express-support");
var import_ssi_sdk = require("@sphereon/ssi-sdk.siopv2-oid4vp-common");
var import_ssi_sdk2 = require("@sphereon/ssi-sdk.siopv2-oid4vp-rp-auth");
var import_short_uuid = __toESM(require("short-uuid"), 1);
var import_ssi_sdk3 = require("@sphereon/ssi-sdk.core");
function createAuthRequestWebappEndpoint(router, context, opts) {
if (opts?.enabled === false) {
console.log(`createAuthRequest Webapp endpoint is disabled`);
return;
}
const path = opts?.path ?? "/webapp/definitions/:definitionId/auth-requests";
router.post(path, (0, import_ssi_express_support2.checkAuth)(opts?.endpoint), async (request, response) => {
try {
const definitionId = request.params.definitionId;
if (!definitionId) {
return (0, import_ssi_express_support2.sendErrorResponse)(response, 400, "No definitionId query parameter provided");
}
const state = request.body.state ?? import_short_uuid.default.uuid();
const correlationId = request.body.correlationId ?? state;
const qrCodeOpts = request.body.qrCodeOpts ?? opts?.qrCodeOpts;
const requestByReferenceURI = (0, import_ssi_sdk.uriWithBase)(`/siop/definitions/${definitionId}/auth-requests/${state}`, {
baseURI: opts?.siopBaseURI
});
const responseURI = (0, import_ssi_sdk.uriWithBase)(`/siop/definitions/${definitionId}/auth-responses/${state}`, {
baseURI: opts?.siopBaseURI
});
const responseRedirectURI = ("response_redirect_uri" in request.body && request.body.response_redirect_uri) ?? ("responseRedirectURI" in request.body && request.body.responseRedirectURI);
const authRequestURI = await context.agent.siopCreateAuthRequestURI({
definitionId,
correlationId,
state,
nonce: import_short_uuid.default.uuid(),
requestByReferenceURI,
responseURIType: "response_uri",
responseURI,
...responseRedirectURI && {
responseRedirectURI
}
});
let qrCodeDataUri;
if (qrCodeOpts) {
const { AwesomeQR } = await import("awesome-qr");
const qrCode = new AwesomeQR({
...qrCodeOpts,
text: authRequestURI
});
qrCodeDataUri = `data:image/png;base64,${(await qrCode.draw()).toString("base64")}`;
}
const authRequestBody = {
correlationId,
state,
definitionId,
authRequestURI,
authStatusURI: `${(0, import_ssi_sdk.uriWithBase)(opts?.webappAuthStatusPath ?? "/webapp/auth-status", {
baseURI: opts?.webappBaseURI
})}`,
...qrCodeDataUri && {
qrCodeDataUri
}
};
console.log(`Auth Request URI data to send back: ${JSON.stringify(authRequestBody)}`);
return response.json(authRequestBody);
} catch (error) {
return (0, import_ssi_express_support2.sendErrorResponse)(response, 500, "Could not create an authorization request URI", error);
}
});
}
__name(createAuthRequestWebappEndpoint, "createAuthRequestWebappEndpoint");
function authStatusWebappEndpoint(router, context, opts) {
if (opts?.enabled === false) {
console.log(`authStatus Webapp endpoint is disabled`);
return;
}
const path = opts?.path ?? "/webapp/auth-status";
router.post(path, (0, import_ssi_express_support2.checkAuth)(opts?.endpoint), async (request, response) => {
try {
console.log("Received auth-status request...");
const correlationId = request.body.correlationId;
const definitionId = request.body.definitionId;
const requestState = correlationId && definitionId ? await context.agent.siopGetAuthRequestState({
correlationId,
definitionId,
errorOnNotFound: false
}) : void 0;
if (!requestState || !definitionId || !correlationId) {
console.log(`No authentication request mapping could be found for the given URL. correlation: ${correlationId}, definitionId: ${definitionId}`);
response.statusCode = 404;
const statusBody2 = {
status: requestState ? requestState.status : "error",
error: "No authentication request mapping could be found for the given URL.",
correlationId,
definitionId,
lastUpdated: requestState ? requestState.lastUpdated : Date.now()
};
return response.json(statusBody2);
}
let includeVerifiedData = import_ssi_sdk2.VerifiedDataMode.NONE;
if ("includeVerifiedData" in request.body) {
includeVerifiedData = request.body.includeVerifiedData;
}
let responseState;
if (requestState.status === "sent") {
responseState = await context.agent.siopGetAuthResponseState({
correlationId,
definitionId,
includeVerifiedData,
errorOnNotFound: false
});
}
const overallState = responseState ?? requestState;
const statusBody = {
status: overallState.status,
...overallState.error ? {
error: overallState.error?.message
} : {},
correlationId,
definitionId,
lastUpdated: overallState.lastUpdated,
...responseState && responseState.status === import_did_auth_siop2.AuthorizationResponseStateStatus.VERIFIED ? {
payload: await responseState.response.mergedPayloads({
hasher: import_ssi_sdk3.shaHasher
}),
verifiedData: responseState.verifiedData
} : {}
};
console.debug(`Will send auth status: ${JSON.stringify(statusBody)}`);
if (overallState.status === "error") {
response.statusCode = 500;
return response.json(statusBody);
}
response.statusCode = 200;
return response.json(statusBody);
} catch (error) {
return (0, import_ssi_express_support2.sendErrorResponse)(response, 500, error.message, error);
}
});
}
__name(authStatusWebappEndpoint, "authStatusWebappEndpoint");
function removeAuthRequestStateWebappEndpoint(router, context, opts) {
if (opts?.enabled === false) {
console.log(`removeAuthStatus Webapp endpoint is disabled`);
return;
}
const path = opts?.path ?? "/webapp/definitions/:definitionId/auth-requests/:correlationId";
router.delete(path, (0, import_ssi_express_support2.checkAuth)(opts?.endpoint), async (request, response) => {
try {
const correlationId = request.params.correlationId;
const definitionId = request.params.definitionId;
if (!correlationId || !definitionId) {
console.log(`No authorization request could be found for the given url. correlationId: ${correlationId}, definitionId: ${definitionId}`);
return (0, import_ssi_express_support2.sendErrorResponse)(response, 404, "No authorization request could be found");
}
response.statusCode = 200;
return response.json(await context.agent.siopDeleteAuthState({
definitionId,
correlationId
}));
} catch (error) {
return (0, import_ssi_express_support2.sendErrorResponse)(response, 500, error.message, error);
}
});
}
__name(removeAuthRequestStateWebappEndpoint, "removeAuthRequestStateWebappEndpoint");
function getDefinitionsEndpoint(router, context, opts) {
if (opts?.enabled === false) {
console.log(`getDefinitions Webapp endpoint is disabled`);
return;
}
const path = opts?.path ?? "/webapp/definitions";
router.get(path, (0, import_ssi_express_support2.checkAuth)(opts?.endpoint), async (request, response) => {
try {
const definitions = await context.agent.pdmGetDefinitions();
response.statusCode = 200;
return response.json(definitions);
} catch (error) {
return (0, import_ssi_express_support2.sendErrorResponse)(response, 500, error.message, error);
}
});
}
__name(getDefinitionsEndpoint, "getDefinitionsEndpoint");
// src/siopv2-rp-api-server.ts
var import_ssi_sdk4 = require("@sphereon/ssi-sdk.core");
var import_ssi_express_support3 = require("@sphereon/ssi-express-support");
var import_express = __toESM(require("express"), 1);
var import_swagger_ui_express = __toESM(require("swagger-ui-express"), 1);
var SIOPv2RPApiServer = class {
static {
__name(this, "SIOPv2RPApiServer");
}
_express;
_router;
_agent;
_opts;
_basePath;
OID4VP_SWAGGER_URL = "https://api.swaggerhub.com/apis/SphereonInt/OID4VP/0.1.0";
constructor(args) {
const { agent, opts } = args;
this._agent = agent;
(0, import_ssi_express_support3.copyGlobalAuthToEndpoints)({
opts,
keys: [
"webappCreateAuthRequest",
"webappAuthStatus",
"webappDeleteAuthRequest"
]
});
if (opts?.endpointOpts?.globalAuth?.secureSiopEndpoints) {
(0, import_ssi_express_support3.copyGlobalAuthToEndpoints)({
opts,
keys: [
"siopGetAuthRequest",
"siopVerifyAuthResponse"
]
});
}
this._opts = opts;
this._express = args.expressSupport.express;
this._router = import_express.default.Router();
const context = (0, import_ssi_sdk4.agentContext)(agent);
const features = opts?.enableFeatures ?? [
"rp-status",
"siop"
];
console.log(`SIOPv2 API enabled, with features: ${JSON.stringify(features)}}`);
if (features.includes("rp-status")) {
createAuthRequestWebappEndpoint(this._router, context, opts?.endpointOpts?.webappCreateAuthRequest);
authStatusWebappEndpoint(this._router, context, opts?.endpointOpts?.webappAuthStatus);
removeAuthRequestStateWebappEndpoint(this._router, context, opts?.endpointOpts?.webappDeleteAuthRequest);
getDefinitionsEndpoint(this._router, context, opts?.endpointOpts?.webappGetDefinitions);
}
if (features.includes("siop")) {
getAuthRequestSIOPv2Endpoint(this._router, context, opts?.endpointOpts?.siopGetAuthRequest);
verifyAuthResponseSIOPv2Endpoint(this._router, context, opts?.endpointOpts?.siopVerifyAuthResponse);
}
this._basePath = opts?.endpointOpts?.basePath ?? "";
this._express.use(this._basePath, this.router);
this._express.set("trust proxy", opts?.endpointOpts?.trustProxy ?? true);
this.setupSwaggerUi();
}
setupSwaggerUi() {
fetch(this.OID4VP_SWAGGER_URL).then((res) => res.json()).then((swagger) => {
const apiDocs = `${this._basePath}/api-docs`;
console.log(`[OID4P] API docs available at ${apiDocs}`);
this._router.use("/api-docs", (req, res, next) => {
const regex = `${apiDocs.replace(/\//, "/")}`.replace("/oid4vp", "").replace(/\/api-docs.*/, "");
swagger.servers = [
{
url: `${req.protocol}://${req.get("host")}${regex}`,
description: "This server"
}
];
req.swaggerDoc = swagger;
next();
}, import_swagger_ui_express.default.serveFiles(swagger, options), import_swagger_ui_express.default.setup());
}).catch((err) => {
console.log(`[OID4VP] Unable to fetch swagger document: ${err}. Will not host api-docs on this instance`);
});
const options = {};
}
get express() {
return this._express;
}
get router() {
return this._router;
}
get agent() {
return this._agent;
}
get opts() {
return this._opts;
}
};
//# sourceMappingURL=index.cjs.map