Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/authentication/src/hooks/authenticate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const debug = createDebug('@feathersjs/authentication/hooks/authenticate');

export interface AuthenticateHookSettings {
service?: string;
strategies: string[];
strategies?: string[];
}

export default (originalSettings: string | AuthenticateHookSettings, ...originalStrategies: string[]) => {
Expand Down
11 changes: 6 additions & 5 deletions packages/client/test/fixture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,17 +31,18 @@ class TodoService extends Service {

export default (configurer?: (app: Application) => void) => {
const app = express.default(feathers())
// Parse HTTP bodies
.use(express.json())
.use(express.urlencoded({ extended: true }))
// Host the current directory (for index.html)
.use(express.static(process.cwd()))
.configure(express.rest());

if (typeof configurer === 'function') {
configurer.call(app, app);
}

// Parse HTTP bodies
app.use(express.json())
.use(express.urlencoded({ extended: true }))
// Host the current directory (for index.html)
.use(express.static(process.cwd()))
app
// Host our Todos service on the /todos path
.use('/todos', new TodoService({
multi: true
Expand Down
5 changes: 2 additions & 3 deletions packages/express/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,17 +49,16 @@
"access": "public"
},
"dependencies": {
"@feathersjs/authentication": "^5.0.0-pre.15",
"@feathersjs/commons": "^5.0.0-pre.15",
"@feathersjs/errors": "^5.0.0-pre.15",
"@feathersjs/feathers": "^5.0.0-pre.15",
"@feathersjs/transport-commons": "^5.0.0-pre.15",
"@types/express": "^4.17.13",
"@types/express-serve-static-core": "^4.17.27",
"express": "^4.17.2",
"lodash": "^4.17.21"
"express": "^4.17.2"
},
"devDependencies": {
"@feathersjs/authentication": "^5.0.0-pre.15",
"@feathersjs/authentication-local": "^5.0.0-pre.15",
"@feathersjs/tests": "^5.0.0-pre.15",
"@types/lodash": "^4.14.178",
Expand Down
82 changes: 36 additions & 46 deletions packages/express/src/authentication.ts
Original file line number Diff line number Diff line change
@@ -1,71 +1,61 @@
import { RequestHandler, Request, Response } from 'express';
import { HookContext } from '@feathersjs/feathers';
import { createDebug } from '@feathersjs/commons';
import { merge, flatten } from 'lodash';
import { NextFunction, RequestHandler, Request, Response } from 'express';
import { authenticate as AuthenticateHook } from '@feathersjs/authentication';

import { Application } from './declarations';

const debug = createDebug('@feathersjs/express/authentication');

type StrategyOptions = {
service?: string;
strategies: string[]
const toHandler = (func: (req: Request, res: Response, next: () => void) => Promise<void>): RequestHandler => {
return (req, res, next) => func(req, res, next).catch(error => next(error));
};

const normalizeStrategy = (_settings: string|StrategyOptions, ..._strategies: string[]) =>
typeof _settings === 'string'
? { strategies: flatten([ _settings, ..._strategies ]) }
: _settings;
export type AuthenticationSettings = {
service?: string;
strategies?: string[];
};

export function parseAuthentication (settings: any = {}): RequestHandler {
return function (req, res, next) {
const app = req.app as any;
const service = app.defaultAuthentication ? app.defaultAuthentication(settings.service) : null;
export function parseAuthentication (settings: AuthenticationSettings = {}): RequestHandler {
return toHandler(async (req, res, next) => {
const app = req.app as any as Application;
const service = app.defaultAuthentication?.(settings.service);

if (service === null) {
if (!service) {
return next();
}

const config = service.configuration;
const authStrategies = config.parseStrategies || config.authStrategies || [];
const authStrategies = settings.strategies || config.parseStrategies || config.authStrategies || [];

if (authStrategies.length === 0) {
debug('No `authStrategies` or `parseStrategies` found in authentication configuration');
return next();
}

service.parse(req, res, ...authStrategies)
.then((authentication: any) => {
if (authentication) {
debug('Parsed authentication from HTTP header', authentication);
merge(req, {
authentication,
feathers: { authentication }
});
}

next();
}).catch(next);
};
}
const authentication = await service.parse(req, res, ...authStrategies)

if (authentication) {
debug('Parsed authentication from HTTP header', authentication);
req.feathers = { ...req.feathers, authentication };
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💯 for removing Lodash!

}

export function authenticate (_settings: string|StrategyOptions, ..._strategies: string[]) {
const settings = normalizeStrategy(_settings, ..._strategies);
return next();
});
}

if (!Array.isArray(settings.strategies) || settings.strategies.length === 0) {
throw new Error('\'authenticate\' middleware requires at least one strategy name');
}
export function authenticate (settings: string | AuthenticationSettings, ...strategies: string[]): RequestHandler {
const hook = AuthenticateHook(settings, ...strategies);

return (_req: Request, _res: Response, next: NextFunction) => {
const req = _req as any;
const { app, authentication } = req;
const service = app.defaultAuthentication(settings.service);
return toHandler(async (req, _res, next) => {
const app = req.app as any as Application;
const params = req.feathers;
const context = { app, params } as any as HookContext;

debug('Authenticating with Express middleware and strategies', settings.strategies);
await hook(context);

service.authenticate(authentication, req.feathers, ...settings.strategies)
.then((authResult: any) => {
debug('Merging request with', authResult);
merge(req, authResult);
req.feathers = context.params;

next();
}).catch(next);
};
return next();
});
}
28 changes: 15 additions & 13 deletions packages/express/src/declarations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import http from 'http';
import express, { Express } from 'express';
import {
Application as FeathersApplication, Params as FeathersParams,
HookContext, ServiceMethods, ServiceInterface
HookContext, ServiceMethods, ServiceInterface, RouteLookup
} from '@feathersjs/feathers';

interface ExpressUseHandler<T, Services> {
Expand Down Expand Up @@ -33,28 +33,30 @@ export type Application<Services = any, Settings = any> =

declare module '@feathersjs/feathers/lib/declarations' {
interface ServiceOptions {
middleware?: {
before: express.RequestHandler[],
after: express.RequestHandler[]
}
express?: {
before?: express.RequestHandler[];
after?: express.RequestHandler[];
composed?: express.RequestHandler;
};
}
}

declare module 'express-serve-static-core' {
interface Request {
feathers?: Partial<FeathersParams>;
feathers?: Partial<FeathersParams>;
lookup?: RouteLookup;
}

interface Response {
data?: any;
hook?: HookContext;
data?: any;
hook?: HookContext;
}

interface IRouterMatcher<T> {
// eslint-disable-next-line
<P extends Params = ParamsDictionary, ResBody = any, ReqBody = any>(
path: PathParams,
...handlers: (RequestHandler<P, ResBody, ReqBody> | Partial<ServiceMethods> | Application)[]
): T;
// eslint-disable-next-line
<P extends Params = ParamsDictionary, ResBody = any, ReqBody = any>(
path: PathParams,
...handlers: (RequestHandler<P, ResBody, ReqBody> | Partial<ServiceMethods> | Application)[]
): T;
}
}
8 changes: 4 additions & 4 deletions packages/express/src/handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ export function notFound ({ verbose = false } = {}): RequestHandler {
}

export type ErrorHandlerOptions = {
public?: string,
logger?: boolean|{ error?: (msg: any) => void, info?: (msg: any) => void },
html?: any,
json?: any
public?: string;
logger?: boolean|{ error?: (msg: any) => void, info?: (msg: any) => void };
html?: any;
json?: any;
};

export function errorHandler (_options: ErrorHandlerOptions = {}): ErrorRequestHandler {
Expand Down
65 changes: 35 additions & 30 deletions packages/express/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,16 @@
import express, {
Express, static as _static, json, raw, text, urlencoded, query
} from 'express';
import {
Application as FeathersApplication, defaultServiceMethods
} from '@feathersjs/feathers';
import express, { Express } from 'express';
import { Application as FeathersApplication, defaultServiceMethods } from '@feathersjs/feathers';
import { routing } from '@feathersjs/transport-commons';
import { createDebug } from '@feathersjs/commons';

import { Application } from './declarations';
import { errorHandler, notFound } from './handlers';
import { parseAuthentication, authenticate } from './authentication';

export {
_static as serveStatic, _static as static, json, raw, text,
urlencoded, query, errorHandler, notFound, express as original,
authenticate, parseAuthentication
};
export { default as original, static, static as serveStatic, json, raw, text, urlencoded, query } from 'express';

export * from './rest';
export * from './authentication';
export * from './declarations';
export * from './handlers';
export * from './rest';

const debug = createDebug('@feathersjs/express');

Expand All @@ -30,10 +23,12 @@ export default function feathersExpress<S = any, C = any> (feathersApp?: Feather
throw new Error('@feathersjs/express requires a valid Feathers application instance');
}

const { use, listen } = expressApp as any;
// A mixin that provides the extended functionality
const mixin: any = {
use (location: string, ...rest: any[]) {
const app = expressApp as any as Application<S, C>;
const { use: expressUse, listen: expressListen } = expressApp as any;
const feathersUse = feathersApp.use;

Object.assign(app, {
use (location: string & keyof S, ...rest: any[]) {
let service: any;
let options = {};

Expand All @@ -60,46 +55,56 @@ export default function feathersExpress<S = any, C = any> (feathersApp?: Feather
// Check for service (any object with at least one service method)
if (hasMethod(['handle', 'set']) || !hasMethod(defaultServiceMethods)) {
debug('Passing app.use call to Express app');
return use.call(this, location, ...rest);
return expressUse.call(this, location, ...rest);
}

debug('Registering service with middleware', middleware);
// Since this is a service, call Feathers `.use`
(feathersApp as FeathersApplication).use.call(this, location, service, {
feathersUse.call(this, location, service, {
...options,
middleware
express: middleware
});

return this;
},

async listen (...args: any[]) {
const server = listen.call(this, ...args);
const server = expressListen.call(this, ...args);

await this.setup(server);
debug('Feathers application listening');

return server;
}
};
} as Application<S, C>);

const feathersDescriptors = {
const appDescriptors = {
...Object.getOwnPropertyDescriptors(Object.getPrototypeOf(app)),
...Object.getOwnPropertyDescriptors(app)
};
const newDescriptors = {
...Object.getOwnPropertyDescriptors(Object.getPrototypeOf(feathersApp)),
...Object.getOwnPropertyDescriptors(feathersApp)
};

// Copy all non-existing properties (including non-enumerables)
// that don't already exist on the Express app
Object.keys(feathersDescriptors).forEach(prop => {
const feathersProp = feathersDescriptors[prop];
const expressProp = Object.getOwnPropertyDescriptor(expressApp, prop);
Object.keys(newDescriptors).forEach(prop => {
const appProp = appDescriptors[prop];
const newProp = newDescriptors[prop];

if (expressProp === undefined && feathersProp !== undefined) {
Object.defineProperty(expressApp, prop, feathersProp);
if (appProp === undefined && newProp !== undefined) {
Object.defineProperty(expressApp, prop, newProp);
}
});

return Object.assign(expressApp, mixin);
app.configure(routing() as any);
app.use((req, _res, next) => {
req.feathers = { ...req.feathers, provider: 'rest' };
return next();
});

return app;
}

if (typeof module !== 'undefined') {
Expand Down
Loading