From 1e4c7d0f317d5c2d98065da12ffeb950b10ee5dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karol=20S=C3=B3jko?= Date: Thu, 25 May 2023 09:43:00 +0200 Subject: [PATCH] feat: refactor auth middleware to handle required and optional cross service token scenarios (#612) * wip: fix variable name * wip: remove redundant middleware in auth * fix: auth middleware refactor * fix(auth): fetching user for key params * fix(auth): specs * fix(auth): registering session controller endpoints --- .../api-gateway/src/Bootstrap/Container.ts | 10 +- packages/api-gateway/src/Bootstrap/Types.ts | 3 +- .../src/Controller/AuthMiddleware.ts | 54 +++++----- .../src/Controller/LegacyController.ts | 6 +- .../OptionalCrossServiceTokenMiddleware.ts | 51 ++++++++++ .../RequiredCrossServiceTokenMiddleware.ts | 57 +++++++++++ .../src/Controller/v1/ActionsController.ts | 6 +- .../Controller/v1/AuthenticatorsController.ts | 8 +- .../src/Controller/v1/FilesController.ts | 2 +- .../src/Controller/v1/ItemsController.ts | 2 +- .../src/Controller/v1/RevisionsController.ts | 2 +- .../src/Controller/v1/SessionsController.ts | 6 +- .../v1/SubscriptionInvitesController.ts | 8 +- .../src/Controller/v1/TokensController.ts | 2 +- .../src/Controller/v1/UsersController.ts | 30 +++--- .../src/Controller/v1/WebSocketsController.ts | 2 +- .../src/Controller/v2/ActionsControllerV2.ts | 8 +- .../Controller/v2/RevisionsControllerV2.ts | 2 +- packages/auth/src/Bootstrap/Container.ts | 14 +-- packages/auth/src/Bootstrap/Types.ts | 5 +- .../GetUserKeyParams/GetUserKeyParams.spec.ts | 6 +- .../GetUserKeyParams/GetUserKeyParams.ts | 10 -- .../GetUserKeyParamsDTOV1Unchallenged.ts | 3 - .../GetUserKeyParamsDTOV2Challenged.ts | 3 - .../InversifyExpressAuthController.ts | 10 +- ...nversifyExpressAuthenticatorsController.ts | 8 +- .../InversifyExpressFeaturesController.ts | 2 +- .../InversifyExpressListedController.ts | 2 +- .../InversifyExpressSessionController.ts | 10 +- .../InversifyExpressSessionsController.ts | 2 +- .../InversifyExpressSettingsController.ts | 8 +- ...ifyExpressSubscriptionInvitesController.ts | 8 +- ...fyExpressSubscriptionSettingsController.ts | 2 +- ...sifyExpressSubscriptionTokensController.ts | 2 +- .../InversifyExpressUserRequestsController.ts | 2 +- .../InversifyExpressUsersController.ts | 6 +- .../InversifyExpressValetTokenController.ts | 2 +- .../ApiGatewayAuthMiddleware.spec.ts | 99 ------------------- .../Middleware/ApiGatewayAuthMiddleware.ts | 23 +---- .../Middleware/AuthMiddleware.spec.ts | 79 --------------- .../Middleware/AuthMiddleware.ts | 45 --------- .../AuthMiddlewareWithoutResponse.spec.ts | 68 ------------- .../AuthMiddlewareWithoutResponse.ts | 32 ------ .../OptionalCrossServiceTokenMiddleware.ts | 27 +++++ .../RequiredCrossServiceTokenMiddleware.ts | 32 ++++++ 45 files changed, 296 insertions(+), 473 deletions(-) create mode 100644 packages/api-gateway/src/Controller/OptionalCrossServiceTokenMiddleware.ts create mode 100644 packages/api-gateway/src/Controller/RequiredCrossServiceTokenMiddleware.ts delete mode 100644 packages/auth/src/Infra/InversifyExpressUtils/Middleware/ApiGatewayAuthMiddleware.spec.ts delete mode 100644 packages/auth/src/Infra/InversifyExpressUtils/Middleware/AuthMiddleware.spec.ts delete mode 100644 packages/auth/src/Infra/InversifyExpressUtils/Middleware/AuthMiddleware.ts delete mode 100644 packages/auth/src/Infra/InversifyExpressUtils/Middleware/AuthMiddlewareWithoutResponse.spec.ts delete mode 100644 packages/auth/src/Infra/InversifyExpressUtils/Middleware/AuthMiddlewareWithoutResponse.ts create mode 100644 packages/auth/src/Infra/InversifyExpressUtils/Middleware/OptionalCrossServiceTokenMiddleware.ts create mode 100644 packages/auth/src/Infra/InversifyExpressUtils/Middleware/RequiredCrossServiceTokenMiddleware.ts diff --git a/packages/api-gateway/src/Bootstrap/Container.ts b/packages/api-gateway/src/Bootstrap/Container.ts index a49cb5f4a..6094fa574 100644 --- a/packages/api-gateway/src/Bootstrap/Container.ts +++ b/packages/api-gateway/src/Bootstrap/Container.ts @@ -8,7 +8,6 @@ import { Timer, TimerInterface } from '@standardnotes/time' import { Env } from './Env' import { TYPES } from './Types' -import { AuthMiddleware } from '../Controller/AuthMiddleware' import { ServiceProxyInterface } from '../Service/Http/ServiceProxyInterface' import { HttpServiceProxy } from '../Service/Http/HttpServiceProxy' import { SubscriptionTokenAuthMiddleware } from '../Controller/SubscriptionTokenAuthMiddleware' @@ -20,6 +19,8 @@ import { DirectCallServiceProxy } from '../Service/Proxy/DirectCallServiceProxy' import { ServiceContainerInterface } from '@standardnotes/domain-core' import { EndpointResolverInterface } from '../Service/Resolver/EndpointResolverInterface' import { EndpointResolver } from '../Service/Resolver/EndpointResolver' +import { RequiredCrossServiceTokenMiddleware } from '../Controller/RequiredCrossServiceTokenMiddleware' +import { OptionalCrossServiceTokenMiddleware } from '../Controller/OptionalCrossServiceTokenMiddleware' // eslint-disable-next-line @typescript-eslint/no-var-requires const newrelicFormatter = require('@newrelic/winston-enricher') @@ -77,7 +78,12 @@ export class ContainerConfigLoader { container.bind(TYPES.CROSS_SERVICE_TOKEN_CACHE_TTL).toConstantValue(+env.get('CROSS_SERVICE_TOKEN_CACHE_TTL', true)) // Middleware - container.bind(TYPES.AuthMiddleware).to(AuthMiddleware) + container + .bind(TYPES.RequiredCrossServiceTokenMiddleware) + .to(RequiredCrossServiceTokenMiddleware) + container + .bind(TYPES.OptionalCrossServiceTokenMiddleware) + .to(OptionalCrossServiceTokenMiddleware) container.bind(TYPES.WebSocketAuthMiddleware).to(WebSocketAuthMiddleware) container .bind(TYPES.SubscriptionTokenAuthMiddleware) diff --git a/packages/api-gateway/src/Bootstrap/Types.ts b/packages/api-gateway/src/Bootstrap/Types.ts index c4c5957a6..97a9bf8dc 100644 --- a/packages/api-gateway/src/Bootstrap/Types.ts +++ b/packages/api-gateway/src/Bootstrap/Types.ts @@ -15,7 +15,8 @@ export const TYPES = { VERSION: Symbol.for('VERSION'), CROSS_SERVICE_TOKEN_CACHE_TTL: Symbol.for('CROSS_SERVICE_TOKEN_CACHE_TTL'), // Middleware - AuthMiddleware: Symbol.for('AuthMiddleware'), + RequiredCrossServiceTokenMiddleware: Symbol.for('RequiredCrossServiceTokenMiddleware'), + OptionalCrossServiceTokenMiddleware: Symbol.for('OptionalCrossServiceTokenMiddleware'), WebSocketAuthMiddleware: Symbol.for('WebSocketAuthMiddleware'), SubscriptionTokenAuthMiddleware: Symbol.for('SubscriptionTokenAuthMiddleware'), // Services diff --git a/packages/api-gateway/src/Controller/AuthMiddleware.ts b/packages/api-gateway/src/Controller/AuthMiddleware.ts index b1f005366..10579330b 100644 --- a/packages/api-gateway/src/Controller/AuthMiddleware.ts +++ b/packages/api-gateway/src/Controller/AuthMiddleware.ts @@ -2,43 +2,33 @@ import { CrossServiceTokenData } from '@standardnotes/security' import { RoleName } from '@standardnotes/domain-core' import { TimerInterface } from '@standardnotes/time' import { NextFunction, Request, Response } from 'express' -import { inject, injectable } from 'inversify' import { BaseMiddleware } from 'inversify-express-utils' import { verify } from 'jsonwebtoken' import { AxiosError } from 'axios' import { Logger } from 'winston' -import { TYPES } from '../Bootstrap/Types' import { CrossServiceTokenCacheInterface } from '../Service/Cache/CrossServiceTokenCacheInterface' import { ServiceProxyInterface } from '../Service/Http/ServiceProxyInterface' -@injectable() -export class AuthMiddleware extends BaseMiddleware { +export abstract class AuthMiddleware extends BaseMiddleware { constructor( - @inject(TYPES.ServiceProxy) private serviceProxy: ServiceProxyInterface, - @inject(TYPES.AUTH_JWT_SECRET) private jwtSecret: string, - @inject(TYPES.CROSS_SERVICE_TOKEN_CACHE_TTL) private crossServiceTokenCacheTTL: number, - @inject(TYPES.CrossServiceTokenCache) private crossServiceTokenCache: CrossServiceTokenCacheInterface, - @inject(TYPES.Timer) private timer: TimerInterface, - @inject(TYPES.Logger) private logger: Logger, + private serviceProxy: ServiceProxyInterface, + private jwtSecret: string, + private crossServiceTokenCacheTTL: number, + private crossServiceTokenCache: CrossServiceTokenCacheInterface, + private timer: TimerInterface, + private logger: Logger, ) { super() } async handler(request: Request, response: Response, next: NextFunction): Promise { - const authHeaderValue = request.headers.authorization as string - - if (!authHeaderValue) { - response.status(401).send({ - error: { - tag: 'invalid-auth', - message: 'Invalid login credentials.', - }, - }) - + if (!this.handleMissingAuthHeader(request.headers.authorization, response, next)) { return } + const authHeaderValue = request.headers.authorization as string + try { let crossServiceTokenFetchedFromCache = true let crossServiceToken = null @@ -49,10 +39,7 @@ export class AuthMiddleware extends BaseMiddleware { if (crossServiceToken === null) { const authResponse = await this.serviceProxy.validateSession(authHeaderValue) - if (authResponse.status > 200) { - response.setHeader('content-type', authResponse.headers.contentType) - response.status(authResponse.status).send(authResponse.data) - + if (!this.handleSessionValidationResponse(authResponse, response, next)) { return } @@ -78,6 +65,7 @@ export class AuthMiddleware extends BaseMiddleware { } response.locals.user = decodedToken.user + response.locals.session = decodedToken.session response.locals.roles = decodedToken.roles } catch (error) { const errorMessage = (error as AxiosError).isAxiosError @@ -105,6 +93,24 @@ export class AuthMiddleware extends BaseMiddleware { return next() } + protected abstract handleSessionValidationResponse( + authResponse: { + status: number + data: unknown + headers: { + contentType: string + } + }, + response: Response, + next: NextFunction, + ): boolean + + protected abstract handleMissingAuthHeader( + authHeaderValue: string | undefined, + response: Response, + next: NextFunction, + ): boolean + private getCrossServiceTokenCacheExpireTimestamp(token: CrossServiceTokenData): number { const crossServiceTokenDefaultCacheExpiration = this.timer.getTimestampInSeconds() + this.crossServiceTokenCacheTTL diff --git a/packages/api-gateway/src/Controller/LegacyController.ts b/packages/api-gateway/src/Controller/LegacyController.ts index fca3ae7d7..58ac96733 100644 --- a/packages/api-gateway/src/Controller/LegacyController.ts +++ b/packages/api-gateway/src/Controller/LegacyController.ts @@ -29,17 +29,17 @@ export class LegacyController extends BaseHttpController { ]) } - @httpPost('/items/sync', TYPES.AuthMiddleware) + @httpPost('/items/sync', TYPES.RequiredCrossServiceTokenMiddleware) async legacyItemsSync(request: Request, response: Response): Promise { await this.httpService.callLegacySyncingServer(request, response, request.path.substring(1), request.body) } - @httpGet('/items/:item_id/revisions', TYPES.AuthMiddleware) + @httpGet('/items/:item_id/revisions', TYPES.RequiredCrossServiceTokenMiddleware) async legacyGetRevisions(request: Request, response: Response): Promise { await this.httpService.callLegacySyncingServer(request, response, request.path.substring(1), request.body) } - @httpGet('/items/:item_id/revisions/:id', TYPES.AuthMiddleware) + @httpGet('/items/:item_id/revisions/:id', TYPES.RequiredCrossServiceTokenMiddleware) async legacyGetRevision(request: Request, response: Response): Promise { await this.httpService.callLegacySyncingServer(request, response, request.path.substring(1), request.body) } diff --git a/packages/api-gateway/src/Controller/OptionalCrossServiceTokenMiddleware.ts b/packages/api-gateway/src/Controller/OptionalCrossServiceTokenMiddleware.ts new file mode 100644 index 000000000..dfec24687 --- /dev/null +++ b/packages/api-gateway/src/Controller/OptionalCrossServiceTokenMiddleware.ts @@ -0,0 +1,51 @@ +import { TimerInterface } from '@standardnotes/time' +import { NextFunction, Response } from 'express' +import { inject, injectable } from 'inversify' +import { Logger } from 'winston' + +import { TYPES } from '../Bootstrap/Types' +import { CrossServiceTokenCacheInterface } from '../Service/Cache/CrossServiceTokenCacheInterface' +import { ServiceProxyInterface } from '../Service/Http/ServiceProxyInterface' +import { AuthMiddleware } from './AuthMiddleware' + +@injectable() +export class OptionalCrossServiceTokenMiddleware extends AuthMiddleware { + constructor( + @inject(TYPES.ServiceProxy) serviceProxy: ServiceProxyInterface, + @inject(TYPES.AUTH_JWT_SECRET) jwtSecret: string, + @inject(TYPES.CROSS_SERVICE_TOKEN_CACHE_TTL) crossServiceTokenCacheTTL: number, + @inject(TYPES.CrossServiceTokenCache) crossServiceTokenCache: CrossServiceTokenCacheInterface, + @inject(TYPES.Timer) timer: TimerInterface, + @inject(TYPES.Logger) logger: Logger, + ) { + super(serviceProxy, jwtSecret, crossServiceTokenCacheTTL, crossServiceTokenCache, timer, logger) + } + + protected override handleSessionValidationResponse( + authResponse: { status: number; data: unknown; headers: { contentType: string } }, + _response: Response, + next: NextFunction, + ): boolean { + if (authResponse.status > 200) { + next() + + return false + } + + return true + } + + protected override handleMissingAuthHeader( + authHeaderValue: string | undefined, + _response: Response, + next: NextFunction, + ): boolean { + if (!authHeaderValue) { + next() + + return false + } + + return true + } +} diff --git a/packages/api-gateway/src/Controller/RequiredCrossServiceTokenMiddleware.ts b/packages/api-gateway/src/Controller/RequiredCrossServiceTokenMiddleware.ts new file mode 100644 index 000000000..62ab18c7d --- /dev/null +++ b/packages/api-gateway/src/Controller/RequiredCrossServiceTokenMiddleware.ts @@ -0,0 +1,57 @@ +import { TimerInterface } from '@standardnotes/time' +import { NextFunction, Response } from 'express' +import { inject, injectable } from 'inversify' +import { Logger } from 'winston' + +import { TYPES } from '../Bootstrap/Types' +import { CrossServiceTokenCacheInterface } from '../Service/Cache/CrossServiceTokenCacheInterface' +import { ServiceProxyInterface } from '../Service/Http/ServiceProxyInterface' +import { AuthMiddleware } from './AuthMiddleware' + +@injectable() +export class RequiredCrossServiceTokenMiddleware extends AuthMiddleware { + constructor( + @inject(TYPES.ServiceProxy) serviceProxy: ServiceProxyInterface, + @inject(TYPES.AUTH_JWT_SECRET) jwtSecret: string, + @inject(TYPES.CROSS_SERVICE_TOKEN_CACHE_TTL) crossServiceTokenCacheTTL: number, + @inject(TYPES.CrossServiceTokenCache) crossServiceTokenCache: CrossServiceTokenCacheInterface, + @inject(TYPES.Timer) timer: TimerInterface, + @inject(TYPES.Logger) logger: Logger, + ) { + super(serviceProxy, jwtSecret, crossServiceTokenCacheTTL, crossServiceTokenCache, timer, logger) + } + + protected override handleSessionValidationResponse( + authResponse: { status: number; data: unknown; headers: { contentType: string } }, + response: Response, + _next: NextFunction, + ): boolean { + if (authResponse.status > 200) { + response.setHeader('content-type', authResponse.headers.contentType) + response.status(authResponse.status).send(authResponse.data) + + return false + } + + return true + } + + protected override handleMissingAuthHeader( + authHeaderValue: string | undefined, + response: Response, + _next: NextFunction, + ): boolean { + if (!authHeaderValue) { + response.status(401).send({ + error: { + tag: 'invalid-auth', + message: 'Invalid login credentials.', + }, + }) + + return false + } + + return true + } +} diff --git a/packages/api-gateway/src/Controller/v1/ActionsController.ts b/packages/api-gateway/src/Controller/v1/ActionsController.ts index 827e776f3..175ba91e5 100644 --- a/packages/api-gateway/src/Controller/v1/ActionsController.ts +++ b/packages/api-gateway/src/Controller/v1/ActionsController.ts @@ -24,7 +24,7 @@ export class ActionsController extends BaseHttpController { ) } - @httpGet('/login-params') + @httpGet('/login-params', TYPES.OptionalCrossServiceTokenMiddleware) async loginParams(request: Request, response: Response): Promise { await this.serviceProxy.callAuthServer( request, @@ -34,7 +34,7 @@ export class ActionsController extends BaseHttpController { ) } - @httpPost('/logout') + @httpPost('/logout', TYPES.OptionalCrossServiceTokenMiddleware) async logout(request: Request, response: Response): Promise { await this.serviceProxy.callAuthServer( request, @@ -54,7 +54,7 @@ export class ActionsController extends BaseHttpController { ) } - @httpPost('/recovery/codes', TYPES.AuthMiddleware) + @httpPost('/recovery/codes', TYPES.RequiredCrossServiceTokenMiddleware) async recoveryCodes(request: Request, response: Response): Promise { await this.serviceProxy.callAuthServer( request, diff --git a/packages/api-gateway/src/Controller/v1/AuthenticatorsController.ts b/packages/api-gateway/src/Controller/v1/AuthenticatorsController.ts index d03612e7f..99cb5b5c1 100644 --- a/packages/api-gateway/src/Controller/v1/AuthenticatorsController.ts +++ b/packages/api-gateway/src/Controller/v1/AuthenticatorsController.ts @@ -15,7 +15,7 @@ export class AuthenticatorsController extends BaseHttpController { super() } - @httpDelete('/:authenticatorId', TYPES.AuthMiddleware) + @httpDelete('/:authenticatorId', TYPES.RequiredCrossServiceTokenMiddleware) async delete(request: Request, response: Response): Promise { await this.httpService.callAuthServer( request, @@ -29,7 +29,7 @@ export class AuthenticatorsController extends BaseHttpController { ) } - @httpGet('/', TYPES.AuthMiddleware) + @httpGet('/', TYPES.RequiredCrossServiceTokenMiddleware) async list(request: Request, response: Response): Promise { await this.httpService.callAuthServer( request, @@ -39,7 +39,7 @@ export class AuthenticatorsController extends BaseHttpController { ) } - @httpGet('/generate-registration-options', TYPES.AuthMiddleware) + @httpGet('/generate-registration-options', TYPES.RequiredCrossServiceTokenMiddleware) async generateRegistrationOptions(request: Request, response: Response): Promise { await this.httpService.callAuthServer( request, @@ -59,7 +59,7 @@ export class AuthenticatorsController extends BaseHttpController { ) } - @httpPost('/verify-registration', TYPES.AuthMiddleware) + @httpPost('/verify-registration', TYPES.RequiredCrossServiceTokenMiddleware) async verifyRegistration(request: Request, response: Response): Promise { await this.httpService.callAuthServer( request, diff --git a/packages/api-gateway/src/Controller/v1/FilesController.ts b/packages/api-gateway/src/Controller/v1/FilesController.ts index 2977cf500..672abf168 100644 --- a/packages/api-gateway/src/Controller/v1/FilesController.ts +++ b/packages/api-gateway/src/Controller/v1/FilesController.ts @@ -15,7 +15,7 @@ export class FilesController extends BaseHttpController { super() } - @httpPost('/valet-tokens', TYPES.AuthMiddleware) + @httpPost('/valet-tokens', TYPES.RequiredCrossServiceTokenMiddleware) async createToken(request: Request, response: Response): Promise { await this.httpService.callAuthServer( request, diff --git a/packages/api-gateway/src/Controller/v1/ItemsController.ts b/packages/api-gateway/src/Controller/v1/ItemsController.ts index fcbfd24ee..fe58fc300 100644 --- a/packages/api-gateway/src/Controller/v1/ItemsController.ts +++ b/packages/api-gateway/src/Controller/v1/ItemsController.ts @@ -5,7 +5,7 @@ import { TYPES } from '../../Bootstrap/Types' import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface' import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolverInterface' -@controller('/v1/items', TYPES.AuthMiddleware) +@controller('/v1/items', TYPES.RequiredCrossServiceTokenMiddleware) export class ItemsController extends BaseHttpController { constructor( @inject(TYPES.ServiceProxy) private serviceProxy: ServiceProxyInterface, diff --git a/packages/api-gateway/src/Controller/v1/RevisionsController.ts b/packages/api-gateway/src/Controller/v1/RevisionsController.ts index 3b5ff704d..612b55cba 100644 --- a/packages/api-gateway/src/Controller/v1/RevisionsController.ts +++ b/packages/api-gateway/src/Controller/v1/RevisionsController.ts @@ -1,7 +1,7 @@ import { BaseHttpController, controller, httpDelete, httpGet, results } from 'inversify-express-utils' import { TYPES } from '../../Bootstrap/Types' -@controller('/v1/items/:item_id/revisions', TYPES.AuthMiddleware) +@controller('/v1/items/:item_id/revisions', TYPES.RequiredCrossServiceTokenMiddleware) export class RevisionsController extends BaseHttpController { @httpGet('/') async getRevisions(): Promise { diff --git a/packages/api-gateway/src/Controller/v1/SessionsController.ts b/packages/api-gateway/src/Controller/v1/SessionsController.ts index 6a171b1ee..596a63dbe 100644 --- a/packages/api-gateway/src/Controller/v1/SessionsController.ts +++ b/packages/api-gateway/src/Controller/v1/SessionsController.ts @@ -14,7 +14,7 @@ export class SessionsController extends BaseHttpController { super() } - @httpGet('/', TYPES.AuthMiddleware) + @httpGet('/', TYPES.RequiredCrossServiceTokenMiddleware) async getSessions(request: Request, response: Response): Promise { await this.httpService.callAuthServer( request, @@ -23,7 +23,7 @@ export class SessionsController extends BaseHttpController { ) } - @httpDelete('/:uuid', TYPES.AuthMiddleware) + @httpDelete('/:uuid', TYPES.RequiredCrossServiceTokenMiddleware) async deleteSession(request: Request, response: Response): Promise { await this.httpService.callAuthServer( request, @@ -35,7 +35,7 @@ export class SessionsController extends BaseHttpController { ) } - @httpDelete('/', TYPES.AuthMiddleware) + @httpDelete('/', TYPES.RequiredCrossServiceTokenMiddleware) async deleteSessions(request: Request, response: Response): Promise { await this.httpService.callAuthServer( request, diff --git a/packages/api-gateway/src/Controller/v1/SubscriptionInvitesController.ts b/packages/api-gateway/src/Controller/v1/SubscriptionInvitesController.ts index c067a59bf..9cc1d17e9 100644 --- a/packages/api-gateway/src/Controller/v1/SubscriptionInvitesController.ts +++ b/packages/api-gateway/src/Controller/v1/SubscriptionInvitesController.ts @@ -15,7 +15,7 @@ export class SubscriptionInvitesController extends BaseHttpController { super() } - @httpPost('/', TYPES.AuthMiddleware) + @httpPost('/', TYPES.RequiredCrossServiceTokenMiddleware) async inviteToSubscriptionSharing(request: Request, response: Response): Promise { await this.httpService.callAuthServer( request, @@ -25,7 +25,7 @@ export class SubscriptionInvitesController extends BaseHttpController { ) } - @httpGet('/', TYPES.AuthMiddleware) + @httpGet('/', TYPES.RequiredCrossServiceTokenMiddleware) async listInvites(request: Request, response: Response): Promise { await this.httpService.callAuthServer( request, @@ -35,7 +35,7 @@ export class SubscriptionInvitesController extends BaseHttpController { ) } - @httpDelete('/:inviteUuid', TYPES.AuthMiddleware) + @httpDelete('/:inviteUuid', TYPES.RequiredCrossServiceTokenMiddleware) async cancelSubscriptionSharing(request: Request, response: Response): Promise { await this.httpService.callAuthServer( request, @@ -48,7 +48,7 @@ export class SubscriptionInvitesController extends BaseHttpController { ) } - @httpPost('/:inviteUuid/accept', TYPES.AuthMiddleware) + @httpPost('/:inviteUuid/accept', TYPES.RequiredCrossServiceTokenMiddleware) async acceptInvite(request: Request, response: Response): Promise { await this.httpService.callAuthServer( request, diff --git a/packages/api-gateway/src/Controller/v1/TokensController.ts b/packages/api-gateway/src/Controller/v1/TokensController.ts index 3384561b6..397008b42 100644 --- a/packages/api-gateway/src/Controller/v1/TokensController.ts +++ b/packages/api-gateway/src/Controller/v1/TokensController.ts @@ -15,7 +15,7 @@ export class TokensController extends BaseHttpController { super() } - @httpPost('/', TYPES.AuthMiddleware) + @httpPost('/', TYPES.RequiredCrossServiceTokenMiddleware) async createToken(request: Request, response: Response): Promise { await this.httpService.callAuthServer( request, diff --git a/packages/api-gateway/src/Controller/v1/UsersController.ts b/packages/api-gateway/src/Controller/v1/UsersController.ts index ca325d38d..c89f8984c 100644 --- a/packages/api-gateway/src/Controller/v1/UsersController.ts +++ b/packages/api-gateway/src/Controller/v1/UsersController.ts @@ -37,7 +37,7 @@ export class UsersController extends BaseHttpController { await this.httpService.callPaymentsServer(request, response, 'api/pro_users/send-activation-code', request.body) } - @httpPatch('/:userId', TYPES.AuthMiddleware) + @httpPatch('/:userId', TYPES.RequiredCrossServiceTokenMiddleware) async updateUser(request: Request, response: Response): Promise { await this.httpService.callAuthServer( request, @@ -47,7 +47,7 @@ export class UsersController extends BaseHttpController { ) } - @httpPut('/:userUuid/password', TYPES.AuthMiddleware) + @httpPut('/:userUuid/password', TYPES.RequiredCrossServiceTokenMiddleware) async changePassword(request: Request, response: Response): Promise { this.logger.debug( '[DEPRECATED] use endpoint /v1/users/:userUuid/attributes/credentials instead of /v1/users/:userUuid/password', @@ -65,7 +65,7 @@ export class UsersController extends BaseHttpController { ) } - @httpPut('/:userUuid/attributes/credentials', TYPES.AuthMiddleware) + @httpPut('/:userUuid/attributes/credentials', TYPES.RequiredCrossServiceTokenMiddleware) async changeCredentials(request: Request, response: Response): Promise { await this.httpService.callAuthServer( request, @@ -79,7 +79,7 @@ export class UsersController extends BaseHttpController { ) } - @httpGet('/:userId/params', TYPES.AuthMiddleware) + @httpGet('/:userId/params', TYPES.RequiredCrossServiceTokenMiddleware) async getKeyParams(request: Request, response: Response): Promise { await this.httpService.callAuthServer( request, @@ -88,12 +88,12 @@ export class UsersController extends BaseHttpController { ) } - @all('/:userId/mfa', TYPES.AuthMiddleware) + @all('/:userId/mfa', TYPES.RequiredCrossServiceTokenMiddleware) async blockMFA(): Promise { return this.statusCode(401) } - @httpPost('/:userUuid/integrations/listed', TYPES.AuthMiddleware) + @httpPost('/:userUuid/integrations/listed', TYPES.RequiredCrossServiceTokenMiddleware) async createListedAccount(request: Request, response: Response): Promise { await this.httpService.callAuthServer( request, @@ -113,7 +113,7 @@ export class UsersController extends BaseHttpController { ) } - @httpGet('/:userUuid/settings', TYPES.AuthMiddleware) + @httpGet('/:userUuid/settings', TYPES.RequiredCrossServiceTokenMiddleware) async listSettings(request: Request, response: Response): Promise { await this.httpService.callAuthServer( request, @@ -126,7 +126,7 @@ export class UsersController extends BaseHttpController { ) } - @httpPut('/:userUuid/settings', TYPES.AuthMiddleware) + @httpPut('/:userUuid/settings', TYPES.RequiredCrossServiceTokenMiddleware) async putSetting(request: Request, response: Response): Promise { await this.httpService.callAuthServer( request, @@ -140,7 +140,7 @@ export class UsersController extends BaseHttpController { ) } - @httpGet('/:userUuid/settings/:settingName', TYPES.AuthMiddleware) + @httpGet('/:userUuid/settings/:settingName', TYPES.RequiredCrossServiceTokenMiddleware) async getSetting(request: Request, response: Response): Promise { await this.httpService.callAuthServer( request, @@ -154,7 +154,7 @@ export class UsersController extends BaseHttpController { ) } - @httpDelete('/:userUuid/settings/:settingName', TYPES.AuthMiddleware) + @httpDelete('/:userUuid/settings/:settingName', TYPES.RequiredCrossServiceTokenMiddleware) async deleteSetting(request: Request, response: Response): Promise { await this.httpService.callAuthServer( request, @@ -169,7 +169,7 @@ export class UsersController extends BaseHttpController { ) } - @httpGet('/:userUuid/subscription-settings/:subscriptionSettingName', TYPES.AuthMiddleware) + @httpGet('/:userUuid/subscription-settings/:subscriptionSettingName', TYPES.RequiredCrossServiceTokenMiddleware) async getSubscriptionSetting(request: Request, response: Response): Promise { await this.httpService.callAuthServer( request, @@ -183,7 +183,7 @@ export class UsersController extends BaseHttpController { ) } - @httpGet('/:userUuid/features', TYPES.AuthMiddleware) + @httpGet('/:userUuid/features', TYPES.RequiredCrossServiceTokenMiddleware) async getFeatures(request: Request, response: Response): Promise { await this.httpService.callAuthServer( request, @@ -196,7 +196,7 @@ export class UsersController extends BaseHttpController { ) } - @httpGet('/:userUuid/subscription', TYPES.AuthMiddleware) + @httpGet('/:userUuid/subscription', TYPES.RequiredCrossServiceTokenMiddleware) async getSubscription(request: Request, response: Response): Promise { await this.httpService.callAuthServer( request, @@ -232,12 +232,12 @@ export class UsersController extends BaseHttpController { ) } - @httpDelete('/:userUuid', TYPES.AuthMiddleware) + @httpDelete('/:userUuid', TYPES.RequiredCrossServiceTokenMiddleware) async deleteUser(request: Request, response: Response): Promise { await this.httpService.callPaymentsServer(request, response, 'api/account', request.body) } - @httpPost('/:userUuid/requests', TYPES.AuthMiddleware) + @httpPost('/:userUuid/requests', TYPES.RequiredCrossServiceTokenMiddleware) async submitRequest(request: Request, response: Response): Promise { await this.httpService.callAuthServer( request, diff --git a/packages/api-gateway/src/Controller/v1/WebSocketsController.ts b/packages/api-gateway/src/Controller/v1/WebSocketsController.ts index 811ce496a..b95251956 100644 --- a/packages/api-gateway/src/Controller/v1/WebSocketsController.ts +++ b/packages/api-gateway/src/Controller/v1/WebSocketsController.ts @@ -17,7 +17,7 @@ export class WebSocketsController extends BaseHttpController { super() } - @httpPost('/tokens', TYPES.AuthMiddleware) + @httpPost('/tokens', TYPES.RequiredCrossServiceTokenMiddleware) async createWebSocketConnectionToken(request: Request, response: Response): Promise { await this.httpService.callWebSocketServer( request, diff --git a/packages/api-gateway/src/Controller/v2/ActionsControllerV2.ts b/packages/api-gateway/src/Controller/v2/ActionsControllerV2.ts index 8a1af1475..474269238 100644 --- a/packages/api-gateway/src/Controller/v2/ActionsControllerV2.ts +++ b/packages/api-gateway/src/Controller/v2/ActionsControllerV2.ts @@ -9,7 +9,7 @@ import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolv @controller('/v2') export class ActionsControllerV2 extends BaseHttpController { constructor( - @inject(TYPES.ServiceProxy) private httpService: ServiceProxyInterface, + @inject(TYPES.ServiceProxy) private serviceProxy: ServiceProxyInterface, @inject(TYPES.EndpointResolver) private endpointResolver: EndpointResolverInterface, ) { super() @@ -17,7 +17,7 @@ export class ActionsControllerV2 extends BaseHttpController { @httpPost('/login') async login(request: Request, response: Response): Promise { - await this.httpService.callAuthServer( + await this.serviceProxy.callAuthServer( request, response, this.endpointResolver.resolveEndpointOrMethodIdentifier('POST', 'auth/pkce_sign_in'), @@ -25,9 +25,9 @@ export class ActionsControllerV2 extends BaseHttpController { ) } - @httpPost('/login-params') + @httpPost('/login-params', TYPES.OptionalCrossServiceTokenMiddleware) async loginParams(request: Request, response: Response): Promise { - await this.httpService.callAuthServer( + await this.serviceProxy.callAuthServer( request, response, this.endpointResolver.resolveEndpointOrMethodIdentifier('POST', 'auth/pkce_params'), diff --git a/packages/api-gateway/src/Controller/v2/RevisionsControllerV2.ts b/packages/api-gateway/src/Controller/v2/RevisionsControllerV2.ts index e4e72b412..e8a883997 100644 --- a/packages/api-gateway/src/Controller/v2/RevisionsControllerV2.ts +++ b/packages/api-gateway/src/Controller/v2/RevisionsControllerV2.ts @@ -6,7 +6,7 @@ import { TYPES } from '../../Bootstrap/Types' import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface' import { EndpointResolverInterface } from '../../Service/Resolver/EndpointResolverInterface' -@controller('/v2/items/:itemUuid/revisions', TYPES.AuthMiddleware) +@controller('/v2/items/:itemUuid/revisions', TYPES.RequiredCrossServiceTokenMiddleware) export class RevisionsControllerV2 extends BaseHttpController { constructor( @inject(TYPES.ServiceProxy) private httpService: ServiceProxyInterface, diff --git a/packages/auth/src/Bootstrap/Container.ts b/packages/auth/src/Bootstrap/Container.ts index 80d8433a7..94be535c1 100644 --- a/packages/auth/src/Bootstrap/Container.ts +++ b/packages/auth/src/Bootstrap/Container.ts @@ -241,17 +241,16 @@ import { InversifyExpressSubscriptionTokensController } from '../Infra/Inversify import { InversifyExpressSubscriptionSettingsController } from '../Infra/InversifyExpressUtils/InversifyExpressSubscriptionSettingsController' import { InversifyExpressSettingsController } from '../Infra/InversifyExpressUtils/InversifyExpressSettingsController' import { SessionMiddleware } from '../Infra/InversifyExpressUtils/Middleware/SessionMiddleware' -import { ApiGatewayAuthMiddleware } from '../Infra/InversifyExpressUtils/Middleware/ApiGatewayAuthMiddleware' import { ApiGatewayOfflineAuthMiddleware } from '../Infra/InversifyExpressUtils/Middleware/ApiGatewayOfflineAuthMiddleware' -import { AuthMiddleware } from '../Infra/InversifyExpressUtils/Middleware/AuthMiddleware' import { OfflineUserAuthMiddleware } from '../Infra/InversifyExpressUtils/Middleware/OfflineUserAuthMiddleware' -import { AuthMiddlewareWithoutResponse } from '../Infra/InversifyExpressUtils/Middleware/AuthMiddlewareWithoutResponse' import { LockMiddleware } from '../Infra/InversifyExpressUtils/Middleware/LockMiddleware' import { InversifyExpressSessionController } from '../Infra/InversifyExpressUtils/InversifyExpressSessionController' import { InversifyExpressOfflineController } from '../Infra/InversifyExpressUtils/InversifyExpressOfflineController' import { InversifyExpressListedController } from '../Infra/InversifyExpressUtils/InversifyExpressListedController' import { InversifyExpressInternalController } from '../Infra/InversifyExpressUtils/InversifyExpressInternalController' import { InversifyExpressFeaturesController } from '../Infra/InversifyExpressUtils/InversifyExpressFeaturesController' +import { RequiredCrossServiceTokenMiddleware } from '../Infra/InversifyExpressUtils/Middleware/RequiredCrossServiceTokenMiddleware' +import { OptionalCrossServiceTokenMiddleware } from '../Infra/InversifyExpressUtils/Middleware/OptionalCrossServiceTokenMiddleware' // eslint-disable-next-line @typescript-eslint/no-var-requires const newrelicFormatter = require('@newrelic/winston-enricher') @@ -448,13 +447,14 @@ export class ContainerConfigLoader { ) // Middleware - container.bind(TYPES.Auth_AuthMiddleware).to(AuthMiddleware) container.bind(TYPES.Auth_SessionMiddleware).to(SessionMiddleware) container.bind(TYPES.Auth_LockMiddleware).to(LockMiddleware) container - .bind(TYPES.Auth_AuthMiddlewareWithoutResponse) - .to(AuthMiddlewareWithoutResponse) - container.bind(TYPES.Auth_ApiGatewayAuthMiddleware).to(ApiGatewayAuthMiddleware) + .bind(TYPES.Auth_RequiredCrossServiceTokenMiddleware) + .to(RequiredCrossServiceTokenMiddleware) + container + .bind(TYPES.Auth_OptionalCrossServiceTokenMiddleware) + .to(OptionalCrossServiceTokenMiddleware) container .bind(TYPES.Auth_ApiGatewayOfflineAuthMiddleware) .to(ApiGatewayOfflineAuthMiddleware) diff --git a/packages/auth/src/Bootstrap/Types.ts b/packages/auth/src/Bootstrap/Types.ts index d355dd8f5..e1d41fc78 100644 --- a/packages/auth/src/Bootstrap/Types.ts +++ b/packages/auth/src/Bootstrap/Types.ts @@ -51,11 +51,10 @@ const TYPES = { Auth_ORMAuthenticatorChallengeRepository: Symbol.for('Auth_ORMAuthenticatorChallengeRepository'), Auth_ORMCacheEntryRepository: Symbol.for('Auth_ORMCacheEntryRepository'), // Middleware - Auth_AuthMiddleware: Symbol.for('Auth_AuthMiddleware'), - Auth_ApiGatewayAuthMiddleware: Symbol.for('Auth_ApiGatewayAuthMiddleware'), + Auth_RequiredCrossServiceTokenMiddleware: Symbol.for('Auth_RequiredCrossServiceTokenMiddleware'), + Auth_OptionalCrossServiceTokenMiddleware: Symbol.for('Auth_OptionalCrossServiceTokenMiddleware'), Auth_ApiGatewayOfflineAuthMiddleware: Symbol.for('Auth_ApiGatewayOfflineAuthMiddleware'), Auth_OfflineUserAuthMiddleware: Symbol.for('Auth_OfflineUserAuthMiddleware'), - Auth_AuthMiddlewareWithoutResponse: Symbol.for('Auth_AuthMiddlewareWithoutResponse'), Auth_LockMiddleware: Symbol.for('Auth_LockMiddleware'), Auth_SessionMiddleware: Symbol.for('Auth_SessionMiddleware'), // Projectors diff --git a/packages/auth/src/Domain/UseCase/GetUserKeyParams/GetUserKeyParams.spec.ts b/packages/auth/src/Domain/UseCase/GetUserKeyParams/GetUserKeyParams.spec.ts index c07311536..f897e0a50 100644 --- a/packages/auth/src/Domain/UseCase/GetUserKeyParams/GetUserKeyParams.spec.ts +++ b/packages/auth/src/Domain/UseCase/GetUserKeyParams/GetUserKeyParams.spec.ts @@ -35,9 +35,7 @@ describe('GetUserKeyParams', () => { }) it('should get key params for an authenticated user - searching by email', async () => { - expect( - await createUseCase().execute({ email: 'test@test.te', authenticated: true, authenticatedUser: user }), - ).toEqual({ + expect(await createUseCase().execute({ email: 'test@test.te', authenticated: true })).toEqual({ keyParams: { foo: 'bar', }, @@ -63,7 +61,7 @@ describe('GetUserKeyParams', () => { }) it('should get key params for an authenticated user - searching by uuid', async () => { - expect(await createUseCase().execute({ userUuid: '1-2-3', authenticated: true, authenticatedUser: user })).toEqual({ + expect(await createUseCase().execute({ userUuid: '1-2-3', authenticated: true })).toEqual({ keyParams: { foo: 'bar', }, diff --git a/packages/auth/src/Domain/UseCase/GetUserKeyParams/GetUserKeyParams.ts b/packages/auth/src/Domain/UseCase/GetUserKeyParams/GetUserKeyParams.ts index d6e0a5472..a44104fe2 100644 --- a/packages/auth/src/Domain/UseCase/GetUserKeyParams/GetUserKeyParams.ts +++ b/packages/auth/src/Domain/UseCase/GetUserKeyParams/GetUserKeyParams.ts @@ -22,16 +22,6 @@ export class GetUserKeyParams implements UseCaseInterface { ) {} async execute(dto: GetUserKeyParamsDTO): Promise { - if (dto.authenticatedUser) { - this.logger.debug(`Creating key params for authenticated user ${dto.authenticatedUser.email}`) - - const keyParams = await this.createKeyParams(dto, dto.authenticatedUser, true) - - return { - keyParams, - } - } - let user: User | null = null if (dto.email !== undefined) { const usernameOrError = Username.create(dto.email) diff --git a/packages/auth/src/Domain/UseCase/GetUserKeyParams/GetUserKeyParamsDTOV1Unchallenged.ts b/packages/auth/src/Domain/UseCase/GetUserKeyParams/GetUserKeyParamsDTOV1Unchallenged.ts index 7356ff5e3..3a452a2ad 100644 --- a/packages/auth/src/Domain/UseCase/GetUserKeyParams/GetUserKeyParamsDTOV1Unchallenged.ts +++ b/packages/auth/src/Domain/UseCase/GetUserKeyParams/GetUserKeyParamsDTOV1Unchallenged.ts @@ -1,8 +1,5 @@ -import { User } from '../../User/User' - export type GetUserKeyParamsDTOV1Unchallenged = { authenticated: boolean email?: string userUuid?: string - authenticatedUser?: User } diff --git a/packages/auth/src/Domain/UseCase/GetUserKeyParams/GetUserKeyParamsDTOV2Challenged.ts b/packages/auth/src/Domain/UseCase/GetUserKeyParams/GetUserKeyParamsDTOV2Challenged.ts index ae18454b7..f3497cbad 100644 --- a/packages/auth/src/Domain/UseCase/GetUserKeyParams/GetUserKeyParamsDTOV2Challenged.ts +++ b/packages/auth/src/Domain/UseCase/GetUserKeyParams/GetUserKeyParamsDTOV2Challenged.ts @@ -1,9 +1,6 @@ -import { User } from '../../User/User' - export type GetUserKeyParamsDTOV2Challenged = { authenticated: boolean codeChallenge: string email?: string userUuid?: string - authenticatedUser?: User } diff --git a/packages/auth/src/Infra/InversifyExpressUtils/InversifyExpressAuthController.ts b/packages/auth/src/Infra/InversifyExpressUtils/InversifyExpressAuthController.ts index 0cd5edeb6..64491bc58 100644 --- a/packages/auth/src/Infra/InversifyExpressUtils/InversifyExpressAuthController.ts +++ b/packages/auth/src/Infra/InversifyExpressUtils/InversifyExpressAuthController.ts @@ -44,13 +44,12 @@ export class InversifyExpressAuthController extends BaseHttpController { this.controllerContainer.register('auth.signOut', this.signOut.bind(this)) } - @httpGet('/params', TYPES.Auth_AuthMiddlewareWithoutResponse) + @httpGet('/params', TYPES.Auth_OptionalCrossServiceTokenMiddleware) async params(request: Request, response: Response): Promise { if (response.locals.session) { const result = await this.getUserKeyParams.execute({ email: response.locals.user.email, authenticated: true, - authenticatedUser: response.locals.user, }) return this.json(result.keyParams) @@ -155,7 +154,7 @@ export class InversifyExpressAuthController extends BaseHttpController { return this.json(signInResult.authResponse) } - @httpPost('/pkce_params', TYPES.Auth_AuthMiddlewareWithoutResponse) + @httpPost('/pkce_params', TYPES.Auth_OptionalCrossServiceTokenMiddleware) async pkceParams(request: Request, response: Response): Promise { if (!request.body.code_challenge) { return this.json( @@ -172,7 +171,6 @@ export class InversifyExpressAuthController extends BaseHttpController { const result = await this.getUserKeyParams.execute({ email: response.locals.user.email, authenticated: true, - authenticatedUser: response.locals.user, codeChallenge: request.body.code_challenge as string, }) @@ -261,7 +259,7 @@ export class InversifyExpressAuthController extends BaseHttpController { return this.json(signInResult.authResponse) } - @httpPost('/recovery/codes', TYPES.Auth_ApiGatewayAuthMiddleware) + @httpPost('/recovery/codes', TYPES.Auth_RequiredCrossServiceTokenMiddleware) async generateRecoveryCodes(_request: Request, response: Response): Promise { const result = await this.authController.generateRecoveryCodes({ userUuid: response.locals.user.uuid, @@ -296,7 +294,7 @@ export class InversifyExpressAuthController extends BaseHttpController { return this.json(result.data, result.status) } - @httpPost('/sign_out', TYPES.Auth_AuthMiddlewareWithoutResponse) + @httpPost('/sign_out', TYPES.Auth_OptionalCrossServiceTokenMiddleware) async signOut(request: Request, response: Response): Promise { const result = await this.authController.signOut({ readOnlyAccess: response.locals.readOnlyAccess, diff --git a/packages/auth/src/Infra/InversifyExpressUtils/InversifyExpressAuthenticatorsController.ts b/packages/auth/src/Infra/InversifyExpressUtils/InversifyExpressAuthenticatorsController.ts index 670da6643..41b3a3872 100644 --- a/packages/auth/src/Infra/InversifyExpressUtils/InversifyExpressAuthenticatorsController.ts +++ b/packages/auth/src/Infra/InversifyExpressUtils/InversifyExpressAuthenticatorsController.ts @@ -37,7 +37,7 @@ export class InversifyExpressAuthenticatorsController extends BaseHttpController ) } - @httpGet('/', TYPES.Auth_ApiGatewayAuthMiddleware) + @httpGet('/', TYPES.Auth_RequiredCrossServiceTokenMiddleware) async list(_request: Request, response: Response): Promise { const result = await this.authenticatorsController.list({ userUuid: response.locals.user.uuid, @@ -46,7 +46,7 @@ export class InversifyExpressAuthenticatorsController extends BaseHttpController return this.json(result.data, result.status) } - @httpDelete('/:authenticatorId', TYPES.Auth_ApiGatewayAuthMiddleware) + @httpDelete('/:authenticatorId', TYPES.Auth_RequiredCrossServiceTokenMiddleware) async delete(request: Request, response: Response): Promise { const result = await this.authenticatorsController.delete({ userUuid: response.locals.user.uuid, @@ -56,7 +56,7 @@ export class InversifyExpressAuthenticatorsController extends BaseHttpController return this.json(result.data, result.status) } - @httpGet('/generate-registration-options', TYPES.Auth_ApiGatewayAuthMiddleware) + @httpGet('/generate-registration-options', TYPES.Auth_RequiredCrossServiceTokenMiddleware) async generateRegistrationOptions(_request: Request, response: Response): Promise { const result = await this.authenticatorsController.generateRegistrationOptions({ username: response.locals.user.email, @@ -66,7 +66,7 @@ export class InversifyExpressAuthenticatorsController extends BaseHttpController return this.json(result.data, result.status) } - @httpPost('/verify-registration', TYPES.Auth_ApiGatewayAuthMiddleware) + @httpPost('/verify-registration', TYPES.Auth_RequiredCrossServiceTokenMiddleware) async verifyRegistration(request: Request, response: Response): Promise { const result = await this.authenticatorsController.verifyRegistrationResponse({ userUuid: response.locals.user.uuid, diff --git a/packages/auth/src/Infra/InversifyExpressUtils/InversifyExpressFeaturesController.ts b/packages/auth/src/Infra/InversifyExpressUtils/InversifyExpressFeaturesController.ts index d7c5ef478..27b632c02 100644 --- a/packages/auth/src/Infra/InversifyExpressUtils/InversifyExpressFeaturesController.ts +++ b/packages/auth/src/Infra/InversifyExpressUtils/InversifyExpressFeaturesController.ts @@ -22,7 +22,7 @@ export class InversifyExpressFeaturesController extends BaseHttpController { this.controllerContainer.register('auth.users.getFeatures', this.getFeatures.bind(this)) } - @httpGet('/', TYPES.Auth_ApiGatewayAuthMiddleware) + @httpGet('/', TYPES.Auth_RequiredCrossServiceTokenMiddleware) async getFeatures(request: Request, response: Response): Promise { if (request.params.userUuid !== response.locals.user.uuid) { return this.json( diff --git a/packages/auth/src/Infra/InversifyExpressUtils/InversifyExpressListedController.ts b/packages/auth/src/Infra/InversifyExpressUtils/InversifyExpressListedController.ts index bee3f2c21..e7b1dd53f 100644 --- a/packages/auth/src/Infra/InversifyExpressUtils/InversifyExpressListedController.ts +++ b/packages/auth/src/Infra/InversifyExpressUtils/InversifyExpressListedController.ts @@ -18,7 +18,7 @@ export class InversifyExpressListedController extends BaseHttpController { this.controllerContainer.register('auth.users.createListedAccount', this.createListedAccount.bind(this)) } - @httpPost('/', TYPES.Auth_ApiGatewayAuthMiddleware) + @httpPost('/', TYPES.Auth_RequiredCrossServiceTokenMiddleware) async createListedAccount(_request: Request, response: Response): Promise { if (response.locals.readOnlyAccess) { return this.json( diff --git a/packages/auth/src/Infra/InversifyExpressUtils/InversifyExpressSessionController.ts b/packages/auth/src/Infra/InversifyExpressUtils/InversifyExpressSessionController.ts index 5540ba9aa..d2356c6bc 100644 --- a/packages/auth/src/Infra/InversifyExpressUtils/InversifyExpressSessionController.ts +++ b/packages/auth/src/Infra/InversifyExpressUtils/InversifyExpressSessionController.ts @@ -26,12 +26,12 @@ export class InversifyExpressSessionController extends BaseHttpController { ) { super() - this.controllerContainer.register('auth.session.delete', this.deleteSession.bind(this)) - this.controllerContainer.register('auth.session.deleteAll', this.deleteAllSessions.bind(this)) - this.controllerContainer.register('auth.session.refresh', this.refresh.bind(this)) + this.controllerContainer.register('auth.sessions.delete', this.deleteSession.bind(this)) + this.controllerContainer.register('auth.sessions.deleteAll', this.deleteAllSessions.bind(this)) + this.controllerContainer.register('auth.sessions.refresh', this.refresh.bind(this)) } - @httpDelete('/', TYPES.Auth_AuthMiddleware, TYPES.Auth_SessionMiddleware) + @httpDelete('/', TYPES.Auth_RequiredCrossServiceTokenMiddleware, TYPES.Auth_SessionMiddleware) async deleteSession(request: Request, response: Response): Promise { if (response.locals.readOnlyAccess) { return this.json( @@ -87,7 +87,7 @@ export class InversifyExpressSessionController extends BaseHttpController { response.status(204).send() } - @httpDelete('/all', TYPES.Auth_AuthMiddleware, TYPES.Auth_SessionMiddleware) + @httpDelete('/all', TYPES.Auth_RequiredCrossServiceTokenMiddleware, TYPES.Auth_SessionMiddleware) async deleteAllSessions(_request: Request, response: Response): Promise { if (response.locals.readOnlyAccess) { return this.json( diff --git a/packages/auth/src/Infra/InversifyExpressUtils/InversifyExpressSessionsController.ts b/packages/auth/src/Infra/InversifyExpressUtils/InversifyExpressSessionsController.ts index 1835ecebb..dfcd9b9d6 100644 --- a/packages/auth/src/Infra/InversifyExpressUtils/InversifyExpressSessionsController.ts +++ b/packages/auth/src/Infra/InversifyExpressUtils/InversifyExpressSessionsController.ts @@ -62,7 +62,7 @@ export class InversifyExpressSessionsController extends BaseHttpController { return this.json({ authToken: result.token }) } - @httpGet('/', TYPES.Auth_AuthMiddleware, TYPES.Auth_SessionMiddleware) + @httpGet('/', TYPES.Auth_RequiredCrossServiceTokenMiddleware, TYPES.Auth_SessionMiddleware) async getSessions(_request: Request, response: Response): Promise { if (response.locals.readOnlyAccess) { return this.json([]) diff --git a/packages/auth/src/Infra/InversifyExpressUtils/InversifyExpressSettingsController.ts b/packages/auth/src/Infra/InversifyExpressUtils/InversifyExpressSettingsController.ts index 1e38f645f..40258e123 100644 --- a/packages/auth/src/Infra/InversifyExpressUtils/InversifyExpressSettingsController.ts +++ b/packages/auth/src/Infra/InversifyExpressUtils/InversifyExpressSettingsController.ts @@ -35,7 +35,7 @@ export class InversifyExpressSettingsController extends BaseHttpController { this.controllerContainer.register('auth.users.deleteSetting', this.deleteSetting.bind(this)) } - @httpGet('/settings', TYPES.Auth_ApiGatewayAuthMiddleware) + @httpGet('/settings', TYPES.Auth_RequiredCrossServiceTokenMiddleware) async getSettings(request: Request, response: Response): Promise { if (request.params.userUuid !== response.locals.user.uuid) { return this.json( @@ -54,7 +54,7 @@ export class InversifyExpressSettingsController extends BaseHttpController { return this.json(result) } - @httpGet('/settings/:settingName', TYPES.Auth_ApiGatewayAuthMiddleware) + @httpGet('/settings/:settingName', TYPES.Auth_RequiredCrossServiceTokenMiddleware) async getSetting(request: Request, response: Response): Promise { if (request.params.userUuid !== response.locals.user.uuid) { return this.json( @@ -77,7 +77,7 @@ export class InversifyExpressSettingsController extends BaseHttpController { return this.json(result, 400) } - @httpPut('/settings', TYPES.Auth_ApiGatewayAuthMiddleware) + @httpPut('/settings', TYPES.Auth_RequiredCrossServiceTokenMiddleware) async updateSetting(request: Request, response: Response): Promise { if (response.locals.readOnlyAccess) { return this.json( @@ -124,7 +124,7 @@ export class InversifyExpressSettingsController extends BaseHttpController { return this.json(result, result.statusCode) } - @httpDelete('/settings/:settingName', TYPES.Auth_ApiGatewayAuthMiddleware) + @httpDelete('/settings/:settingName', TYPES.Auth_RequiredCrossServiceTokenMiddleware) async deleteSetting(request: Request, response: Response): Promise { if (response.locals.readOnlyAccess) { return this.json( diff --git a/packages/auth/src/Infra/InversifyExpressUtils/InversifyExpressSubscriptionInvitesController.ts b/packages/auth/src/Infra/InversifyExpressUtils/InversifyExpressSubscriptionInvitesController.ts index 8cedf2b74..284de7bb6 100644 --- a/packages/auth/src/Infra/InversifyExpressUtils/InversifyExpressSubscriptionInvitesController.ts +++ b/packages/auth/src/Infra/InversifyExpressUtils/InversifyExpressSubscriptionInvitesController.ts @@ -31,7 +31,7 @@ export class InversifyExpressSubscriptionInvitesController extends BaseHttpContr this.controllerContainer.register('auth.subscriptionInvites.list', this.listInvites.bind(this)) } - @httpPost('/:inviteUuid/accept', TYPES.Auth_ApiGatewayAuthMiddleware) + @httpPost('/:inviteUuid/accept', TYPES.Auth_RequiredCrossServiceTokenMiddleware) async acceptInvite(request: Request, response: Response): Promise { const result = await this.subscriptionInvitesController.acceptInvite({ api: request.query.api as ApiVersion, @@ -52,7 +52,7 @@ export class InversifyExpressSubscriptionInvitesController extends BaseHttpContr return this.json(response.data, response.status) } - @httpPost('/', TYPES.Auth_ApiGatewayAuthMiddleware) + @httpPost('/', TYPES.Auth_RequiredCrossServiceTokenMiddleware) async inviteToSubscriptionSharing(request: Request, response: Response): Promise { const result = await this.subscriptionInvitesController.invite({ ...request.body, @@ -64,7 +64,7 @@ export class InversifyExpressSubscriptionInvitesController extends BaseHttpContr return this.json(result.data, result.status) } - @httpDelete('/:inviteUuid', TYPES.Auth_ApiGatewayAuthMiddleware) + @httpDelete('/:inviteUuid', TYPES.Auth_RequiredCrossServiceTokenMiddleware) async cancelSubscriptionSharing(request: Request, response: Response): Promise { const result = await this.subscriptionInvitesController.cancelInvite({ ...request.body, @@ -75,7 +75,7 @@ export class InversifyExpressSubscriptionInvitesController extends BaseHttpContr return this.json(result.data, result.status) } - @httpGet('/', TYPES.Auth_ApiGatewayAuthMiddleware) + @httpGet('/', TYPES.Auth_RequiredCrossServiceTokenMiddleware) async listInvites(request: Request, response: Response): Promise { const result = await this.subscriptionInvitesController.listInvites({ ...request.body, diff --git a/packages/auth/src/Infra/InversifyExpressUtils/InversifyExpressSubscriptionSettingsController.ts b/packages/auth/src/Infra/InversifyExpressUtils/InversifyExpressSubscriptionSettingsController.ts index 722ed2952..e5323563b 100644 --- a/packages/auth/src/Infra/InversifyExpressUtils/InversifyExpressSubscriptionSettingsController.ts +++ b/packages/auth/src/Infra/InversifyExpressUtils/InversifyExpressSubscriptionSettingsController.ts @@ -22,7 +22,7 @@ export class InversifyExpressSubscriptionSettingsController extends BaseHttpCont this.controllerContainer.register('auth.users.getSubscriptionSetting', this.getSubscriptionSetting.bind(this)) } - @httpGet('/subscription-settings/:subscriptionSettingName', TYPES.Auth_ApiGatewayAuthMiddleware) + @httpGet('/subscription-settings/:subscriptionSettingName', TYPES.Auth_RequiredCrossServiceTokenMiddleware) async getSubscriptionSetting(request: Request, response: Response): Promise { const result = await this.doGetSetting.execute({ userUuid: response.locals.user.uuid, diff --git a/packages/auth/src/Infra/InversifyExpressUtils/InversifyExpressSubscriptionTokensController.ts b/packages/auth/src/Infra/InversifyExpressUtils/InversifyExpressSubscriptionTokensController.ts index b80cdc72d..ce3e1b636 100644 --- a/packages/auth/src/Infra/InversifyExpressUtils/InversifyExpressSubscriptionTokensController.ts +++ b/packages/auth/src/Infra/InversifyExpressUtils/InversifyExpressSubscriptionTokensController.ts @@ -37,7 +37,7 @@ export class InversifyExpressSubscriptionTokensController extends BaseHttpContro this.controllerContainer.register('auth.subscription-tokens.create', this.createToken.bind(this)) } - @httpPost('/', TYPES.Auth_ApiGatewayAuthMiddleware) + @httpPost('/', TYPES.Auth_RequiredCrossServiceTokenMiddleware) async createToken(_request: Request, response: Response): Promise { if (response.locals.readOnlyAccess) { return this.json( diff --git a/packages/auth/src/Infra/InversifyExpressUtils/InversifyExpressUserRequestsController.ts b/packages/auth/src/Infra/InversifyExpressUtils/InversifyExpressUserRequestsController.ts index 95d6e1595..ddaf573da 100644 --- a/packages/auth/src/Infra/InversifyExpressUtils/InversifyExpressUserRequestsController.ts +++ b/packages/auth/src/Infra/InversifyExpressUtils/InversifyExpressUserRequestsController.ts @@ -17,7 +17,7 @@ export class InversifyExpressUserRequestsController extends BaseHttpController { this.controllerContainer.register('auth.users.createRequest', this.submitRequest.bind(this)) } - @httpPost('/', TYPES.Auth_ApiGatewayAuthMiddleware) + @httpPost('/', TYPES.Auth_RequiredCrossServiceTokenMiddleware) async submitRequest(request: Request, response: Response): Promise { const result = await this.userRequestsController.submitUserRequest({ requestType: request.body.requestType, diff --git a/packages/auth/src/Infra/InversifyExpressUtils/InversifyExpressUsersController.ts b/packages/auth/src/Infra/InversifyExpressUtils/InversifyExpressUsersController.ts index 18da98acb..9134a6331 100644 --- a/packages/auth/src/Infra/InversifyExpressUtils/InversifyExpressUsersController.ts +++ b/packages/auth/src/Infra/InversifyExpressUtils/InversifyExpressUsersController.ts @@ -41,7 +41,7 @@ export class InversifyExpressUsersController extends BaseHttpController { this.controllerContainer.register('auth.users.updateCredentials', this.changeCredentials.bind(this)) } - @httpPatch('/:userId', TYPES.Auth_ApiGatewayAuthMiddleware) + @httpPatch('/:userId', TYPES.Auth_RequiredCrossServiceTokenMiddleware) async update(request: Request, response: Response): Promise { if (response.locals.readOnlyAccess) { return this.json( @@ -132,7 +132,7 @@ export class InversifyExpressUsersController extends BaseHttpController { return this.json({ message: result.message }, result.responseCode) } - @httpGet('/:userUuid/subscription', TYPES.Auth_ApiGatewayAuthMiddleware) + @httpGet('/:userUuid/subscription', TYPES.Auth_RequiredCrossServiceTokenMiddleware) async getSubscription(request: Request, response: Response): Promise { if (request.params.userUuid !== response.locals.user.uuid) { return this.json( @@ -156,7 +156,7 @@ export class InversifyExpressUsersController extends BaseHttpController { return this.json(result, 400) } - @httpPut('/:userId/attributes/credentials', TYPES.Auth_AuthMiddleware) + @httpPut('/:userId/attributes/credentials', TYPES.Auth_RequiredCrossServiceTokenMiddleware) async changeCredentials(request: Request, response: Response): Promise { if (response.locals.readOnlyAccess) { return this.json( diff --git a/packages/auth/src/Infra/InversifyExpressUtils/InversifyExpressValetTokenController.ts b/packages/auth/src/Infra/InversifyExpressUtils/InversifyExpressValetTokenController.ts index 593cd5760..4a24d3044 100644 --- a/packages/auth/src/Infra/InversifyExpressUtils/InversifyExpressValetTokenController.ts +++ b/packages/auth/src/Infra/InversifyExpressUtils/InversifyExpressValetTokenController.ts @@ -13,7 +13,7 @@ import { ControllerContainerInterface, Uuid } from '@standardnotes/domain-core' import TYPES from '../../Bootstrap/Types' import { CreateValetToken } from '../../Domain/UseCase/CreateValetToken/CreateValetToken' -@controller('/valet-tokens', TYPES.Auth_ApiGatewayAuthMiddleware) +@controller('/valet-tokens', TYPES.Auth_RequiredCrossServiceTokenMiddleware) export class InversifyExpressValetTokenController extends BaseHttpController { constructor( @inject(TYPES.Auth_CreateValetToken) private createValetKey: CreateValetToken, diff --git a/packages/auth/src/Infra/InversifyExpressUtils/Middleware/ApiGatewayAuthMiddleware.spec.ts b/packages/auth/src/Infra/InversifyExpressUtils/Middleware/ApiGatewayAuthMiddleware.spec.ts deleted file mode 100644 index 0b50305bf..000000000 --- a/packages/auth/src/Infra/InversifyExpressUtils/Middleware/ApiGatewayAuthMiddleware.spec.ts +++ /dev/null @@ -1,99 +0,0 @@ -import 'reflect-metadata' - -import { ApiGatewayAuthMiddleware } from './ApiGatewayAuthMiddleware' -import { NextFunction, Request, Response } from 'express' -import { Logger } from 'winston' -import { CrossServiceTokenData, TokenDecoderInterface } from '@standardnotes/security' -import { RoleName } from '@standardnotes/domain-core' - -describe('ApiGatewayAuthMiddleware', () => { - let tokenDecoder: TokenDecoderInterface - let request: Request - let response: Response - let next: NextFunction - - const logger = { - debug: jest.fn(), - } as unknown as jest.Mocked - - const createMiddleware = () => new ApiGatewayAuthMiddleware(tokenDecoder, logger) - - beforeEach(() => { - tokenDecoder = {} as jest.Mocked> - tokenDecoder.decodeToken = jest.fn().mockReturnValue({ - user: { - uuid: '1-2-3', - email: 'test@test.te', - }, - roles: [ - { - uuid: 'a-b-c', - name: RoleName.NAMES.CoreUser, - }, - ], - }) - - request = { - headers: {}, - } as jest.Mocked - response = { - locals: {}, - } as jest.Mocked - response.status = jest.fn().mockReturnThis() - response.send = jest.fn() - next = jest.fn() - }) - - it('should authorize user', async () => { - request.headers['x-auth-token'] = 'auth-jwt-token' - - await createMiddleware().handler(request, response, next) - - expect(response.locals.user).toEqual({ - uuid: '1-2-3', - email: 'test@test.te', - }) - expect(response.locals.roles).toEqual([ - { - uuid: 'a-b-c', - name: RoleName.NAMES.CoreUser, - }, - ]) - - expect(next).toHaveBeenCalled() - }) - - it('should not authorize if request is missing auth jwt token in headers', async () => { - await createMiddleware().handler(request, response, next) - - expect(response.status).toHaveBeenCalledWith(401) - expect(next).not.toHaveBeenCalled() - }) - - it('should not authorize if auth jwt token is malformed', async () => { - request.headers['x-auth-token'] = 'auth-jwt-token' - - tokenDecoder.decodeToken = jest.fn().mockReturnValue(undefined) - - await createMiddleware().handler(request, response, next) - - expect(response.status).toHaveBeenCalledWith(401) - expect(next).not.toHaveBeenCalled() - }) - - it('should pass the error to next middleware if one occurres', async () => { - request.headers['x-auth-token'] = 'auth-jwt-token' - - const error = new Error('Ooops') - - tokenDecoder.decodeToken = jest.fn().mockImplementation(() => { - throw error - }) - - await createMiddleware().handler(request, response, next) - - expect(response.status).not.toHaveBeenCalled() - - expect(next).toHaveBeenCalledWith(error) - }) -}) diff --git a/packages/auth/src/Infra/InversifyExpressUtils/Middleware/ApiGatewayAuthMiddleware.ts b/packages/auth/src/Infra/InversifyExpressUtils/Middleware/ApiGatewayAuthMiddleware.ts index 4f48d216a..753c7d63d 100644 --- a/packages/auth/src/Infra/InversifyExpressUtils/Middleware/ApiGatewayAuthMiddleware.ts +++ b/packages/auth/src/Infra/InversifyExpressUtils/Middleware/ApiGatewayAuthMiddleware.ts @@ -1,31 +1,16 @@ import { CrossServiceTokenData, TokenDecoderInterface } from '@standardnotes/security' import { NextFunction, Request, Response } from 'express' -import { inject, injectable } from 'inversify' import { BaseMiddleware } from 'inversify-express-utils' import { Logger } from 'winston' -import TYPES from '../../../Bootstrap/Types' -@injectable() -export class ApiGatewayAuthMiddleware extends BaseMiddleware { - constructor( - @inject(TYPES.Auth_CrossServiceTokenDecoder) private tokenDecoder: TokenDecoderInterface, - @inject(TYPES.Auth_Logger) private logger: Logger, - ) { +export abstract class ApiGatewayAuthMiddleware extends BaseMiddleware { + constructor(private tokenDecoder: TokenDecoderInterface, private logger: Logger) { super() } async handler(request: Request, response: Response, next: NextFunction): Promise { try { - if (!request.headers['x-auth-token']) { - this.logger.debug('ApiGatewayAuthMiddleware missing x-auth-token header.') - - response.status(401).send({ - error: { - tag: 'invalid-auth', - message: 'Invalid login credentials.', - }, - }) - + if (!this.handleMissingToken(request, response, next)) { return } @@ -56,4 +41,6 @@ export class ApiGatewayAuthMiddleware extends BaseMiddleware { return next(error) } } + + protected abstract handleMissingToken(request: Request, response: Response, next: NextFunction): boolean } diff --git a/packages/auth/src/Infra/InversifyExpressUtils/Middleware/AuthMiddleware.spec.ts b/packages/auth/src/Infra/InversifyExpressUtils/Middleware/AuthMiddleware.spec.ts deleted file mode 100644 index d0e8bbbed..000000000 --- a/packages/auth/src/Infra/InversifyExpressUtils/Middleware/AuthMiddleware.spec.ts +++ /dev/null @@ -1,79 +0,0 @@ -import 'reflect-metadata' - -import { AuthMiddleware } from './AuthMiddleware' -import { NextFunction, Request, Response } from 'express' -import { User } from '../../../Domain/User/User' -import { AuthenticateRequest } from '../../../Domain/UseCase/AuthenticateRequest' -import { Session } from '../../../Domain/Session/Session' -import { Logger } from 'winston' - -describe('AuthMiddleware', () => { - let authenticateRequest: AuthenticateRequest - let request: Request - let response: Response - let next: NextFunction - - const logger = { - debug: jest.fn(), - } as unknown as jest.Mocked - - const createMiddleware = () => new AuthMiddleware(authenticateRequest, logger) - - beforeEach(() => { - authenticateRequest = {} as jest.Mocked - authenticateRequest.execute = jest.fn() - - request = { - headers: {}, - } as jest.Mocked - response = { - locals: {}, - } as jest.Mocked - response.status = jest.fn().mockReturnThis() - response.send = jest.fn() - next = jest.fn() - }) - - it('should authorize user', async () => { - const user = {} as jest.Mocked - const session = {} as jest.Mocked - authenticateRequest.execute = jest.fn().mockReturnValue({ - success: true, - user, - session, - }) - - await createMiddleware().handler(request, response, next) - - expect(response.locals.user).toEqual(user) - expect(response.locals.session).toEqual(session) - - expect(next).toHaveBeenCalled() - }) - - it('should not authorize if request authentication fails', async () => { - authenticateRequest.execute = jest.fn().mockReturnValue({ - success: false, - responseCode: 401, - }) - - await createMiddleware().handler(request, response, next) - - expect(response.status).toHaveBeenCalledWith(401) - expect(next).not.toHaveBeenCalled() - }) - - it('should pass the error to next middleware if one occurres', async () => { - const error = new Error('Ooops') - - authenticateRequest.execute = jest.fn().mockImplementation(() => { - throw error - }) - - await createMiddleware().handler(request, response, next) - - expect(response.status).not.toHaveBeenCalled() - - expect(next).toHaveBeenCalledWith(error) - }) -}) diff --git a/packages/auth/src/Infra/InversifyExpressUtils/Middleware/AuthMiddleware.ts b/packages/auth/src/Infra/InversifyExpressUtils/Middleware/AuthMiddleware.ts deleted file mode 100644 index 45173a897..000000000 --- a/packages/auth/src/Infra/InversifyExpressUtils/Middleware/AuthMiddleware.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { NextFunction, Request, Response } from 'express' -import { inject, injectable } from 'inversify' -import { BaseMiddleware } from 'inversify-express-utils' -import { Logger } from 'winston' -import TYPES from '../../../Bootstrap/Types' -import { AuthenticateRequest } from '../../../Domain/UseCase/AuthenticateRequest' - -@injectable() -export class AuthMiddleware extends BaseMiddleware { - constructor( - @inject(TYPES.Auth_AuthenticateRequest) private authenticateRequest: AuthenticateRequest, - @inject(TYPES.Auth_Logger) private logger: Logger, - ) { - super() - } - - async handler(request: Request, response: Response, next: NextFunction): Promise { - try { - const authenticateRequestResponse = await this.authenticateRequest.execute({ - authorizationHeader: request.headers.authorization, - }) - - if (!authenticateRequestResponse.success) { - this.logger.debug('AuthMiddleware authentication failure.') - - response.status(authenticateRequestResponse.responseCode).send({ - error: { - tag: authenticateRequestResponse.errorTag, - message: authenticateRequestResponse.errorMessage, - }, - }) - - return - } - - response.locals.user = authenticateRequestResponse.user - response.locals.session = authenticateRequestResponse.session - response.locals.readOnlyAccess = authenticateRequestResponse.session?.readonlyAccess ?? false - - return next() - } catch (error) { - return next(error) - } - } -} diff --git a/packages/auth/src/Infra/InversifyExpressUtils/Middleware/AuthMiddlewareWithoutResponse.spec.ts b/packages/auth/src/Infra/InversifyExpressUtils/Middleware/AuthMiddlewareWithoutResponse.spec.ts deleted file mode 100644 index d2d57cf08..000000000 --- a/packages/auth/src/Infra/InversifyExpressUtils/Middleware/AuthMiddlewareWithoutResponse.spec.ts +++ /dev/null @@ -1,68 +0,0 @@ -import 'reflect-metadata' - -import { AuthMiddlewareWithoutResponse } from './AuthMiddlewareWithoutResponse' -import { NextFunction, Request, Response } from 'express' -import { User } from '../../../Domain/User/User' -import { AuthenticateRequest } from '../../../Domain/UseCase/AuthenticateRequest' -import { Session } from '../../../Domain/Session/Session' - -describe('AuthMiddlewareWithoutResponse', () => { - let authenticateRequest: AuthenticateRequest - let request: Request - let response: Response - let next: NextFunction - - const createMiddleware = () => new AuthMiddlewareWithoutResponse(authenticateRequest) - - beforeEach(() => { - authenticateRequest = {} as jest.Mocked - authenticateRequest.execute = jest.fn() - - request = { - headers: {}, - } as jest.Mocked - response = { - locals: {}, - } as jest.Mocked - response.status = jest.fn().mockReturnThis() - response.send = jest.fn() - next = jest.fn() - }) - - it('should authorize user', async () => { - const user = {} as jest.Mocked - const session = {} as jest.Mocked - authenticateRequest.execute = jest.fn().mockReturnValue({ - success: true, - user, - session, - }) - - await createMiddleware().handler(request, response, next) - - expect(response.locals.user).toEqual(user) - expect(response.locals.session).toEqual(session) - - expect(next).toHaveBeenCalled() - }) - - it('should skip middleware if authentication fails', async () => { - authenticateRequest.execute = jest.fn().mockReturnValue({ - success: false, - }) - - await createMiddleware().handler(request, response, next) - - expect(next).toHaveBeenCalled() - }) - - it('should skip middleware if authentication errors', async () => { - authenticateRequest.execute = jest.fn().mockImplementation(() => { - throw new Error('Ooops') - }) - - await createMiddleware().handler(request, response, next) - - expect(next).toHaveBeenCalled() - }) -}) diff --git a/packages/auth/src/Infra/InversifyExpressUtils/Middleware/AuthMiddlewareWithoutResponse.ts b/packages/auth/src/Infra/InversifyExpressUtils/Middleware/AuthMiddlewareWithoutResponse.ts deleted file mode 100644 index 9d5ed5b4f..000000000 --- a/packages/auth/src/Infra/InversifyExpressUtils/Middleware/AuthMiddlewareWithoutResponse.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { NextFunction, Request, Response } from 'express' -import { inject, injectable } from 'inversify' -import { BaseMiddleware } from 'inversify-express-utils' -import TYPES from '../../../Bootstrap/Types' -import { AuthenticateRequest } from '../../../Domain/UseCase/AuthenticateRequest' - -@injectable() -export class AuthMiddlewareWithoutResponse extends BaseMiddleware { - constructor(@inject(TYPES.Auth_AuthenticateRequest) private authenticateRequest: AuthenticateRequest) { - super() - } - - async handler(request: Request, response: Response, next: NextFunction): Promise { - try { - const authenticateRequestResponse = await this.authenticateRequest.execute({ - authorizationHeader: request.headers.authorization, - }) - - if (!authenticateRequestResponse.success) { - return next() - } - - response.locals.user = authenticateRequestResponse.user - response.locals.session = authenticateRequestResponse.session - response.locals.readOnlyAccess = authenticateRequestResponse.session?.readonlyAccess ?? false - - return next() - } catch (error) { - return next() - } - } -} diff --git a/packages/auth/src/Infra/InversifyExpressUtils/Middleware/OptionalCrossServiceTokenMiddleware.ts b/packages/auth/src/Infra/InversifyExpressUtils/Middleware/OptionalCrossServiceTokenMiddleware.ts new file mode 100644 index 000000000..4c5f4a83f --- /dev/null +++ b/packages/auth/src/Infra/InversifyExpressUtils/Middleware/OptionalCrossServiceTokenMiddleware.ts @@ -0,0 +1,27 @@ +import { CrossServiceTokenData, TokenDecoderInterface } from '@standardnotes/security' +import { NextFunction, Request, Response } from 'express' +import { inject, injectable } from 'inversify' +import { Logger } from 'winston' + +import TYPES from '../../../Bootstrap/Types' +import { ApiGatewayAuthMiddleware } from './ApiGatewayAuthMiddleware' + +@injectable() +export class OptionalCrossServiceTokenMiddleware extends ApiGatewayAuthMiddleware { + constructor( + @inject(TYPES.Auth_CrossServiceTokenDecoder) tokenDecoder: TokenDecoderInterface, + @inject(TYPES.Auth_Logger) logger: Logger, + ) { + super(tokenDecoder, logger) + } + + protected override handleMissingToken(request: Request, _response: Response, next: NextFunction): boolean { + if (!request.headers['x-auth-token']) { + next() + + return false + } + + return true + } +} diff --git a/packages/auth/src/Infra/InversifyExpressUtils/Middleware/RequiredCrossServiceTokenMiddleware.ts b/packages/auth/src/Infra/InversifyExpressUtils/Middleware/RequiredCrossServiceTokenMiddleware.ts new file mode 100644 index 000000000..3a5ed809d --- /dev/null +++ b/packages/auth/src/Infra/InversifyExpressUtils/Middleware/RequiredCrossServiceTokenMiddleware.ts @@ -0,0 +1,32 @@ +import { CrossServiceTokenData, TokenDecoderInterface } from '@standardnotes/security' +import { NextFunction, Request, Response } from 'express' +import { inject, injectable } from 'inversify' +import { Logger } from 'winston' + +import TYPES from '../../../Bootstrap/Types' +import { ApiGatewayAuthMiddleware } from './ApiGatewayAuthMiddleware' + +@injectable() +export class RequiredCrossServiceTokenMiddleware extends ApiGatewayAuthMiddleware { + constructor( + @inject(TYPES.Auth_CrossServiceTokenDecoder) tokenDecoder: TokenDecoderInterface, + @inject(TYPES.Auth_Logger) logger: Logger, + ) { + super(tokenDecoder, logger) + } + + protected override handleMissingToken(request: Request, response: Response, _next: NextFunction): boolean { + if (!request.headers['x-auth-token']) { + response.status(401).send({ + error: { + tag: 'invalid-auth', + message: 'Invalid login credentials.', + }, + }) + + return false + } + + return true + } +}