From a53d3844fa2317d514d2afabb0b620ba72b7670c Mon Sep 17 00:00:00 2001 From: Max Kurapov Date: Mon, 28 Nov 2022 12:09:59 +0100 Subject: [PATCH 1/3] feat(auth): bypassOrCallHttpsigMiddleware --- packages/auth/src/app.ts | 10 ++++++---- packages/auth/src/signature/middleware.ts | 22 ++++++++++++++++++++++ 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/packages/auth/src/app.ts b/packages/auth/src/app.ts index b40665dd82..68a32daecc 100644 --- a/packages/auth/src/app.ts +++ b/packages/auth/src/app.ts @@ -19,7 +19,8 @@ import { createValidatorMiddleware, HttpMethod } from 'openapi' import { grantInitiationHttpsigMiddleware, grantContinueHttpsigMiddleware, - tokenHttpsigMiddleware + tokenHttpsigMiddleware, + bypassOrCallHttpsigMiddleware } from './signature/middleware' export interface AppContextData extends DefaultContext { @@ -199,9 +200,10 @@ export class App { path: '/', method: HttpMethod.POST }), - this.config.bypassSignatureValidation - ? (ctx, next) => next() - : grantInitiationHttpsigMiddleware, + bypassOrCallHttpsigMiddleware( + this.config.bypassSignatureValidation, + grantInitiationHttpsigMiddleware + ), grantRoutes.create ) diff --git a/packages/auth/src/signature/middleware.ts b/packages/auth/src/signature/middleware.ts index 492a650588..9f65e51aae 100644 --- a/packages/auth/src/signature/middleware.ts +++ b/packages/auth/src/signature/middleware.ts @@ -7,6 +7,7 @@ import { JWK } from 'open-payments' import { AppContext } from '../app' import { Grant } from '../grant/model' import { Context } from 'koa' +import { Logger } from 'pino' export async function verifySig( sig: string, @@ -277,3 +278,24 @@ export async function tokenHttpsigMiddleware( await verifySigFromBoundKey(grant, ctx) await next() } + +type Middleware = (ctx: AppContext, next: () => Promise) => Promise + +export function bypassOrCallHttpsigMiddleware( + bypassSignatureValidation: boolean, + middleware: Middleware +): Middleware { + if (bypassSignatureValidation) { + return async (ctx: AppContext, next) => { + ctx.logger.info('Skipping httpsig validation') + + ctx.clientKeyId = getSigInputKeyId( + ctx.headers['signature-input'] as string + ) //TODO: remove once workaround is found for clientKeyId definition + + await next() + } + } + + return middleware +} From e76dfbacc3bb24faa32ffb5af7c24a782e9a0f18 Mon Sep 17 00:00:00 2001 From: Max Kurapov Date: Tue, 29 Nov 2022 14:44:36 +0100 Subject: [PATCH 2/3] feat(auth): use bypassOrCallHttpsigMiddleware, inject clientKeyId when necessary --- packages/auth/src/app.ts | 24 +++++---- packages/auth/src/signature/middleware.ts | 60 ++++++++++++++++------- 2 files changed, 57 insertions(+), 27 deletions(-) diff --git a/packages/auth/src/app.ts b/packages/auth/src/app.ts index 68a32daecc..cb3eda6925 100644 --- a/packages/auth/src/app.ts +++ b/packages/auth/src/app.ts @@ -202,7 +202,8 @@ export class App { }), bypassOrCallHttpsigMiddleware( this.config.bypassSignatureValidation, - grantInitiationHttpsigMiddleware + grantInitiationHttpsigMiddleware, + { injectClientKeyId: true } ), grantRoutes.create ) @@ -214,9 +215,10 @@ export class App { path: '/continue/{id}', method: HttpMethod.POST }), - this.config.bypassSignatureValidation - ? (ctx, next) => next() - : grantContinueHttpsigMiddleware, + bypassOrCallHttpsigMiddleware( + this.config.bypassSignatureValidation, + grantContinueHttpsigMiddleware + ), grantRoutes.continue ) @@ -227,9 +229,10 @@ export class App { path: '/token/{id}', method: HttpMethod.POST }), - this.config.bypassSignatureValidation - ? (ctx, next) => next() - : tokenHttpsigMiddleware, + bypassOrCallHttpsigMiddleware( + this.config.bypassSignatureValidation, + tokenHttpsigMiddleware + ), accessTokenRoutes.rotate ) @@ -240,9 +243,10 @@ export class App { path: '/token/{id}', method: HttpMethod.DELETE }), - this.config.bypassSignatureValidation - ? (ctx, next) => next() - : tokenHttpsigMiddleware, + bypassOrCallHttpsigMiddleware( + this.config.bypassSignatureValidation, + tokenHttpsigMiddleware + ), accessTokenRoutes.revoke ) diff --git a/packages/auth/src/signature/middleware.ts b/packages/auth/src/signature/middleware.ts index 9f65e51aae..0df5a7c612 100644 --- a/packages/auth/src/signature/middleware.ts +++ b/packages/auth/src/signature/middleware.ts @@ -47,16 +47,11 @@ async function verifySigFromClient( client: string, ctx: HttpSigContext ): Promise { - const clientService = await ctx.container.use('clientService') - const clientKey = await clientService.getKey({ + const clientKey = await getClientKeyOrThrow(ctx, { client, - keyId: ctx.clientKeyId + clientKeyId: ctx.clientKeyId }) - if (!clientKey) { - ctx.throw(400, 'invalid client', { error: 'invalid_client' }) - } - return verifySigAndChallenge(clientKey, ctx) } @@ -281,21 +276,52 @@ export async function tokenHttpsigMiddleware( type Middleware = (ctx: AppContext, next: () => Promise) => Promise +export async function getClientKeyOrThrow( + ctx: AppContext | HttpSigContext, + { client, clientKeyId }: { client: string; clientKeyId: string } +): Promise { + const clientService = await ctx.container.use('clientService') + const clientKey = await clientService.getKey({ + client, + keyId: clientKeyId + }) + + if (!clientKey) { + ctx.throw(400, 'invalid client', { error: 'invalid_client' }) + } + + return clientKey +} + export function bypassOrCallHttpsigMiddleware( bypassSignatureValidation: boolean, - middleware: Middleware + middleware: Middleware, + args?: { injectClientKeyId: boolean } ): Middleware { - if (bypassSignatureValidation) { - return async (ctx: AppContext, next) => { - ctx.logger.info('Skipping httpsig validation') + if (!bypassSignatureValidation) { + return middleware + } - ctx.clientKeyId = getSigInputKeyId( - ctx.headers['signature-input'] as string - ) //TODO: remove once workaround is found for clientKeyId definition + return async (ctx: AppContext, next) => { + ctx.logger.info('Skipping httpsig validation') - await next() + if (args?.injectClientKeyId) { + const clientKeyId = ctx.headers['client-key'] as string + + if (!clientKeyId) { + ctx.throw(400, 'invalid client', { error: 'invalid_client' }) + } + + const clientPaymentPointer = ctx.request.body?.client as string + + await getClientKeyOrThrow(ctx, { + client: clientPaymentPointer, + clientKeyId + }) + + ctx.clientKeyId = clientKeyId } - } - return middleware + await next() + } } From 482ee0458f4114686913079174f204f2e5388cd9 Mon Sep 17 00:00:00 2001 From: Max Kurapov Date: Tue, 29 Nov 2022 15:41:06 +0100 Subject: [PATCH 3/3] feat(auth): revert to sig-input header --- packages/auth/src/signature/middleware.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/auth/src/signature/middleware.ts b/packages/auth/src/signature/middleware.ts index 0df5a7c612..3eb9458a9c 100644 --- a/packages/auth/src/signature/middleware.ts +++ b/packages/auth/src/signature/middleware.ts @@ -7,7 +7,6 @@ import { JWK } from 'open-payments' import { AppContext } from '../app' import { Grant } from '../grant/model' import { Context } from 'koa' -import { Logger } from 'pino' export async function verifySig( sig: string, @@ -306,7 +305,9 @@ export function bypassOrCallHttpsigMiddleware( ctx.logger.info('Skipping httpsig validation') if (args?.injectClientKeyId) { - const clientKeyId = ctx.headers['client-key'] as string + const clientKeyId = getSigInputKeyId( + ctx.headers['signature-input'] as string + ) if (!clientKeyId) { ctx.throw(400, 'invalid client', { error: 'invalid_client' })