diff --git a/packages/express-support/src/auth-utils.ts b/packages/express-support/src/auth-utils.ts index 326b06995..5315f886f 100644 --- a/packages/express-support/src/auth-utils.ts +++ b/packages/express-support/src/auth-utils.ts @@ -1,6 +1,8 @@ -import express, { NextFunction } from 'express' +import express, { NextFunction, RequestHandler } from 'express' +import { ParamsDictionary } from 'express-serve-static-core' import passport from 'passport' -import { EndpointArgs } from './types' +import { ParsedQs } from 'qs' +import { EndpointArgs, hasEndpointOpts, HasEndpointOpts } from './types' export const checkUserIsInRole = (opts: { roles: string | string[] }) => (req: express.Request, res: express.Response, next: NextFunction) => { if (!opts?.roles || opts.roles.length === 0) { @@ -20,24 +22,50 @@ export const checkUserIsInRole = (opts: { roles: string | string[] }) => (req: e return next() } -const checkAuthenticationImpl = (req: express.Request, res: express.Response, opts?: EndpointArgs) => { +const checkAuthenticationImpl = (req: express.Request, res: express.Response, next: express.NextFunction, opts?: EndpointArgs) => { if (!opts || !opts.authentication || opts.authentication.enabled === false) { - return + return next() } if (!opts.authentication.strategy) { return res.status(401).end() } - passport.authenticate(opts.authentication.strategy) - - if (typeof req.isAuthenticated !== 'function' || !req.isAuthenticated()) { - return res.status(403).end() - } - return + const options = { authInfo: true, session: false } + passport + .authenticate( + opts.authentication.strategy, + options, + ( + err: any, + user?: Express.User | false | null, + _info?: object | string | Array, + _status?: number | Array + ) => { + if (err) { + return next({ statusCode: 403, message: err }) + } else if (!user) { + return next({ statusCode: 403, message: 'user not found' }) + } + if (options.session) { + req.logIn(user, function (err) { + if (err) { + return next(err) + } + return res.send(user) + }) + } + return next() + } + ) + .call(this, req, res, next) + // next() } -const checkAuthorizationImpl = (req: express.Request, res: express.Response, opts?: EndpointArgs) => { +const checkAuthorizationImpl = (req: express.Request, res: express.Response, next: express.NextFunction, opts?: EndpointArgs) => { if (!opts || !opts.authentication || !opts.authorization || opts.authentication.enabled === false || opts?.authorization.enabled === false) { - return + return next() } + /*if (!req.isAuthenticated()) { + return sendErrorResponse(res, 403, 'Authorization with an unauthenticated request is not possible') + }*/ const authorization = opts.authorization if (!authorization.enforcer && (!authorization.requireUserInRoles || authorization.requireUserInRoles.length === 0)) { @@ -55,31 +83,43 @@ const checkAuthorizationImpl = (req: express.Request, res: express.Response, opt return res.status(403).end() } } - return + return next() } -const executeRequestHandlers = (req: express.Request, res: express.Response, next: NextFunction, opts?: EndpointArgs) => { - if (opts?.handlers) { - opts.handlers.forEach((requestHandler) => requestHandler(req, res, next)) - } +export const checkAuthenticationOnly = (opts?: EndpointArgs) => (req: express.Request, res: express.Response, next: express.NextFunction) => { + // executeRequestHandlers(req, res, next, opts) + return checkAuthenticationImpl(req, res, next, opts) } -export const checkAuthenticationOnly = (opts?: EndpointArgs) => (req: express.Request, res: express.Response, next: NextFunction) => { - executeRequestHandlers(req, res, next, opts) - return checkAuthenticationImpl(req, res, opts) ?? next() +export const checkAuthorizationOnly = (opts?: EndpointArgs) => (req: express.Request, res: express.Response, next: express.NextFunction) => { + // executeRequestHandlers(req, res, next, opts) + return checkAuthorizationImpl(req, res, next, opts) +} +export const checkAuth = (opts?: EndpointArgs): RequestHandler>[] => { + const handlers: RequestHandler>[] = [] + handlers.push(checkAuthenticationOnly(opts)) + handlers.push(checkAuthorizationOnly(opts)) + opts?.handlers && handlers.push(...opts.handlers) + return handlers } -export const checkAuthorizationOnly = (opts?: EndpointArgs) => (req: express.Request, res: express.Response, next: NextFunction) => { - executeRequestHandlers(req, res, next, opts) - return checkAuthorizationImpl(req, res, opts) ?? next() +export function copyGlobalAuthToEndpoint(args?: { opts?: HasEndpointOpts; key: string }) { + const opts = args?.opts + const key = args?.key + if (!opts || !key || !hasEndpointOpts(opts)) { + return + } + if (opts.endpointOpts?.globalAuth) { + if (opts.endpointOpts[key]?.disableGlobalAuth === true) { + return + } + opts.endpointOpts[key] = { + ...opts.endpointOpts[key], + endpoint: { ...opts.endpointOpts.globalAuth, ...opts.endpointOpts[key]?.endpoint }, + } + } } -export const checkAuth = (opts?: EndpointArgs) => (req: express.Request, res: express.Response, next: NextFunction) => { - /*const handlers = /!*this._handlers ??*!/ [] - checkAuthenticationImpl(req, res, opts)) - handlers.push(checkAuthorizationImpl(req, res, opts)) - handlers.push(next) - return handlers -*/ - executeRequestHandlers(req, res, next, opts) - return checkAuthenticationImpl(req, res, opts) ?? checkAuthorizationImpl(req, res, opts) ?? next() + +export function copyGlobalAuthToEndpoints(args?: { opts?: HasEndpointOpts; keys: string[] }) { + args?.keys.forEach((key) => copyGlobalAuthToEndpoint({ opts: args?.opts, key })) } diff --git a/packages/express-support/src/express-utils.ts b/packages/express-support/src/express-utils.ts new file mode 100644 index 000000000..3ff540241 --- /dev/null +++ b/packages/express-support/src/express-utils.ts @@ -0,0 +1,33 @@ +import express, { NextFunction } from 'express' +export function sendErrorResponse(response: express.Response, statusCode: number, message: string | object, error?: Error) { + console.log(`sendErrorResponse: ${message}`) + if (error) { + console.log(JSON.stringify(error)) + } + if (response.headersSent) { + console.log(`sendErrorResponse headers already sent`) + return + } + if (response.headersSent) { + return + } + response.statusCode = statusCode + if (typeof message === 'string' && !message.startsWith('{')) { + message = { error: message } + } + if (typeof message === 'string' && message.startsWith('{')) { + return response.status(statusCode).end(message) + } + return response.status(statusCode).json(message) +} + +export const jsonErrorHandler = (err: any, req: express.Request, res: express.Response, next: NextFunction) => { + const statusCode: number = 'statusCode' in err ? err.statusCode : 500 + const errorMsg = typeof err === 'string' ? err : err.message + if (res.headersSent) { + console.log('Headers already sent, when calling error handler. Will defer to next error handler') + console.log(`Error was: ${JSON.stringify(err)}`) + return next(err) + } + return sendErrorResponse(res, statusCode, errorMsg, typeof err !== 'string' ? err : undefined) +} diff --git a/packages/express-support/src/functions.ts b/packages/express-support/src/functions.ts index 5eb95bce0..324c641e3 100644 --- a/packages/express-support/src/functions.ts +++ b/packages/express-support/src/functions.ts @@ -1,31 +1,6 @@ -import express, { NextFunction } from 'express' -import process from 'process' - export function env(key?: string, prefix?: string): string | undefined { if (!key) { return } return process.env[`${prefix ? prefix.trim() : ''}${key}`] } - -export function sendErrorResponse(response: express.Response, statusCode: number, message: string | object, error?: Error) { - console.log(message) - if (error) { - console.log(error) - } - response.statusCode = statusCode - if (typeof message === 'string' && !message.startsWith('{')) { - message = { error: message } - } - if (typeof message === 'string' && message.startsWith('{')) { - return response.status(statusCode).end(message) - } - return response.status(statusCode).json(message) -} - -export const jsonErrorHandler = (err: any, req: express.Request, res: express.Response, next: NextFunction) => { - if (res.headersSent) { - return next(err) - } - return sendErrorResponse(res, 500, err.message, err) -} diff --git a/packages/express-support/src/index.ts b/packages/express-support/src/index.ts index ecb92ded6..10f99734f 100644 --- a/packages/express-support/src/index.ts +++ b/packages/express-support/src/index.ts @@ -1,4 +1,7 @@ +export * from './entra-id-auth' +export * from './static-bearer-auth' export * from './auth-utils' -export * from './builders' +export * from './express-builders' export * from './types' -export { sendErrorResponse, jsonErrorHandler, env } from './functions' +export { sendErrorResponse, jsonErrorHandler } from './express-utils' +export * from './functions'