UNPKG

6.22 kBJavaScriptView Raw
1"use strict";
2var __importDefault = (this && this.__importDefault) || function (mod) {
3 return (mod && mod.__esModule) ? mod : { "default": mod };
4};
5Object.defineProperty(exports, "__esModule", { value: true });
6exports.JWTStrategy = void 0;
7const errors_1 = require("@feathersjs/errors");
8const commons_1 = require("@feathersjs/commons");
9// @ts-ignore
10const long_timeout_1 = __importDefault(require("long-timeout"));
11const strategy_1 = require("./strategy");
12const debug = (0, commons_1.createDebug)('@feathersjs/authentication/jwt');
13const SPLIT_HEADER = /(\S+)\s+(\S+)/;
14class JWTStrategy extends strategy_1.AuthenticationBaseStrategy {
15 constructor() {
16 super(...arguments);
17 this.expirationTimers = new WeakMap();
18 }
19 get configuration() {
20 const authConfig = this.authentication.configuration;
21 const config = super.configuration;
22 return {
23 service: authConfig.service,
24 entity: authConfig.entity,
25 entityId: authConfig.entityId,
26 header: 'Authorization',
27 schemes: ['Bearer', 'JWT'],
28 ...config
29 };
30 }
31 async handleConnection(event, connection, authResult) {
32 var _a;
33 const isValidLogout = event === 'logout' &&
34 connection.authentication &&
35 authResult &&
36 connection.authentication.accessToken === authResult.accessToken;
37 const { accessToken } = authResult || {};
38 const { entity } = this.configuration;
39 if (accessToken && event === 'login') {
40 debug('Adding authentication information to connection');
41 const { exp } = ((_a = authResult === null || authResult === void 0 ? void 0 : authResult.authentication) === null || _a === void 0 ? void 0 : _a.payload) || (await this.authentication.verifyAccessToken(accessToken));
42 // The time (in ms) until the token expires
43 const duration = exp * 1000 - Date.now();
44 const timer = long_timeout_1.default.setTimeout(() => this.app.emit('disconnect', connection), duration);
45 debug(`Registering connection expiration timer for ${duration}ms`);
46 long_timeout_1.default.clearTimeout(this.expirationTimers.get(connection));
47 this.expirationTimers.set(connection, timer);
48 debug('Adding authentication information to connection');
49 connection.authentication = {
50 strategy: this.name,
51 accessToken
52 };
53 connection[entity] = authResult[entity];
54 }
55 else if (event === 'disconnect' || isValidLogout) {
56 debug('Removing authentication information and expiration timer from connection');
57 await new Promise((resolve) => process.nextTick(() => {
58 delete connection[entity];
59 delete connection.authentication;
60 resolve(connection);
61 }));
62 long_timeout_1.default.clearTimeout(this.expirationTimers.get(connection));
63 this.expirationTimers.delete(connection);
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 async getEntityQuery(_params) {
78 return {};
79 }
80 /**
81 * Return the entity for a given id
82 *
83 * @param id The id to use
84 * @param params Service call parameters
85 */
86 async getEntity(id, params) {
87 const entityService = this.entityService;
88 const { entity } = this.configuration;
89 debug('Getting entity', id);
90 if (entityService === null) {
91 throw new errors_1.NotAuthenticated('Could not find entity service');
92 }
93 const query = await this.getEntityQuery(params);
94 const { provider, ...paramsWithoutProvider } = params;
95 const result = await entityService.get(id, {
96 ...paramsWithoutProvider,
97 query
98 });
99 if (!params.provider) {
100 return result;
101 }
102 return entityService.get(id, { ...params, [entity]: result });
103 }
104 async getEntityId(authResult, _params) {
105 return authResult.authentication.payload.sub;
106 }
107 async authenticate(authentication, params) {
108 const { accessToken } = authentication;
109 const { entity } = this.configuration;
110 if (!accessToken) {
111 throw new errors_1.NotAuthenticated('No access token');
112 }
113 const payload = await this.authentication.verifyAccessToken(accessToken, params.jwt);
114 const result = {
115 accessToken,
116 authentication: {
117 strategy: 'jwt',
118 accessToken,
119 payload
120 }
121 };
122 if (entity === null) {
123 return result;
124 }
125 const entityId = await this.getEntityId(result, params);
126 const value = await this.getEntity(entityId, params);
127 return {
128 ...result,
129 [entity]: value
130 };
131 }
132 async parse(req) {
133 const { header, schemes } = this.configuration;
134 const headerValue = req.headers && req.headers[header.toLowerCase()];
135 if (!headerValue || typeof headerValue !== 'string') {
136 return null;
137 }
138 debug('Found parsed header value');
139 const [, scheme, schemeValue] = headerValue.match(SPLIT_HEADER) || [];
140 const hasScheme = scheme && schemes.some((current) => new RegExp(current, 'i').test(scheme));
141 if (scheme && !hasScheme) {
142 return null;
143 }
144 return {
145 strategy: this.name,
146 accessToken: hasScheme ? schemeValue : headerValue
147 };
148 }
149}
150exports.JWTStrategy = JWTStrategy;
151//# sourceMappingURL=jwt.js.map
\No newline at end of file