UNPKG

@sphereon/jarm

Version:

Sphereon JARM

465 lines (452 loc) 17.4 kB
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