UNPKG

7.4 kBJavaScriptView Raw
1"use strict";
2var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3 function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4 return new (P || (P = Promise))(function (resolve, reject) {
5 function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6 function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7 function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8 step((generator = generator.apply(thisArg, _arguments || [])).next());
9 });
10};
11var __importDefault = (this && this.__importDefault) || function (mod) {
12 return (mod && mod.__esModule) ? mod : { "default": mod };
13};
14Object.defineProperty(exports, "__esModule", { value: true });
15exports.JWTStrategy = void 0;
16const debug_1 = __importDefault(require("debug"));
17const omit_1 = __importDefault(require("lodash/omit"));
18const errors_1 = require("@feathersjs/errors");
19// @ts-ignore
20const long_timeout_1 = __importDefault(require("long-timeout"));
21const strategy_1 = require("./strategy");
22const debug = (0, debug_1.default)('@feathersjs/authentication/jwt');
23const SPLIT_HEADER = /(\S+)\s+(\S+)/;
24class JWTStrategy extends strategy_1.AuthenticationBaseStrategy {
25 constructor() {
26 super(...arguments);
27 this.expirationTimers = new WeakMap();
28 }
29 get configuration() {
30 const authConfig = this.authentication.configuration;
31 const config = super.configuration;
32 return Object.assign({ service: authConfig.service, entity: authConfig.entity, entityId: authConfig.entityId, header: 'Authorization', schemes: ['Bearer', 'JWT'] }, config);
33 }
34 handleConnection(event, connection, authResult) {
35 return __awaiter(this, void 0, void 0, function* () {
36 const isValidLogout = event === 'logout' && connection.authentication && authResult &&
37 connection.authentication.accessToken === authResult.accessToken;
38 const { accessToken } = authResult || {};
39 if (accessToken && event === 'login') {
40 debug('Adding authentication information to connection');
41 const { exp } = yield this.authentication.verifyAccessToken(accessToken);
42 // The time (in ms) until the token expires
43 const duration = (exp * 1000) - Date.now();
44 // This may have to be a `logout` event but right now we don't want
45 // the whole context object lingering around until the timer is gone
46 const timer = long_timeout_1.default.setTimeout(() => this.app.emit('disconnect', connection), duration);
47 debug(`Registering connection expiration timer for ${duration}ms`);
48 long_timeout_1.default.clearTimeout(this.expirationTimers.get(connection));
49 this.expirationTimers.set(connection, timer);
50 debug('Adding authentication information to connection');
51 connection.authentication = {
52 strategy: this.name,
53 accessToken
54 };
55 }
56 else if (event === 'disconnect' || isValidLogout) {
57 debug('Removing authentication information and expiration timer from connection');
58 const { entity } = this.configuration;
59 delete connection[entity];
60 delete connection.authentication;
61 long_timeout_1.default.clearTimeout(this.expirationTimers.get(connection));
62 this.expirationTimers.delete(connection);
63 }
64 });
65 }
66 verifyConfiguration() {
67 const allowedKeys = ['entity', 'entityId', 'service', 'header', 'schemes'];
68 for (const key of Object.keys(this.configuration)) {
69 if (!allowedKeys.includes(key)) {
70 throw new Error(`Invalid JwtStrategy option 'authentication.${this.name}.${key}'. Did you mean to set it in 'authentication.jwtOptions'?`);
71 }
72 }
73 if (typeof this.configuration.header !== 'string') {
74 throw new Error(`The 'header' option for the ${this.name} strategy must be a string`);
75 }
76 }
77 getEntityQuery(_params) {
78 return __awaiter(this, void 0, void 0, function* () {
79 return {};
80 });
81 }
82 /**
83 * Return the entity for a given id
84 * @param id The id to use
85 * @param params Service call parameters
86 */
87 getEntity(id, params) {
88 return __awaiter(this, void 0, void 0, function* () {
89 const entityService = this.entityService;
90 const { entity } = this.configuration;
91 debug('Getting entity', id);
92 if (entityService === null) {
93 throw new errors_1.NotAuthenticated(`Could not find entity service`);
94 }
95 const query = yield this.getEntityQuery(params);
96 const getParams = Object.assign({}, (0, omit_1.default)(params, 'provider'), { query });
97 const result = yield entityService.get(id, getParams);
98 if (!params.provider) {
99 return result;
100 }
101 return entityService.get(id, Object.assign(Object.assign({}, params), { [entity]: result }));
102 });
103 }
104 getEntityId(authResult, _params) {
105 return __awaiter(this, void 0, void 0, function* () {
106 return authResult.authentication.payload.sub;
107 });
108 }
109 authenticate(authentication, params) {
110 return __awaiter(this, void 0, void 0, function* () {
111 const { accessToken } = authentication;
112 const { entity } = this.configuration;
113 if (!accessToken) {
114 throw new errors_1.NotAuthenticated('No access token');
115 }
116 const payload = yield this.authentication.verifyAccessToken(accessToken, params.jwt);
117 const result = {
118 accessToken,
119 authentication: {
120 strategy: 'jwt',
121 accessToken,
122 payload
123 }
124 };
125 if (entity === null) {
126 return result;
127 }
128 const entityId = yield this.getEntityId(result, params);
129 const value = yield this.getEntity(entityId, params);
130 return Object.assign(Object.assign({}, result), { [entity]: value });
131 });
132 }
133 parse(req) {
134 return __awaiter(this, void 0, void 0, function* () {
135 const { header, schemes } = this.configuration;
136 const headerValue = req.headers && req.headers[header.toLowerCase()];
137 if (!headerValue || typeof headerValue !== 'string') {
138 return null;
139 }
140 debug('Found parsed header value');
141 const [, scheme, schemeValue] = headerValue.match(SPLIT_HEADER) || [];
142 const hasScheme = scheme && schemes.some(current => new RegExp(current, 'i').test(scheme));
143 if (scheme && !hasScheme) {
144 return null;
145 }
146 return {
147 strategy: this.name,
148 accessToken: hasScheme ? schemeValue : headerValue
149 };
150 });
151 }
152}
153exports.JWTStrategy = JWTStrategy;
154//# sourceMappingURL=jwt.js.map
\No newline at end of file