From d76efe4c87d329b8e5f283209a682d9856a4f85d Mon Sep 17 00:00:00 2001 From: Martin Auer Date: Mon, 30 Sep 2024 13:44:23 +0200 Subject: [PATCH] fix: things --- packages/jarm/src/index.ts | 1 + .../src/jarm-auth-response-create/index.ts | 1 + .../jarm-auth-response-create.ts | 78 +++++++++++ .../c-jarm-auth-response-send.ts | 7 - .../jarm-auth-response-send.ts | 127 +++++------------- packages/jarm/src/jarm-auth-response/index.ts | 5 +- ...nse.ts => jarm-auth-response-encrypted.ts} | 79 +++++++---- .../jarm-auth-response.fixtures.ts | 2 +- .../jarm-auth-response.test.ts | 69 +++++----- ...e.ts => v-jarm-auth-response-encrypted.ts} | 10 +- ...arm-auth-response.ts => v-auth-request.ts} | 24 +--- packages/jose/src/jwe/c-jwe.ts | 102 ++++++++------ packages/jose/src/jwk/c-jwk.ts | 22 +-- packages/jose/src/jws/c-jws.ts | 100 ++++++++------ tooling/eslint/base.js | 2 +- 15 files changed, 343 insertions(+), 286 deletions(-) create mode 100644 packages/jarm/src/jarm-auth-response-create/index.ts create mode 100644 packages/jarm/src/jarm-auth-response-create/jarm-auth-response-create.ts delete mode 100644 packages/jarm/src/jarm-auth-response-send/c-jarm-auth-response-send.ts rename packages/jarm/src/jarm-auth-response/{jarm-auth-response.ts => jarm-auth-response-encrypted.ts} (62%) rename packages/jarm/src/jarm-auth-response/{v-jarm-direct-post-jwt-auth-response.ts => v-jarm-auth-response-encrypted.ts} (77%) rename packages/jarm/src/{jarm-auth-response/c-jarm-auth-response.ts => v-auth-request.ts} (54%) diff --git a/packages/jarm/src/index.ts b/packages/jarm/src/index.ts index da75c97..de45120 100644 --- a/packages/jarm/src/index.ts +++ b/packages/jarm/src/index.ts @@ -1,3 +1,4 @@ +export * from './jarm-auth-response-create/index.js'; export * from './jarm-auth-response-send/index.js'; export * from './jarm-auth-response/index.js'; export * from './metadata/index.js'; diff --git a/packages/jarm/src/jarm-auth-response-create/index.ts b/packages/jarm/src/jarm-auth-response-create/index.ts new file mode 100644 index 0000000..3740f8e --- /dev/null +++ b/packages/jarm/src/jarm-auth-response-create/index.ts @@ -0,0 +1 @@ +export * from './jarm-auth-response-create.js'; diff --git a/packages/jarm/src/jarm-auth-response-create/jarm-auth-response-create.ts b/packages/jarm/src/jarm-auth-response-create/jarm-auth-response-create.ts new file mode 100644 index 0000000..d308402 --- /dev/null +++ b/packages/jarm/src/jarm-auth-response-create/jarm-auth-response-create.ts @@ -0,0 +1,78 @@ +import type { PickDeep } from '@protokoll/core'; +import * as v from 'valibot'; + +import type { JoseContext } from '@protokoll/jose'; +import { + JoseJweEncryptCompact, + JoseJweEncryptJwt, + JoseJwsSignJwt, +} from '@protokoll/jose'; + +import { vJarmAuthResponseEncrypted as vJarmEncryptedOnlyAuthResponse } from '../jarm-auth-response/v-jarm-auth-response-encrypted.js'; +import { vJarmAuthResponse } from '../jarm-auth-response/v-jarm-auth-response.js'; + +export namespace JarmAuthResponseCreate { + export const vInput = v.variant('type', [ + v.object({ + type: v.literal('signed'), + authResponse: vJarmAuthResponse, + jwsSignJwtInput: v.omit(JoseJwsSignJwt.vInput, ['payload']), + }), + v.object({ + type: v.literal('encrypted'), + authResponse: vJarmEncryptedOnlyAuthResponse, + jweEncryptJwtInput: v.omit(JoseJweEncryptJwt.vInput, ['payload']), + }), + v.object({ + type: v.literal('signed encrypted'), + authResponse: vJarmAuthResponse, + jwsSignJwtInput: v.omit(JoseJwsSignJwt.vInput, ['payload']), + jweEncryptCompactInput: v.omit(JoseJweEncryptCompact.vInput, [ + 'plaintext', + ]), + }), + ]); + export type Input = v.InferOutput; + + export const vOut = v.object({ + authResponse: v.string(), + }); + + export type Out = v.InferOutput; + + export type Context = PickDeep< + JoseContext, + 'jose.jwe.encryptJwt' | 'jose.jws.signJwt' | 'jose.jwe.encryptCompact' + >; +} + +export const jarmAuthResponseCreate = async ( + input: JarmAuthResponseCreate.Input, + ctx: JarmAuthResponseCreate.Context +): Promise => { + const { type, authResponse } = input; + if (input.type === 'encrypted') { + const { jwe } = await ctx.jose.jwe.encryptJwt({ + ...input.jweEncryptJwtInput, + payload: authResponse, + }); + return { authResponse: jwe }; + } else if (type === 'signed') { + const { jws } = await ctx.jose.jws.signJwt({ + ...input.jwsSignJwtInput, + payload: authResponse, + }); + return { authResponse: jws }; + } else { + const { jws } = await ctx.jose.jws.signJwt({ + ...input.jwsSignJwtInput, + payload: authResponse, + }); + const { jwe } = await ctx.jose.jwe.encryptCompact({ + ...input.jweEncryptCompactInput, + plaintext: jws, + }); + + return { authResponse: jwe }; + } +}; diff --git a/packages/jarm/src/jarm-auth-response-send/c-jarm-auth-response-send.ts b/packages/jarm/src/jarm-auth-response-send/c-jarm-auth-response-send.ts deleted file mode 100644 index 830fec8..0000000 --- a/packages/jarm/src/jarm-auth-response-send/c-jarm-auth-response-send.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type { PickDeep } from '@protokoll/core'; -import type { JoseContext } from '@protokoll/jose'; - -export type JarmAuthResponseCreateContext = PickDeep< - JoseContext, - 'jose.jwe.encryptJwt' | 'jose.jws.signJwt' | 'jose.jwe.encryptCompact' ->; diff --git a/packages/jarm/src/jarm-auth-response-send/jarm-auth-response-send.ts b/packages/jarm/src/jarm-auth-response-send/jarm-auth-response-send.ts index d9d99ee..e4d48ae 100644 --- a/packages/jarm/src/jarm-auth-response-send/jarm-auth-response-send.ts +++ b/packages/jarm/src/jarm-auth-response-send/jarm-auth-response-send.ts @@ -5,17 +5,9 @@ import { } from '@protokoll/core'; import * as v from 'valibot'; -import { - vJoseJweEncryptCompactInput, - vJoseJweEncryptJwtInput, - vJoseJwsSignJwtInput, - vJwe, - vJws, -} from '@protokoll/jose'; +import { vJwe, vJws } from '@protokoll/jose'; import { JarmError } from '../e-jarm.js'; -import { vJarmAuthResponse } from '../jarm-auth-response/v-jarm-auth-response'; -import { vJarmEncrytedOnlyAuthResponse as vJarmEncryptedOnlyAuthResponse } from '../jarm-auth-response/v-jarm-direct-post-jwt-auth-response.js'; import { getJarmDefaultResponseMode, validateResponseMode, @@ -23,96 +15,41 @@ import { vOpenid4vpJarmResponseMode, } from '../v-response-mode-registry.js'; import { vResponseType } from '../v-response-type-registry.js'; -import type { JarmAuthResponseCreateContext } from './c-jarm-auth-response-send.js'; - -export const vJarmAuthResponseCreateInput = v.variant('type', [ - v.object({ - type: v.literal('signed'), - authResponse: vJarmAuthResponse, - jwsSignJwtInput: v.omit(vJoseJwsSignJwtInput, ['payload']), - }), - v.object({ - type: v.literal('encrypted'), - authResponse: vJarmEncryptedOnlyAuthResponse, - jweEncryptJwtInput: v.omit(vJoseJweEncryptJwtInput, ['payload']), - }), - v.object({ - type: v.literal('signed encrypted'), - authResponse: vJarmAuthResponse, - jwsSignJwtInput: v.omit(vJoseJwsSignJwtInput, ['payload']), - jweEncryptCompactInput: v.omit(vJoseJweEncryptCompactInput, ['plaintext']), - }), -]); - -export type JarmAuthResponseCreateInput = v.InferOutput< - typeof vJarmAuthResponseCreateInput ->; -export const jarmAuthResponseCreate = async ( - input: JarmAuthResponseCreateInput, - ctx: JarmAuthResponseCreateContext -) => { - const { type, authResponse } = input; - if (input.type === 'encrypted') { - const { jwe } = await ctx.jose.jwe.encryptJwt({ - ...input.jweEncryptJwtInput, - payload: authResponse, - }); - return { authResponse: jwe }; - } else if (type === 'signed') { - const { jws } = await ctx.jose.jws.signJwt({ - ...input.jwsSignJwtInput, - payload: authResponse, - }); - return { authResponse: jws }; - } else { - const { jws } = await ctx.jose.jws.signJwt({ - ...input.jwsSignJwtInput, - payload: authResponse, - }); - const { jwe } = await ctx.jose.jwe.encryptCompact({ - ...input.jweEncryptCompactInput, - plaintext: jws, - }); - - return { authResponse: jwe }; - } -}; - -export const vJarmAuthResponseSendInput = v.object({ - authRequest: v.intersect([ - v.object({ - response_mode: v.optional( - v.union([vJarmResponseMode, vOpenid4vpJarmResponseMode]) - ), - response_type: vResponseType, - }), - v.union([ - v.looseObject({ - response_uri: v.string(), - redirect_uri: v.optional(v.never()), - }), - v.looseObject({ - redirect_uri: v.string(), - response_uri: v.optional(v.never()), +export namespace JarmAuthResponseSend { + export const vInput = v.object({ + authRequest: v.intersect([ + v.object({ + response_mode: v.optional( + v.union([vJarmResponseMode, vOpenid4vpJarmResponseMode]) + ), + response_type: vResponseType, }), + v.union([ + v.object({ + response_uri: v.string(), + redirect_uri: v.optional(v.never()), + }), + v.object({ + response_uri: v.optional(v.never()), + redirect_uri: v.string(), + }), + ]), ]), - ]), - authResponse: v.union([vJwe, vJws]), -}); -export type JarmAuthResponseSendInput = v.InferOutput< - typeof vJarmAuthResponseSendInput ->; + authResponse: v.union([vJwe, vJws]), + }); + export type Input = v.InferOutput; + + export type Out = Response; +} export const jarmAuthResponseSend = async ( - input: JarmAuthResponseSendInput -): Promise => { + input: JarmAuthResponseSend.Input +): Promise => { const { authRequest, authResponse } = input; - const responseEndpoint = authRequest.response_uri - ? new URL(authRequest.response_uri) - : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - new URL(authRequest.redirect_uri!); + const responseEndpoint = authRequest.response_uri ?? authRequest.redirect_uri; + const responseEndpointUrl = new URL(responseEndpoint); const responseMode = authRequest.response_mode && authRequest.response_mode !== 'jwt' @@ -126,11 +63,11 @@ export const jarmAuthResponseSend = async ( switch (responseMode) { case 'direct_post.jwt': - return handleDirectPostJwt(responseEndpoint, authResponse); + return handleDirectPostJwt(responseEndpointUrl, authResponse); case 'query.jwt': - return handleQueryJwt(responseEndpoint, authResponse); + return handleQueryJwt(responseEndpointUrl, authResponse); case 'fragment.jwt': - return handleFragmentJwt(responseEndpoint, authResponse); + return handleFragmentJwt(responseEndpointUrl, authResponse); case 'form_post.jwt': return NOT_IMPLEMENTED({ message: 'form_post.jwt', diff --git a/packages/jarm/src/jarm-auth-response/index.ts b/packages/jarm/src/jarm-auth-response/index.ts index 1a58042..ddd6f2b 100644 --- a/packages/jarm/src/jarm-auth-response/index.ts +++ b/packages/jarm/src/jarm-auth-response/index.ts @@ -1,4 +1,3 @@ -export * from './c-jarm-auth-response.js'; -export * from './jarm-auth-response.js'; +export * from './jarm-auth-response-encrypted.js'; +export * from './v-jarm-auth-response-encrypted.js'; export * from './v-jarm-auth-response.js'; -export * from './v-jarm-direct-post-jwt-auth-response.js'; diff --git a/packages/jarm/src/jarm-auth-response/jarm-auth-response.ts b/packages/jarm/src/jarm-auth-response/jarm-auth-response-encrypted.ts similarity index 62% rename from packages/jarm/src/jarm-auth-response/jarm-auth-response.ts rename to packages/jarm/src/jarm-auth-response/jarm-auth-response-encrypted.ts index d0fcb55..60e6a29 100644 --- a/packages/jarm/src/jarm-auth-response/jarm-auth-response.ts +++ b/packages/jarm/src/jarm-auth-response/jarm-auth-response-encrypted.ts @@ -1,5 +1,6 @@ import * as v from 'valibot'; +import type { JoseContext } from '@protokoll/jose'; import { decodeJwt, decodeProtectedHeader, @@ -7,26 +8,50 @@ import { isJws, } from '@protokoll/jose'; +import type { MaybePromise, PickDeep } from '@protokoll/core'; import { JarmAuthResponseValidationError, JarmReceivedErrorResponse, } from '../e-jarm.js'; -import type { JarmDirectPostJwtResponse } from '../index.js'; -import type { - AuthRequest, - JarmDirectPostJwtAuthResponseValidationContext, -} from './c-jarm-auth-response.js'; -import { vJarmAuthResponseError } from './v-jarm-auth-response.js'; +import type { JarmAuthResponse, JarmAuthResponseEncrypted } from '../index.js'; +import type { OAuthAuthRequestGetParamsOut } from '../v-auth-request.js'; +import { vAuthRequest } from '../v-auth-request.js'; import { - jarmAuthResponseEncryptionOnlyValidate, - vJarmEncrytedOnlyAuthResponse, -} from './v-jarm-direct-post-jwt-auth-response.js'; - -export interface JarmDirectPostJwtAuthResponseValidation { - /** - * The JARM response parameter conveyed either as url query param, fragment param, or application/x-www-form-urlencoded in the body of a post request - */ - response: string; + jarmAuthResponseEncryptedValidate, + vJarmAuthResponseEncrypted, + vJarmAuthResponseEncrypted as vJarmEncryptedOnlyAuthResponse, +} from './v-jarm-auth-response-encrypted.js'; +import { vJarmAuthResponseError } from './v-jarm-auth-response.js'; + +export namespace JarmAuthResponseEncryptedHandle { + export const vInput = v.object({ + /** + * The JARM response parameter conveyed either as url query param, fragment param, or application/x-www-form-urlencoded in the body of a post request + */ + response: v.string(), + }); + export type Input = v.InferOutput; + + export const vOut = v.object({ + authRequest: vAuthRequest, + authResponse: vJarmAuthResponseEncrypted, + type: v.picklist(['signed encrypted', 'encrypted', 'signed']), + }); + export type Out = v.InferOutput; + + export interface Context + extends PickDeep< + JoseContext, + 'jose.jwe.decryptCompact' | 'jose.jws.verifyJwt' + > { + openid4vp: { + authRequest: { + get: ( + input: JarmAuthResponse | JarmAuthResponseEncrypted + ) => MaybePromise; + }; + }; + } } const parseJarmAuthResponse = < @@ -48,7 +73,7 @@ const parseJarmAuthResponse = < const decryptJarmAuthResponse = async ( input: { response: string }, - ctx: JarmDirectPostJwtAuthResponseValidationContext + ctx: JarmAuthResponseEncryptedHandle.Context ) => { const { response } = input; @@ -72,10 +97,10 @@ const decryptJarmAuthResponse = async ( * * The decryption key should be resolvable using the the protected header's 'kid' field * * The signature verification jwk should be resolvable using the jws protected header's 'kid' field and the payload's 'iss' field. */ -export const jarmAuthResponseDirectPostJwtValidate = async ( - input: JarmDirectPostJwtAuthResponseValidation, - ctx: JarmDirectPostJwtAuthResponseValidationContext -) => { +export const jarmAuthResponseEncryptedHandle = async ( + input: JarmAuthResponseEncryptedHandle.Input, + ctx: JarmAuthResponseEncryptedHandle.Context +): Promise => { const { response } = input; const responseIsEncrypted = isJwe(response); @@ -91,14 +116,13 @@ export const jarmAuthResponseDirectPostJwtValidate = async ( }); } - let authResponse: JarmDirectPostJwtResponse; - let authRequest: AuthRequest; + let authResponse: JarmAuthResponseEncrypted; + let authRequest: v.InferOutput; if (responseIsSigned) { const jwsProtectedHeader = decodeProtectedHeader(decryptedResponse); const jwsPayload = decodeJwt(decryptedResponse); - - const schema = v.required(vJarmEncrytedOnlyAuthResponse, [ + const schema = v.required(vJarmEncryptedOnlyAuthResponse, [ 'iss', 'aud', 'exp', @@ -120,16 +144,13 @@ export const jarmAuthResponseDirectPostJwtValidate = async ( } else { const jsonResponse: unknown = JSON.parse(decryptedResponse); authResponse = parseJarmAuthResponse( - vJarmEncrytedOnlyAuthResponse, + vJarmEncryptedOnlyAuthResponse, jsonResponse ); ({ authRequest } = await ctx.openid4vp.authRequest.get(authResponse)); } - jarmAuthResponseEncryptionOnlyValidate({ - authRequest: authRequest, - authResponse: authResponse, - }); + jarmAuthResponseEncryptedValidate({ authRequest, authResponse }); let type: 'signed encrypted' | 'encrypted' | 'signed'; if (responseIsSigned && responseIsEncrypted) type = 'signed encrypted'; diff --git a/packages/jarm/src/jarm-auth-response/jarm-auth-response.fixtures.ts b/packages/jarm/src/jarm-auth-response/jarm-auth-response.fixtures.ts index 126c98b..864b7f4 100644 --- a/packages/jarm/src/jarm-auth-response/jarm-auth-response.fixtures.ts +++ b/packages/jarm/src/jarm-auth-response/jarm-auth-response.fixtures.ts @@ -1,4 +1,4 @@ -import type { AuthRequest } from './c-jarm-auth-response.js'; +import type { AuthRequest } from '../v-auth-request'; // ISO-compliant driving licence — Part 7: Mobile driving licence (mDL) add-on functions export const ISO_MDL_7_EPHEMERAL_MDOC_PUBLIC_KEY_JWK = { diff --git a/packages/jarm/src/jarm-auth-response/jarm-auth-response.test.ts b/packages/jarm/src/jarm-auth-response/jarm-auth-response.test.ts index ffbde5e..1207497 100644 --- a/packages/jarm/src/jarm-auth-response/jarm-auth-response.test.ts +++ b/packages/jarm/src/jarm-auth-response/jarm-auth-response.test.ts @@ -2,15 +2,15 @@ import { http, HttpResponse } from 'msw'; import assert from 'node:assert'; import { describe, it } from 'node:test'; -import type { Jwk } from '@protokoll/jose'; +import type { JoseJweDecryptCompact, Jwk } from '@protokoll/jose'; import { joseContext } from '@protokoll/jose/dist/src/u-jose-test-context.js'; import { setupServer } from 'msw/node'; -import type { JarmAuthResponseCreateContext } from '../jarm-auth-response-send/c-jarm-auth-response-send.js'; -import { - jarmAuthResponseCreate, - jarmAuthResponseSend, -} from '../jarm-auth-response-send/jarm-auth-response-send.js'; -import type { JarmDirectPostJwtAuthResponseValidationContext } from './c-jarm-auth-response.js'; +import type { JoseJwsVerifyJwt } from '../../../jose/dist/src/jws/c-jws'; +import type { JarmAuthResponseCreate } from '../jarm-auth-response-create/index.js'; +import { jarmAuthResponseCreate } from '../jarm-auth-response-create/index.js'; +import { jarmAuthResponseSend } from '../jarm-auth-response-send/jarm-auth-response-send.js'; +import type { JarmAuthResponseEncryptedHandle } from './jarm-auth-response-encrypted.js'; +import { jarmAuthResponseEncryptedHandle } from './jarm-auth-response-encrypted.js'; import { EXAMPLE_RP_P256_PRIVATE_KEY_JWK, ISO_MDL_7_EPHEMERAL_READER_PRIVATE_KEY_JWK, @@ -19,9 +19,8 @@ import { ISO_MDL_7_JARM_AUTH_RESPONSE, ISO_MDL_7_JARM_AUTH_RESPONSE_JWT, } from './jarm-auth-response.fixtures.js'; -import { jarmAuthResponseDirectPostJwtValidate } from './jarm-auth-response.js'; -const jarmAuthResponseCreateContext: JarmAuthResponseCreateContext = { +const jarmAuthResponseCreateContext: JarmAuthResponseCreate.Context = { jose: { jwe: { encryptJwt: joseContext.jose.jwe.encryptJwt, @@ -31,22 +30,21 @@ const jarmAuthResponseCreateContext: JarmAuthResponseCreateContext = { }, }; -export const decryptCompact: typeof joseContext.jose.jwe.decryptCompact = - async input => { - const { jwe, jwk: jwkToResolve } = input; - let jwk: Jwk; - if ( - jwkToResolve.kty === 'auto' && - jwkToResolve.kid === ISO_MDL_7_EPHEMERAL_READER_PRIVATE_KEY_JWK.kid - ) { - jwk = ISO_MDL_7_EPHEMERAL_READER_PRIVATE_KEY_JWK; - } else { - throw new Error('Received an invalid jwk.'); - } - return await joseContext.jose.jwe.decryptCompact({ jwe, jwk }); - }; +export const decryptCompact: JoseJweDecryptCompact = async input => { + const { jwe, jwk: jwkToResolve } = input; + let jwk: Jwk; + if ( + jwkToResolve.kty === 'auto' && + jwkToResolve.kid === ISO_MDL_7_EPHEMERAL_READER_PRIVATE_KEY_JWK.kid + ) { + jwk = ISO_MDL_7_EPHEMERAL_READER_PRIVATE_KEY_JWK; + } else { + throw new Error('Received an invalid jwk.'); + } + return await joseContext.jose.jwe.decryptCompact({ jwe, jwk }); +}; -export const verifyJwt: typeof joseContext.jose.jws.verifyJwt = async input => { +export const verifyJwt: JoseJwsVerifyJwt = async input => { const { jws, jwk: jwkToResolve } = input; let jwk: Jwk; if ( @@ -60,7 +58,7 @@ export const verifyJwt: typeof joseContext.jose.jws.verifyJwt = async input => { return await joseContext.jose.jws.verifyJwt({ jws, jwk }); }; -const jarmAuthResponseDirectPostJwtValidationContext: JarmDirectPostJwtAuthResponseValidationContext = +const jarmAuthResponseEncryptedHandleContext: JarmAuthResponseEncryptedHandle.Context = { openid4vp: { authRequest: { @@ -107,9 +105,9 @@ void describe('Jarm Auth Response', () => { assert(response); assert.equal(response, authResponse); - const validatedResponse = await jarmAuthResponseDirectPostJwtValidate( + const validatedResponse = await jarmAuthResponseEncryptedHandle( { response }, - jarmAuthResponseDirectPostJwtValidationContext + jarmAuthResponseEncryptedHandleContext ); assert.deepEqual( @@ -171,9 +169,9 @@ void describe('Jarm Auth Response', () => { assert(response); assert.equal(response, authResponse); - const validatedResponse = await jarmAuthResponseDirectPostJwtValidate( + const validatedResponse = await jarmAuthResponseEncryptedHandle( { response }, - jarmAuthResponseDirectPostJwtValidationContext + jarmAuthResponseEncryptedHandleContext ); assert.deepEqual(validatedResponse.type, 'signed'); @@ -238,9 +236,9 @@ void describe('Jarm Auth Response', () => { assert(response); assert.equal(response, authResponse); - const validatedResponse = await jarmAuthResponseDirectPostJwtValidate( + const validatedResponse = await jarmAuthResponseEncryptedHandle( { response }, - jarmAuthResponseDirectPostJwtValidationContext + jarmAuthResponseEncryptedHandleContext ); assert.deepEqual(validatedResponse.type, 'signed encrypted'); @@ -260,11 +258,10 @@ void describe('Jarm Auth Response', () => { }); void it(`'ISO_MDL_7_JARM_AUTH_RESPONSE' can be validated`, async () => { - const { authRequest, authResponse } = - await jarmAuthResponseDirectPostJwtValidate( - { response: ISO_MDL_7_JARM_AUTH_RESPONSE_JWT }, - jarmAuthResponseDirectPostJwtValidationContext - ); + const { authRequest, authResponse } = await jarmAuthResponseEncryptedHandle( + { response: ISO_MDL_7_JARM_AUTH_RESPONSE_JWT }, + jarmAuthResponseEncryptedHandleContext + ); assert.deepEqual(ISO_MDL_7_JARM_AUTH_RESPONSE, authResponse); assert.deepEqual(authRequest, authRequest); diff --git a/packages/jarm/src/jarm-auth-response/v-jarm-direct-post-jwt-auth-response.ts b/packages/jarm/src/jarm-auth-response/v-jarm-auth-response-encrypted.ts similarity index 77% rename from packages/jarm/src/jarm-auth-response/v-jarm-direct-post-jwt-auth-response.ts rename to packages/jarm/src/jarm-auth-response/v-jarm-auth-response-encrypted.ts index 779324e..49c4fb5 100644 --- a/packages/jarm/src/jarm-auth-response/v-jarm-direct-post-jwt-auth-response.ts +++ b/packages/jarm/src/jarm-auth-response/v-jarm-auth-response-encrypted.ts @@ -3,7 +3,7 @@ import * as v from 'valibot'; import { JarmAuthResponseValidationError } from '../e-jarm.js'; import { vJarmAuthResponse } from './v-jarm-auth-response.js'; -export const vJarmEncrytedOnlyAuthResponse = v.looseObject({ +export const vJarmAuthResponseEncrypted = v.looseObject({ ...v.omit(vJarmAuthResponse, ['iss', 'aud', 'exp']).entries, ...v.partial(v.pick(vJarmAuthResponse, ['iss', 'aud', 'exp'])).entries, @@ -12,13 +12,13 @@ export const vJarmEncrytedOnlyAuthResponse = v.looseObject({ nonce: v.optional(v.string()), }); -export type JarmDirectPostJwtResponse = v.InferInput< - typeof vJarmEncrytedOnlyAuthResponse +export type JarmAuthResponseEncrypted = v.InferInput< + typeof vJarmAuthResponseEncrypted >; -export const jarmAuthResponseEncryptionOnlyValidate = (input: { +export const jarmAuthResponseEncryptedValidate = (input: { authRequest: { state?: string }; - authResponse: JarmDirectPostJwtResponse; + authResponse: JarmAuthResponseEncrypted; }) => { const { authRequest, authResponse } = input; diff --git a/packages/jarm/src/jarm-auth-response/c-jarm-auth-response.ts b/packages/jarm/src/v-auth-request.ts similarity index 54% rename from packages/jarm/src/jarm-auth-response/c-jarm-auth-response.ts rename to packages/jarm/src/v-auth-request.ts index fd10510..f220075 100644 --- a/packages/jarm/src/jarm-auth-response/c-jarm-auth-response.ts +++ b/packages/jarm/src/v-auth-request.ts @@ -1,15 +1,9 @@ import * as v from 'valibot'; - -import type { MaybePromise, PickDeep } from '@protokoll/core'; - -import type { JoseContext } from '@protokoll/jose'; import { vJarmResponseMode, vOpenid4vpJarmResponseMode, -} from '../v-response-mode-registry.js'; -import { vResponseType } from '../v-response-type-registry.js'; -import type { JarmAuthResponse } from './v-jarm-auth-response.js'; -import type { JarmDirectPostJwtResponse } from './v-jarm-direct-post-jwt-auth-response.js'; +} from './v-response-mode-registry'; +import { vResponseType } from './v-response-type-registry'; export const vAuthRequest = v.looseObject({ state: v.optional(v.string()), @@ -41,17 +35,3 @@ export const vOAuthAuthRequestGetParamsOut = v.object({ export type OAuthAuthRequestGetParamsOut = v.InferOutput< typeof vOAuthAuthRequestGetParamsOut >; - -export interface JarmDirectPostJwtAuthResponseValidationContext - extends PickDeep< - JoseContext, - 'jose.jwe.decryptCompact' | 'jose.jws.verifyJwt' - > { - openid4vp: { - authRequest: { - get: ( - input: JarmAuthResponse | JarmDirectPostJwtResponse - ) => MaybePromise; - }; - }; -} diff --git a/packages/jose/src/jwe/c-jwe.ts b/packages/jose/src/jwe/c-jwe.ts index 9c5dc80..1545553 100644 --- a/packages/jose/src/jwe/c-jwe.ts +++ b/packages/jose/src/jwe/c-jwe.ts @@ -6,53 +6,75 @@ import { vJwk } from '../jwk/v-jwk.js'; import { vJwtPayload } from '../jwt/v-jwt.js'; import { vCompactJweHeader } from './v-jwe.js'; -export const vJoseJweDecryptCompactInput = v.object({ - jwe: v.string(), - jwk: vJwk, -}); -export const vJoseJweDecryptCompactOut = v.object({ - plaintext: v.string(), - protectedHeader: vCompactJweHeader, -}); +export namespace JoseJweDecryptCompact { + export const vInput = v.object({ + jwe: v.string(), + jwk: vJwk, + }); + export type Input = v.InferInput; + + export const vOut = v.object({ + plaintext: v.string(), + protectedHeader: vCompactJweHeader, + }); + export type Out = v.InferOutput; +} + export type JoseJweDecryptCompact = ( - input: v.InferInput -) => MaybePromise>; + input: JoseJweDecryptCompact.Input +) => MaybePromise; -export const vJoseJweEncryptCompactInput = v.object({ - plaintext: v.string(), - jwk: vJwk, - protectedHeader: vCompactJweHeader, - alg: v.optional(v.string()), - keyManagement: v.optional(v.object({ apu: v.string(), apv: v.string() })), -}); -export const vJoseJweEncryptCompactOut = v.object({ jwe: v.string() }); +export namespace JoseJweEncryptCompact { + export const vInput = v.object({ + plaintext: v.string(), + jwk: vJwk, + protectedHeader: vCompactJweHeader, + alg: v.optional(v.string()), + keyManagement: v.optional(v.object({ apu: v.string(), apv: v.string() })), + }); + export type Input = v.InferInput; + + export const vOut = v.object({ jwe: v.string() }); + export type Out = v.InferOutput; +} export type JoseJweEncryptCompact = ( - input: v.InferInput -) => MaybePromise>; + input: JoseJweEncryptCompact.Input +) => MaybePromise; -export const vJoseJweEncryptJwtInput = v.object({ - payload: vJwtPayload, - protectedHeader: vCompactJweHeader, - jwk: vJwk, - alg: v.optional(v.string()), - keyManagement: v.optional(v.object({ apu: v.string(), apv: v.string() })), -}); -export const vJoseJweEncryptJwtOut = v.object({ jwe: v.string() }); +export namespace JoseJweEncryptJwt { + export const vInput = v.object({ + payload: vJwtPayload, + protectedHeader: vCompactJweHeader, + jwk: vJwk, + alg: v.optional(v.string()), + keyManagement: v.optional(v.object({ apu: v.string(), apv: v.string() })), + }); + export type Input = v.InferInput; + + export const vOut = v.object({ jwe: v.string() }); + export type Out = v.InferOutput; +} export type JoseJweEncryptJwt = ( - input: v.InferInput -) => MaybePromise>; + input: JoseJweEncryptJwt.Input +) => MaybePromise; + +export namespace JoseJweDecryptJwt { + export const vInput = v.object({ + jwe: v.string(), + jwk: vJwk, + }); + export type Input = v.InferInput; + + export const vOut = v.object({ + payload: vJwtPayload, + protectedHeader: vCompactJweHeader, + }); + export type Out = v.InferOutput; +} -export const vJoseJweDecryptJwtInput = v.object({ - jwe: v.string(), - jwk: vJwk, -}); -export const vJoseJweDecryptJwtOut = v.object({ - payload: vJwtPayload, - protectedHeader: vCompactJweHeader, -}); export type JoseJweDecryptJwt = ( - input: v.InferInput -) => MaybePromise>; + input: JoseJweDecryptJwt.Input +) => MaybePromise; export interface JoseJweContext { jose: { diff --git a/packages/jose/src/jwk/c-jwk.ts b/packages/jose/src/jwk/c-jwk.ts index be8dcbd..c50bebf 100644 --- a/packages/jose/src/jwk/c-jwk.ts +++ b/packages/jose/src/jwk/c-jwk.ts @@ -4,18 +4,22 @@ import type { MaybePromise } from '@protokoll/core'; import { vJwk } from './v-jwk.js'; -export const vJoseJwkCalculateThumbprintUriInput = v.object({ - jwk: vJwk, - digestAlgorithm: v.picklist(['sha256', 'sha384', 'sha512']), -}); +export namespace JoseJwkCalculateThumbprintUri { + export const vInput = v.object({ + jwk: vJwk, + digestAlgorithm: v.picklist(['sha256', 'sha384', 'sha512']), + }); + export type Input = v.InferInput; -export const vJoseJwkCalculateThumbprintUriOut = v.object({ - jwkThumbprintUri: v.string(), -}); + export const vOut = v.object({ + jwkThumbprintUri: v.string(), + }); + export type Out = v.InferOutput; +} export type JoseJwkCalculateThumbprintUri = ( - input: v.InferInput -) => MaybePromise>; + input: JoseJwkCalculateThumbprintUri.Input +) => MaybePromise; export interface JoseJwkContext { jose: { diff --git a/packages/jose/src/jws/c-jws.ts b/packages/jose/src/jws/c-jws.ts index 40eb8fc..638a4d2 100644 --- a/packages/jose/src/jws/c-jws.ts +++ b/packages/jose/src/jws/c-jws.ts @@ -11,51 +11,75 @@ import { } from '../jwt/index.js'; import { vCompactJwsHeader } from './v-jws.js'; -export const vJoseJwsSignCompactInput = v.object({ - payload: v.string(), - protectedHeader: vCompactJwsHeader, - jwk: vJwk, -}); -export const vJoseJwsSignCompactOut = v.object({ jws: v.string() }); +export namespace JoseJwsSignCompact { + export const vInput = v.object({ + payload: v.string(), + protectedHeader: vCompactJwsHeader, + jwk: vJwk, + }); + export type Input = v.InferInput; + + export const vOut = v.object({ + jws: v.string(), + }); + export type Out = v.InferOutput; +} export type JoseJwsSignCompact = ( - input: v.InferInput -) => MaybePromise>; + input: JoseJwsSignCompact.Input +) => MaybePromise; + +export namespace JoseJwsVerifyCompact { + export const vInput = v.object({ + jws: v.string(), + jwk: vJwk, + options: v.optional(vVerifyOptions), + }); + export type Input = v.InferInput; + + export const vOut = v.object({ + payload: v.string(), + protectedHeader: vCompactJwsHeader, + }); + export type Out = v.InferOutput; +} -export const vJoseJwsVerifyCompactInput = v.object({ - jws: v.string(), - jwk: vJwk, - options: v.optional(vVerifyOptions), -}); -export const vJoseJwsVerifyCompactOut = v.object({ - payload: v.string(), - protectedHeader: vCompactJwsHeader, -}); export type JoseJwsVerifyCompact = ( - input: v.InferInput -) => MaybePromise>; + input: JoseJwsVerifyCompact.Input +) => MaybePromise; + +export namespace JoseJwsSignJwt { + export const vInput = v.object({ + payload: vJwtPayload, + protectedHeader: vJwtHeader, + jwk: vJwk, + }); + export type Input = v.InferInput; + + export const vOut = v.object({ jws: v.string() }); + export type Out = v.InferOutput; +} -export const vJoseJwsSignJwtInput = v.object({ - payload: vJwtPayload, - protectedHeader: vJwtHeader, - jwk: vJwk, -}); -export const vJoseJwsSignJwtOut = v.object({ jws: v.string() }); export type JoseJwsSignJwt = ( - input: v.InferInput -) => MaybePromise>; + input: JoseJwsSignJwt.Input +) => MaybePromise; + +export namespace JoseJwsVerifyJwt { + export const vInput = v.object({ + jws: v.string(), + jwk: vJwk, + options: v.optional(vJwtVerifyOptions), + }); + export type Input = v.InferInput; + export const vOut = v.object({ + payload: vJwtPayload, + protectedHeader: vJwtHeader, + }); + export type Out = v.InferOutput; +} -export const vJoseJwsVerifyJwtInput = v.object({ - jws: v.string(), - jwk: vJwk, - options: v.optional(vJwtVerifyOptions), -}); -export const vJoseJwsVerifyJwtOut = v.object({ - payload: vJwtPayload, - protectedHeader: vJwtHeader, -}); export type JoseJwsVerifyJwt = ( - input: v.InferInput -) => MaybePromise>; + input: JoseJwsVerifyJwt.Input +) => MaybePromise; export interface JoseJwsContext { jose: { diff --git a/tooling/eslint/base.js b/tooling/eslint/base.js index e8e9b11..1aab944 100644 --- a/tooling/eslint/base.js +++ b/tooling/eslint/base.js @@ -59,7 +59,7 @@ export default tseslint.config( rules: { ...turboPlugin.configs.recommended.rules, ...drizzlePlugin.configs.recommended.rules, - + '@typescript-eslint/no-namespace': ['off', {}], 'drizzle/enforce-delete-with-where': [ 'error', { drizzleObjectName: 'db' },