mirror of
https://github.com/standardnotes/server
synced 2026-01-16 20:04:32 -05:00
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
This commit is contained in:
@@ -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<AuthMiddleware>(TYPES.AuthMiddleware).to(AuthMiddleware)
|
||||
container
|
||||
.bind<RequiredCrossServiceTokenMiddleware>(TYPES.RequiredCrossServiceTokenMiddleware)
|
||||
.to(RequiredCrossServiceTokenMiddleware)
|
||||
container
|
||||
.bind<OptionalCrossServiceTokenMiddleware>(TYPES.OptionalCrossServiceTokenMiddleware)
|
||||
.to(OptionalCrossServiceTokenMiddleware)
|
||||
container.bind<WebSocketAuthMiddleware>(TYPES.WebSocketAuthMiddleware).to(WebSocketAuthMiddleware)
|
||||
container
|
||||
.bind<SubscriptionTokenAuthMiddleware>(TYPES.SubscriptionTokenAuthMiddleware)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<void> {
|
||||
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
|
||||
|
||||
|
||||
@@ -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<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
await this.httpService.callLegacySyncingServer(request, response, request.path.substring(1), request.body)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,7 @@ export class ActionsController extends BaseHttpController {
|
||||
)
|
||||
}
|
||||
|
||||
@httpGet('/login-params')
|
||||
@httpGet('/login-params', TYPES.OptionalCrossServiceTokenMiddleware)
|
||||
async loginParams(request: Request, response: Response): Promise<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
await this.serviceProxy.callAuthServer(
|
||||
request,
|
||||
|
||||
@@ -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<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
await this.httpService.callAuthServer(
|
||||
request,
|
||||
|
||||
@@ -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<void> {
|
||||
await this.httpService.callAuthServer(
|
||||
request,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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<results.JsonResult> {
|
||||
|
||||
@@ -14,7 +14,7 @@ export class SessionsController extends BaseHttpController {
|
||||
super()
|
||||
}
|
||||
|
||||
@httpGet('/', TYPES.AuthMiddleware)
|
||||
@httpGet('/', TYPES.RequiredCrossServiceTokenMiddleware)
|
||||
async getSessions(request: Request, response: Response): Promise<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
await this.httpService.callAuthServer(
|
||||
request,
|
||||
|
||||
@@ -15,7 +15,7 @@ export class SubscriptionInvitesController extends BaseHttpController {
|
||||
super()
|
||||
}
|
||||
|
||||
@httpPost('/', TYPES.AuthMiddleware)
|
||||
@httpPost('/', TYPES.RequiredCrossServiceTokenMiddleware)
|
||||
async inviteToSubscriptionSharing(request: Request, response: Response): Promise<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
await this.httpService.callAuthServer(
|
||||
request,
|
||||
|
||||
@@ -15,7 +15,7 @@ export class TokensController extends BaseHttpController {
|
||||
super()
|
||||
}
|
||||
|
||||
@httpPost('/', TYPES.AuthMiddleware)
|
||||
@httpPost('/', TYPES.RequiredCrossServiceTokenMiddleware)
|
||||
async createToken(request: Request, response: Response): Promise<void> {
|
||||
await this.httpService.callAuthServer(
|
||||
request,
|
||||
|
||||
@@ -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<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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<results.StatusCodeResult> {
|
||||
return this.statusCode(401)
|
||||
}
|
||||
|
||||
@httpPost('/:userUuid/integrations/listed', TYPES.AuthMiddleware)
|
||||
@httpPost('/:userUuid/integrations/listed', TYPES.RequiredCrossServiceTokenMiddleware)
|
||||
async createListedAccount(request: Request, response: Response): Promise<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
await this.httpService.callAuthServer(
|
||||
request,
|
||||
|
||||
@@ -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<void> {
|
||||
await this.httpService.callWebSocketServer(
|
||||
request,
|
||||
|
||||
@@ -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<void> {
|
||||
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<void> {
|
||||
await this.httpService.callAuthServer(
|
||||
await this.serviceProxy.callAuthServer(
|
||||
request,
|
||||
response,
|
||||
this.endpointResolver.resolveEndpointOrMethodIdentifier('POST', 'auth/pkce_params'),
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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<AuthMiddleware>(TYPES.Auth_AuthMiddleware).to(AuthMiddleware)
|
||||
container.bind<SessionMiddleware>(TYPES.Auth_SessionMiddleware).to(SessionMiddleware)
|
||||
container.bind<LockMiddleware>(TYPES.Auth_LockMiddleware).to(LockMiddleware)
|
||||
container
|
||||
.bind<AuthMiddlewareWithoutResponse>(TYPES.Auth_AuthMiddlewareWithoutResponse)
|
||||
.to(AuthMiddlewareWithoutResponse)
|
||||
container.bind<ApiGatewayAuthMiddleware>(TYPES.Auth_ApiGatewayAuthMiddleware).to(ApiGatewayAuthMiddleware)
|
||||
.bind<RequiredCrossServiceTokenMiddleware>(TYPES.Auth_RequiredCrossServiceTokenMiddleware)
|
||||
.to(RequiredCrossServiceTokenMiddleware)
|
||||
container
|
||||
.bind<OptionalCrossServiceTokenMiddleware>(TYPES.Auth_OptionalCrossServiceTokenMiddleware)
|
||||
.to(OptionalCrossServiceTokenMiddleware)
|
||||
container
|
||||
.bind<ApiGatewayOfflineAuthMiddleware>(TYPES.Auth_ApiGatewayOfflineAuthMiddleware)
|
||||
.to(ApiGatewayOfflineAuthMiddleware)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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',
|
||||
},
|
||||
|
||||
@@ -22,16 +22,6 @@ export class GetUserKeyParams implements UseCaseInterface {
|
||||
) {}
|
||||
|
||||
async execute(dto: GetUserKeyParamsDTO): Promise<GetUserKeyParamsResponse> {
|
||||
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)
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import { User } from '../../User/User'
|
||||
|
||||
export type GetUserKeyParamsDTOV1Unchallenged = {
|
||||
authenticated: boolean
|
||||
email?: string
|
||||
userUuid?: string
|
||||
authenticatedUser?: User
|
||||
}
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
import { User } from '../../User/User'
|
||||
|
||||
export type GetUserKeyParamsDTOV2Challenged = {
|
||||
authenticated: boolean
|
||||
codeChallenge: string
|
||||
email?: string
|
||||
userUuid?: string
|
||||
authenticatedUser?: User
|
||||
}
|
||||
|
||||
@@ -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<results.JsonResult> {
|
||||
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<results.JsonResult> {
|
||||
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<results.JsonResult> {
|
||||
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<results.JsonResult | void> {
|
||||
const result = await this.authController.signOut({
|
||||
readOnlyAccess: response.locals.readOnlyAccess,
|
||||
|
||||
@@ -37,7 +37,7 @@ export class InversifyExpressAuthenticatorsController extends BaseHttpController
|
||||
)
|
||||
}
|
||||
|
||||
@httpGet('/', TYPES.Auth_ApiGatewayAuthMiddleware)
|
||||
@httpGet('/', TYPES.Auth_RequiredCrossServiceTokenMiddleware)
|
||||
async list(_request: Request, response: Response): Promise<results.JsonResult> {
|
||||
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<results.JsonResult> {
|
||||
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<results.JsonResult> {
|
||||
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<results.JsonResult> {
|
||||
const result = await this.authenticatorsController.verifyRegistrationResponse({
|
||||
userUuid: response.locals.user.uuid,
|
||||
|
||||
@@ -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<results.JsonResult> {
|
||||
if (request.params.userUuid !== response.locals.user.uuid) {
|
||||
return this.json(
|
||||
|
||||
@@ -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<results.JsonResult> {
|
||||
if (response.locals.readOnlyAccess) {
|
||||
return this.json(
|
||||
|
||||
@@ -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<results.JsonResult | void> {
|
||||
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<results.JsonResult | void> {
|
||||
if (response.locals.readOnlyAccess) {
|
||||
return this.json(
|
||||
|
||||
@@ -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<results.JsonResult> {
|
||||
if (response.locals.readOnlyAccess) {
|
||||
return this.json([])
|
||||
|
||||
@@ -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<results.JsonResult> {
|
||||
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<results.JsonResult> {
|
||||
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<results.JsonResult | results.StatusCodeResult> {
|
||||
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<results.JsonResult> {
|
||||
if (response.locals.readOnlyAccess) {
|
||||
return this.json(
|
||||
|
||||
@@ -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<void> {
|
||||
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<results.JsonResult> {
|
||||
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<results.JsonResult> {
|
||||
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<results.JsonResult> {
|
||||
const result = await this.subscriptionInvitesController.listInvites({
|
||||
...request.body,
|
||||
|
||||
@@ -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<results.JsonResult> {
|
||||
const result = await this.doGetSetting.execute({
|
||||
userUuid: response.locals.user.uuid,
|
||||
|
||||
@@ -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<results.JsonResult> {
|
||||
if (response.locals.readOnlyAccess) {
|
||||
return this.json(
|
||||
|
||||
@@ -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<results.JsonResult> {
|
||||
const result = await this.userRequestsController.submitUserRequest({
|
||||
requestType: request.body.requestType,
|
||||
|
||||
@@ -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<results.JsonResult | void> {
|
||||
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<results.JsonResult> {
|
||||
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<results.JsonResult | void> {
|
||||
if (response.locals.readOnlyAccess) {
|
||||
return this.json(
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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<CrossServiceTokenData>
|
||||
let request: Request
|
||||
let response: Response
|
||||
let next: NextFunction
|
||||
|
||||
const logger = {
|
||||
debug: jest.fn(),
|
||||
} as unknown as jest.Mocked<Logger>
|
||||
|
||||
const createMiddleware = () => new ApiGatewayAuthMiddleware(tokenDecoder, logger)
|
||||
|
||||
beforeEach(() => {
|
||||
tokenDecoder = {} as jest.Mocked<TokenDecoderInterface<CrossServiceTokenData>>
|
||||
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<Request>
|
||||
response = {
|
||||
locals: {},
|
||||
} as jest.Mocked<Response>
|
||||
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)
|
||||
})
|
||||
})
|
||||
@@ -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<CrossServiceTokenData>,
|
||||
@inject(TYPES.Auth_Logger) private logger: Logger,
|
||||
) {
|
||||
export abstract class ApiGatewayAuthMiddleware extends BaseMiddleware {
|
||||
constructor(private tokenDecoder: TokenDecoderInterface<CrossServiceTokenData>, private logger: Logger) {
|
||||
super()
|
||||
}
|
||||
|
||||
async handler(request: Request, response: Response, next: NextFunction): Promise<void> {
|
||||
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
|
||||
}
|
||||
|
||||
@@ -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<Logger>
|
||||
|
||||
const createMiddleware = () => new AuthMiddleware(authenticateRequest, logger)
|
||||
|
||||
beforeEach(() => {
|
||||
authenticateRequest = {} as jest.Mocked<AuthenticateRequest>
|
||||
authenticateRequest.execute = jest.fn()
|
||||
|
||||
request = {
|
||||
headers: {},
|
||||
} as jest.Mocked<Request>
|
||||
response = {
|
||||
locals: {},
|
||||
} as jest.Mocked<Response>
|
||||
response.status = jest.fn().mockReturnThis()
|
||||
response.send = jest.fn()
|
||||
next = jest.fn()
|
||||
})
|
||||
|
||||
it('should authorize user', async () => {
|
||||
const user = {} as jest.Mocked<User>
|
||||
const session = {} as jest.Mocked<Session>
|
||||
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)
|
||||
})
|
||||
})
|
||||
@@ -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<void> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
authenticateRequest.execute = jest.fn()
|
||||
|
||||
request = {
|
||||
headers: {},
|
||||
} as jest.Mocked<Request>
|
||||
response = {
|
||||
locals: {},
|
||||
} as jest.Mocked<Response>
|
||||
response.status = jest.fn().mockReturnThis()
|
||||
response.send = jest.fn()
|
||||
next = jest.fn()
|
||||
})
|
||||
|
||||
it('should authorize user', async () => {
|
||||
const user = {} as jest.Mocked<User>
|
||||
const session = {} as jest.Mocked<Session>
|
||||
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()
|
||||
})
|
||||
})
|
||||
@@ -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<void> {
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<CrossServiceTokenData>,
|
||||
@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
|
||||
}
|
||||
}
|
||||
@@ -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<CrossServiceTokenData>,
|
||||
@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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user