@feathersjs/authentication
Version:
Add Authentication to your FeathersJS app.
148 lines • 6.61 kB
JavaScript
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AuthenticationService = void 0;
const merge_1 = __importDefault(require("lodash/merge"));
const errors_1 = require("@feathersjs/errors");
require("@feathersjs/transport-commons");
const commons_1 = require("@feathersjs/commons");
const schema_1 = require("@feathersjs/schema");
const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
const hooks_1 = require("@feathersjs/hooks");
const core_1 = require("./core");
const hooks_2 = require("./hooks");
const debug = (0, commons_1.createDebug)('@feathersjs/authentication/service');
class AuthenticationService extends core_1.AuthenticationBase {
constructor(app, configKey = 'authentication', options = {}) {
super(app, configKey, options);
(0, hooks_1.hooks)(this, {
create: [(0, schema_1.resolveDispatch)(), (0, hooks_2.event)('login'), (0, hooks_2.connection)('login')],
remove: [(0, schema_1.resolveDispatch)(), (0, hooks_2.event)('logout'), (0, hooks_2.connection)('logout')]
});
this.app.on('disconnect', async (connection) => {
await this.handleConnection('disconnect', connection);
});
if (typeof app.defaultAuthentication !== 'function') {
app.defaultAuthentication = function (location) {
const configKey = app.get('defaultAuthentication');
const path = location ||
Object.keys(this.services).find((current) => this.service(current).configKey === configKey);
return path ? this.service(path) : null;
};
}
}
/**
* Return the payload for a JWT based on the authentication result.
* Called internally by the `create` method.
*
* @param _authResult The current authentication result
* @param params The service call parameters
*/
async getPayload(_authResult, params) {
// Uses `params.payload` or returns an empty payload
const { payload = {} } = params;
return payload;
}
/**
* Returns the JWT options based on an authentication result.
* By default sets the JWT subject to the entity id.
*
* @param authResult The authentication result
* @param params Service call parameters
*/
async getTokenOptions(authResult, params) {
const { service, entity, entityId } = this.configuration;
const jwtOptions = (0, merge_1.default)({}, params.jwtOptions, params.jwt);
const value = service && entity && authResult[entity];
// Set the subject to the entity id if it is available
if (value && !jwtOptions.subject) {
const idProperty = entityId || this.app.service(service).id;
const subject = value[idProperty];
if (subject === undefined) {
throw new errors_1.NotAuthenticated(`Can not set subject from ${entity}.${idProperty}`);
}
jwtOptions.subject = `${subject}`;
}
return jwtOptions;
}
/**
* Create and return a new JWT for a given authentication request.
* Will trigger the `login` event.
*
* @param data The authentication request (should include `strategy` key)
* @param params Service call parameters
*/
async create(data, params) {
const authStrategies = params.authStrategies || this.configuration.authStrategies;
if (!authStrategies.length) {
throw new errors_1.NotAuthenticated('No authentication strategies allowed for creating a JWT (`authStrategies`)');
}
const authResult = await this.authenticate(data, params, ...authStrategies);
debug('Got authentication result', authResult);
if (authResult.accessToken) {
return authResult;
}
const [payload, jwtOptions] = await Promise.all([
this.getPayload(authResult, params),
this.getTokenOptions(authResult, params)
]);
debug('Creating JWT with', payload, jwtOptions);
const accessToken = await this.createAccessToken(payload, jwtOptions, params.secret);
return {
accessToken,
...authResult,
authentication: {
...authResult.authentication,
payload: jsonwebtoken_1.default.decode(accessToken)
}
};
}
/**
* Mark a JWT as removed. By default only verifies the JWT and returns the result.
* Triggers the `logout` event.
*
* @param id The JWT to remove or null
* @param params Service call parameters
*/
async remove(id, params) {
const { authentication } = params;
const { authStrategies } = this.configuration;
// When an id is passed it is expected to be the authentication `accessToken`
if (id !== null && id !== authentication.accessToken) {
throw new errors_1.NotAuthenticated('Invalid access token');
}
debug('Verifying authentication strategy in remove');
return this.authenticate(authentication, params, ...authStrategies);
}
/**
* Validates the service configuration.
*/
async setup() {
await super.setup();
// The setup method checks for valid settings and registers the
// connection and event (login, logout) hooks
const { secret, service, entity, entityId } = this.configuration;
if (typeof secret !== 'string') {
throw new Error("A 'secret' must be provided in your authentication configuration");
}
if (entity !== null) {
if (service === undefined) {
throw new Error("The 'service' option is not set in the authentication configuration");
}
if (this.app.service(service) === undefined) {
throw new Error(`The '${service}' entity service does not exist (set to 'null' if it is not required)`);
}
if (this.app.service(service).id === undefined && entityId === undefined) {
throw new Error(`The '${service}' service does not have an 'id' property and no 'entityId' option is set.`);
}
}
const publishable = this;
if (typeof publishable.publish === 'function') {
publishable.publish(() => null);
}
}
}
exports.AuthenticationService = AuthenticationService;
//# sourceMappingURL=service.js.map
;