@sphereon/jarm
Version:
Sphereon JARM
465 lines (452 loc) • 17.4 kB
JavaScript
var __defProp = Object.defineProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
// lib/utils.ts
function appendQueryParams(input) {
const { url: url2, params } = input;
for (const [key, value] of Object.entries(params)) {
url2.searchParams.append(key, encodeURIComponent(value));
}
return url2;
}
__name(appendQueryParams, "appendQueryParams");
function appendFragmentParams(input) {
const { url: url2, fragments } = input;
const fragmentParams = new URLSearchParams(url2.hash.slice(1));
for (const [key, value] of Object.entries(fragments)) {
fragmentParams.append(key, encodeURIComponent(value));
}
url2.hash = fragmentParams.toString();
return url2;
}
__name(appendFragmentParams, "appendFragmentParams");
function assertValueSupported(input) {
const { required, error, supported, actual } = input;
const intersection = supported.find((value) => value === actual);
if (required && !intersection) throw error;
return intersection;
}
__name(assertValueSupported, "assertValueSupported");
// lib/v-response-mode-registry.ts
import * as v from "valibot";
var vJarmResponseMode = v.picklist([
"jwt",
"query.jwt",
"fragment.jwt",
"form_post.jwt"
]);
var vOpenid4vpResponseMode = v.picklist([
"direct_post"
]);
var vOpenid4vpJarmResponseMode = v.picklist([
"direct_post.jwt"
]);
var vResponseMode = v.pipe(v.picklist([
"query",
"fragment",
...vOpenid4vpResponseMode.options,
...vJarmResponseMode.options,
...vOpenid4vpJarmResponseMode.options
]), v.description("Informs the Authorization Server of the mechanism to be used for returning parameters from the Authorization Endpoint."));
var getDisAllowedResponseModes = /* @__PURE__ */ __name((input) => {
const { response_type } = input;
switch (response_type) {
case "code token":
return [
"query"
];
case "code id_token":
return [
"query"
];
case "id_token token":
return [
"query"
];
case "code id_token token":
return [
"query"
];
}
return void 0;
}, "getDisAllowedResponseModes");
var getDefaultResponseMode = /* @__PURE__ */ __name((input) => {
const { response_type } = input;
switch (response_type) {
case "code":
case "none":
return "query";
case "token":
case "id_token":
case "code token":
case "code id_token":
case "id_token token":
case "code id_token token":
case "vp_token":
case "id_token vp_token":
return "fragment";
}
}, "getDefaultResponseMode");
var getJarmDefaultResponseMode = /* @__PURE__ */ __name((input) => {
const responseMode = getDefaultResponseMode(input);
switch (responseMode) {
case "query":
return "query.jwt";
case "fragment":
return "fragment.jwt";
}
}, "getJarmDefaultResponseMode");
var validateResponseMode = /* @__PURE__ */ __name((input) => {
const disallowedResponseModes = getDisAllowedResponseModes(input);
if (disallowedResponseModes?.includes(input.response_mode)) {
throw new Error(`Response_type '${input.response_type}' is not compatible with response_mode '${input.response_mode}'.`);
}
}, "validateResponseMode");
// lib/jarm-auth-response-send/jarm-auth-response-send.ts
var jarmAuthResponseSend = /* @__PURE__ */ __name(async (input) => {
const { authRequestParams, authResponse, state } = input;
const responseEndpoint = "response_uri" in authRequestParams ? new URL(authRequestParams.response_uri) : new URL(authRequestParams.redirect_uri);
const responseMode = authRequestParams.response_mode && authRequestParams.response_mode !== "jwt" ? authRequestParams.response_mode : getJarmDefaultResponseMode(authRequestParams);
validateResponseMode({
response_type: authRequestParams.response_type,
response_mode: responseMode
});
switch (responseMode) {
case "direct_post.jwt":
return handleDirectPostJwt(responseEndpoint, authResponse, state);
case "query.jwt":
return handleQueryJwt(responseEndpoint, authResponse, state);
case "fragment.jwt":
return handleFragmentJwt(responseEndpoint, authResponse, state);
case "form_post.jwt":
throw new Error("Not implemented. form_post.jwt is not yet supported.");
}
}, "jarmAuthResponseSend");
async function handleDirectPostJwt(responseEndpoint, responseJwt, state) {
const response = await fetch(responseEndpoint, {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded"
},
body: `response=${responseJwt}&state=${state}`
});
return response;
}
__name(handleDirectPostJwt, "handleDirectPostJwt");
async function handleQueryJwt(responseEndpoint, responseJwt, state) {
const responseUrl = appendQueryParams({
url: responseEndpoint,
params: {
response: responseJwt,
state
}
});
const response = await fetch(responseUrl, {
method: "POST"
});
return response;
}
__name(handleQueryJwt, "handleQueryJwt");
async function handleFragmentJwt(responseEndpoint, responseJwt, state) {
const responseUrl = appendFragmentParams({
url: responseEndpoint,
fragments: {
response: responseJwt,
state
}
});
const response = await fetch(responseUrl, {
method: "POST"
});
return response;
}
__name(handleFragmentJwt, "handleFragmentJwt");
// lib/jarm-auth-response/c-jarm-auth-response.ts
import * as v3 from "valibot";
// lib/v-response-type-registry.ts
import * as v2 from "valibot";
var oAuthResponseTypes = v2.picklist([
"code",
"token"
]);
var oAuthMRTEPResponseTypes = v2.picklist([
"none",
"id_token",
"code token",
"code id_token",
"id_token token",
"code id_token token"
]);
var openid4vpResponseTypes = v2.picklist([
"vp_token",
"id_token vp_token"
]);
var vTransformedResponseTypes = v2.picklist([
...openid4vpResponseTypes.options,
...oAuthResponseTypes.options,
...oAuthMRTEPResponseTypes.options
]);
var vResponseType = v2.pipe(v2.string(), v2.transform((val) => val.split(" ").sort().join(" ")), vTransformedResponseTypes);
// lib/jarm-auth-response/c-jarm-auth-response.ts
var vAuthRequestParams = v3.looseObject({
state: v3.optional(v3.string()),
response_mode: v3.optional(v3.union([
vJarmResponseMode,
vOpenid4vpJarmResponseMode
])),
client_id: v3.string(),
response_type: vResponseType,
client_metadata: v3.looseObject({
jwks: v3.optional(v3.object({
keys: v3.array(v3.looseObject({
kid: v3.optional(v3.string()),
kty: v3.string()
}))
})),
jwks_uri: v3.optional(v3.string())
})
});
var vOAuthAuthRequestGetParamsOut = v3.object({
authRequestParams: vAuthRequestParams
});
// lib/jarm-auth-response/jarm-auth-response.ts
import { decodeProtectedHeader, isJwe, isJws } from "@sphereon/oid4vc-common";
import * as v6 from "valibot";
// lib/jarm-auth-response/v-jarm-auth-response-params.ts
import { checkExp } from "@sphereon/oid4vc-common";
import * as v4 from "valibot";
var vJarmAuthResponseErrorParams = v4.looseObject({
error: v4.string(),
state: v4.optional(v4.string()),
error_description: v4.pipe(v4.optional(v4.string()), v4.description("Text providing additional information, used to assist the client developer in understanding the error that occurred.")),
error_uri: v4.pipe(v4.optional(v4.pipe(v4.string(), v4.url())), v4.description("A URI identifying a human-readable web page with information about the error, used to provide the client developer with additional information about the error"))
});
var vJarmAuthResponseParams = v4.looseObject({
state: v4.optional(v4.string()),
/**
* The issuer URL of the authorization server that created the response
*/
iss: v4.string(),
/**
* Expiration of the JWT
*/
exp: v4.number(),
/**
* The client_id of the client the response is intended for
*/
aud: v4.string()
});
var validateJarmAuthResponseParams = /* @__PURE__ */ __name((input) => {
const { authRequestParams, authResponseParams } = input;
if (authRequestParams.state !== authResponseParams.state) {
throw new Error(`State missmatch in jarm-auth-response. Expected '${authRequestParams.state}' received '${authRequestParams.state}'.`);
}
if (authRequestParams.client_id !== authResponseParams.aud) {
throw new Error(`Invalid audience in jarm-auth-response. Expected '${authRequestParams.client_id}' received '${authResponseParams.aud}'.`);
}
if (checkExp({
exp: authResponseParams.exp
})) {
throw new Error(`The '${authRequestParams.state}' and the jarm-auth-response.`);
}
}, "validateJarmAuthResponseParams");
// lib/jarm-auth-response/v-jarm-direct-post-jwt-auth-response-params.ts
import * as v5 from "valibot";
var vJarmDirectPostJwtParams = v5.looseObject({
...v5.omit(vJarmAuthResponseParams, [
"iss",
"aud",
"exp"
]).entries,
...v5.partial(v5.pick(vJarmAuthResponseParams, [
"iss",
"aud",
"exp"
])).entries,
vp_token: v5.union([
v5.string(),
v5.array(v5.pipe(v5.string(), v5.nonEmpty()))
]),
dcql_query: v5.unknown(),
nonce: v5.optional(v5.string())
});
var jarmAuthResponseDirectPostValidateParams = /* @__PURE__ */ __name((input) => {
const { authRequestParams, authResponseParams } = input;
if (authRequestParams.state !== authResponseParams.state) {
throw new Error(`State missmatch between auth request '${authRequestParams.state}' and the jarm-auth-response.`);
}
}, "jarmAuthResponseDirectPostValidateParams");
// lib/jarm-auth-response/jarm-auth-response.ts
var parseJarmAuthResponseParams = /* @__PURE__ */ __name((schema, responseParams) => {
if (v6.is(vJarmAuthResponseErrorParams, responseParams)) {
const errorResponseJson = JSON.stringify(responseParams, void 0, 2);
throw new Error(`Received error response from authorization server. '${errorResponseJson}'`);
}
return v6.parse(schema, responseParams);
}, "parseJarmAuthResponseParams");
var decryptJarmAuthResponse = /* @__PURE__ */ __name(async (input, ctx) => {
const { response } = input;
const responseProtectedHeader = decodeProtectedHeader(response);
if (!responseProtectedHeader.kid) {
throw new Error(`Jarm JWE is missing the protected header field 'kid'.`);
}
const { plaintext } = await ctx.jwe.decryptCompact({
jwe: response,
jwk: {
kid: responseProtectedHeader.kid
}
});
return plaintext;
}, "decryptJarmAuthResponse");
var jarmAuthResponseDirectPostJwtValidate = /* @__PURE__ */ __name(async (input, ctx) => {
const { response } = input;
const responseIsEncrypted = isJwe(response);
const decryptedResponse = responseIsEncrypted ? await decryptJarmAuthResponse(input, ctx) : response;
const responseIsSigned = isJws(decryptedResponse);
if (!responseIsEncrypted && !responseIsSigned) {
throw new Error("Jarm Auth Response must be either encrypted, signed, or signed and encrypted.");
}
let authResponseParams;
let authRequestParams;
if (responseIsSigned) {
throw new Error("Signed JARM responses are not supported.");
} else {
const jsonResponse = JSON.parse(decryptedResponse);
authResponseParams = parseJarmAuthResponseParams(vJarmDirectPostJwtParams, jsonResponse);
({ authRequestParams } = await ctx.openid4vp.authRequest.getParams(authResponseParams));
}
jarmAuthResponseDirectPostValidateParams({
authRequestParams,
authResponseParams
});
let type;
if (responseIsSigned && responseIsEncrypted) type = "signed encrypted";
else if (responseIsEncrypted) type = "encrypted";
else type = "signed";
return {
authRequestParams,
authResponseParams,
type
};
}, "jarmAuthResponseDirectPostJwtValidate");
// lib/metadata/v-jarm-client-metadata.ts
import * as v7 from "valibot";
var vJarmClientMetadataSign = v7.object({
authorization_signed_response_alg: v7.pipe(v7.optional(v7.string()), v7.description("JWA. If this is specified, the response will be signed using JWS and the configured algorithm. The algorithm none is not allowed.")),
authorization_encrypted_response_alg: v7.optional(v7.never()),
authorization_encrypted_response_enc: v7.optional(v7.never())
});
var vJarmClientMetadataEncrypt = v7.object({
authorization_signed_response_alg: v7.optional(v7.never()),
authorization_encrypted_response_alg: v7.pipe(v7.string(), v7.description("JWE alg algorithm JWA. If both signing and encryption are requested, the response will be signed then encrypted with the provided algorithm.")),
authorization_encrypted_response_enc: v7.pipe(v7.optional(v7.string(), "A128CBC-HS256"), v7.description("JWE enc algorithm JWA. If both signing and encryption are requested, the response will be signed then encrypted with the provided algorithm."))
});
var vJarmClientMetadataSignEncrypt = v7.object({
...v7.pick(vJarmClientMetadataSign, [
"authorization_signed_response_alg"
]).entries,
...v7.pick(vJarmClientMetadataEncrypt, [
"authorization_encrypted_response_alg",
"authorization_encrypted_response_enc"
]).entries
});
var vJarmClientMetadata = v7.union([
vJarmClientMetadataSign,
vJarmClientMetadataEncrypt,
vJarmClientMetadataSignEncrypt
]);
// lib/metadata/v-jarm-server-metadata.ts
import * as v8 from "valibot";
var vJarmServerMetadata = v8.object({
authorization_signing_alg_values_supported: v8.pipe(v8.array(v8.string()), v8.description("JSON array containing a list of the JWS [RFC7515] signing algorithms (alg values) JWA [RFC7518] supported by the authorization endpoint to sign the response.")),
authorization_encryption_alg_values_supported: v8.pipe(v8.array(v8.string()), v8.description("JSON array containing a list of the JWE [RFC7516] encryption algorithms (alg values) JWA [RFC7518] supported by the authorization endpoint to encrypt the response.")),
authorization_encryption_enc_values_supported: v8.pipe(v8.array(v8.string()), v8.description("JSON array containing a list of the JWE [RFC7516] encryption algorithms (enc values) JWA [RFC7518] supported by the authorization endpoint to encrypt the response."))
});
// lib/metadata/jarm-validate-metadata.ts
import * as v9 from "valibot";
var vJarmAuthResponseValidateMetadataInput = v9.object({
client_metadata: vJarmClientMetadata,
server_metadata: v9.partial(vJarmServerMetadata)
});
var vJarmMetadataValidateOut = v9.variant("type", [
v9.object({
type: v9.literal("signed"),
client_metadata: vJarmClientMetadataSign
}),
v9.object({
type: v9.literal("encrypted"),
client_metadata: vJarmClientMetadataEncrypt
}),
v9.object({
type: v9.literal("signed encrypted"),
client_metadata: vJarmClientMetadataSignEncrypt
})
]);
var jarmMetadataValidate = /* @__PURE__ */ __name((vJarmMetadataValidate) => {
const { client_metadata, server_metadata } = vJarmMetadataValidate;
const { authorization_encrypted_response_alg, authorization_encrypted_response_enc, authorization_signed_response_alg } = client_metadata;
assertValueSupported({
supported: server_metadata.authorization_signing_alg_values_supported ?? [],
actual: authorization_signed_response_alg,
required: !!authorization_signed_response_alg,
error: new Error("Invalid authorization_signed_response_alg")
});
assertValueSupported({
supported: server_metadata.authorization_encryption_alg_values_supported ?? [],
actual: authorization_encrypted_response_alg,
required: !!authorization_encrypted_response_alg,
error: new Error("Invalid authorization_encrypted_response_alg")
});
assertValueSupported({
supported: server_metadata.authorization_encryption_enc_values_supported ?? [],
actual: authorization_encrypted_response_enc,
required: !!authorization_encrypted_response_enc,
error: new Error("Invalid authorization_encrypted_response_enc")
});
if (authorization_signed_response_alg && authorization_encrypted_response_alg && authorization_encrypted_response_enc) {
return {
type: "signed encrypted",
client_metadata: {
authorization_signed_response_alg,
authorization_encrypted_response_alg,
authorization_encrypted_response_enc
}
};
} else if (authorization_signed_response_alg && !authorization_encrypted_response_alg && !authorization_encrypted_response_enc) {
return {
type: "signed",
client_metadata: {
authorization_signed_response_alg
}
};
} else if (!authorization_signed_response_alg && authorization_encrypted_response_alg && authorization_encrypted_response_enc) {
return {
type: "encrypted",
client_metadata: {
authorization_encrypted_response_alg,
authorization_encrypted_response_enc
}
};
} else {
throw new Error(`Invalid jarm client_metadata combination`);
}
}, "jarmMetadataValidate");
export {
jarmAuthResponseDirectPostJwtValidate,
jarmAuthResponseDirectPostValidateParams,
jarmAuthResponseSend,
jarmMetadataValidate,
vAuthRequestParams,
vJarmAuthResponseErrorParams,
vJarmAuthResponseParams,
vJarmAuthResponseValidateMetadataInput,
vJarmClientMetadata,
vJarmClientMetadataEncrypt,
vJarmClientMetadataSign,
vJarmClientMetadataSignEncrypt,
vJarmDirectPostJwtParams,
vJarmMetadataValidateOut,
vJarmServerMetadata,
vOAuthAuthRequestGetParamsOut,
validateJarmAuthResponseParams
};
//# sourceMappingURL=index.js.map