@terreno/api 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/README.md +170 -0
- package/biome.jsonc +22 -0
- package/bunfig.toml +4 -0
- package/dist/api.d.ts +227 -0
- package/dist/api.js +1024 -0
- package/dist/api.test.d.ts +1 -0
- package/dist/api.test.js +2143 -0
- package/dist/auth.d.ts +50 -0
- package/dist/auth.js +512 -0
- package/dist/auth.test.d.ts +1 -0
- package/dist/auth.test.js +778 -0
- package/dist/errors.d.ts +75 -0
- package/dist/errors.js +216 -0
- package/dist/example.d.ts +1 -0
- package/dist/example.js +118 -0
- package/dist/expressServer.d.ts +35 -0
- package/dist/expressServer.js +436 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +30 -0
- package/dist/logger.d.ts +23 -0
- package/dist/logger.js +249 -0
- package/dist/middleware.d.ts +10 -0
- package/dist/middleware.js +52 -0
- package/dist/notifiers/googleChatNotifier.d.ts +5 -0
- package/dist/notifiers/googleChatNotifier.js +130 -0
- package/dist/notifiers/googleChatNotifier.test.d.ts +1 -0
- package/dist/notifiers/googleChatNotifier.test.js +260 -0
- package/dist/notifiers/index.d.ts +3 -0
- package/dist/notifiers/index.js +19 -0
- package/dist/notifiers/slackNotifier.d.ts +5 -0
- package/dist/notifiers/slackNotifier.js +130 -0
- package/dist/notifiers/slackNotifier.test.d.ts +1 -0
- package/dist/notifiers/slackNotifier.test.js +259 -0
- package/dist/notifiers/zoomNotifier.d.ts +34 -0
- package/dist/notifiers/zoomNotifier.js +181 -0
- package/dist/notifiers/zoomNotifier.test.d.ts +1 -0
- package/dist/notifiers/zoomNotifier.test.js +370 -0
- package/dist/openApi.d.ts +60 -0
- package/dist/openApi.js +441 -0
- package/dist/openApi.test.d.ts +1 -0
- package/dist/openApi.test.js +445 -0
- package/dist/openApiBuilder.d.ts +419 -0
- package/dist/openApiBuilder.js +424 -0
- package/dist/openApiBuilder.test.d.ts +1 -0
- package/dist/openApiBuilder.test.js +509 -0
- package/dist/openApiEtag.d.ts +7 -0
- package/dist/openApiEtag.js +38 -0
- package/dist/permissions.d.ts +26 -0
- package/dist/permissions.js +331 -0
- package/dist/permissions.test.d.ts +1 -0
- package/dist/permissions.test.js +413 -0
- package/dist/plugins.d.ts +67 -0
- package/dist/plugins.js +315 -0
- package/dist/plugins.test.d.ts +1 -0
- package/dist/plugins.test.js +639 -0
- package/dist/populate.d.ts +14 -0
- package/dist/populate.js +315 -0
- package/dist/populate.test.d.ts +1 -0
- package/dist/populate.test.js +133 -0
- package/dist/response.d.ts +0 -0
- package/dist/response.js +1 -0
- package/dist/tests/bunSetup.d.ts +1 -0
- package/dist/tests/bunSetup.js +297 -0
- package/dist/tests/index.d.ts +1 -0
- package/dist/tests/index.js +17 -0
- package/dist/tests.d.ts +99 -0
- package/dist/tests.js +273 -0
- package/dist/transformers.d.ts +25 -0
- package/dist/transformers.js +217 -0
- package/dist/transformers.test.d.ts +1 -0
- package/dist/transformers.test.js +370 -0
- package/dist/utils.d.ts +11 -0
- package/dist/utils.js +143 -0
- package/dist/utils.test.d.ts +1 -0
- package/dist/utils.test.js +14 -0
- package/index.ts +1 -0
- package/package.json +88 -0
- package/src/__snapshots__/openApi.test.ts.snap +4814 -0
- package/src/__snapshots__/openApiBuilder.test.ts.snap +1485 -0
- package/src/api.test.ts +1661 -0
- package/src/api.ts +1036 -0
- package/src/auth.test.ts +550 -0
- package/src/auth.ts +408 -0
- package/src/errors.ts +225 -0
- package/src/example.ts +99 -0
- package/src/express.d.ts +5 -0
- package/src/expressServer.ts +387 -0
- package/src/index.ts +14 -0
- package/src/logger.ts +190 -0
- package/src/middleware.ts +18 -0
- package/src/notifiers/googleChatNotifier.test.ts +114 -0
- package/src/notifiers/googleChatNotifier.ts +47 -0
- package/src/notifiers/index.ts +3 -0
- package/src/notifiers/slackNotifier.test.ts +113 -0
- package/src/notifiers/slackNotifier.ts +55 -0
- package/src/notifiers/zoomNotifier.test.ts +207 -0
- package/src/notifiers/zoomNotifier.ts +111 -0
- package/src/openApi.test.ts +331 -0
- package/src/openApi.ts +494 -0
- package/src/openApiBuilder.test.ts +442 -0
- package/src/openApiBuilder.ts +636 -0
- package/src/openApiEtag.ts +40 -0
- package/src/permissions.test.ts +219 -0
- package/src/permissions.ts +228 -0
- package/src/plugins.test.ts +390 -0
- package/src/plugins.ts +289 -0
- package/src/populate.test.ts +65 -0
- package/src/populate.ts +258 -0
- package/src/response.ts +0 -0
- package/src/tests/bunSetup.ts +234 -0
- package/src/tests/index.ts +1 -0
- package/src/tests.ts +218 -0
- package/src/transformers.test.ts +202 -0
- package/src/transformers.ts +170 -0
- package/src/utils.test.ts +14 -0
- package/src/utils.ts +47 -0
- package/tsconfig.json +60 -0
- package/types.d.ts +17 -0
package/dist/auth.d.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import express from "express";
|
|
2
|
+
import type { Model, ObjectId } from "mongoose";
|
|
3
|
+
import type { AuthOptions } from "./expressServer";
|
|
4
|
+
export interface User {
|
|
5
|
+
_id: ObjectId | string;
|
|
6
|
+
id: string;
|
|
7
|
+
admin: boolean;
|
|
8
|
+
/**
|
|
9
|
+
* We support anonymous users, which do not yet have login information.
|
|
10
|
+
* This can be helpful for pre-signup users.
|
|
11
|
+
*/
|
|
12
|
+
isAnonymous?: boolean;
|
|
13
|
+
}
|
|
14
|
+
export interface UserModel extends Model<User> {
|
|
15
|
+
createAnonymousUser?: (id?: string) => Promise<User>;
|
|
16
|
+
postCreate?: (body: any) => Promise<void>;
|
|
17
|
+
createStrategy(): any;
|
|
18
|
+
serializeUser(): any;
|
|
19
|
+
deserializeUser(): any;
|
|
20
|
+
findByUsername(username: string, findOpts: any): any;
|
|
21
|
+
}
|
|
22
|
+
export declare function authenticateMiddleware(anonymous?: boolean): any;
|
|
23
|
+
export declare function signupUser(userModel: UserModel, email: string, password: string, body?: any): Promise<any>;
|
|
24
|
+
/**
|
|
25
|
+
* Generates both an access token (JWT) and a refresh token for a given user.
|
|
26
|
+
*
|
|
27
|
+
* This function:
|
|
28
|
+
* - Signs the user's `_id` into a short-lived JWT (`token`)
|
|
29
|
+
* and a long-lived refresh token (`refreshToken`).
|
|
30
|
+
* - Supports custom expiration logic
|
|
31
|
+
* and payload customization via `AuthOptions`.
|
|
32
|
+
* - Reads token secrets, issuer,
|
|
33
|
+
* and default expirations from environment variables.
|
|
34
|
+
* - Returns `{ token, refreshToken }`,
|
|
35
|
+
* or `{ token: null, refreshToken: null }` if the user is missing.
|
|
36
|
+
*
|
|
37
|
+
* It is exported to allow external implementations (such as OAuth integrations or other
|
|
38
|
+
* authentication providers) to reuse and customize the same token generation logic.
|
|
39
|
+
* This ensures consistent and secure token issuance across different authentication flows.
|
|
40
|
+
*/
|
|
41
|
+
export declare const generateTokens: (user: any, authOptions?: AuthOptions) => Promise<{
|
|
42
|
+
refreshToken: null;
|
|
43
|
+
token: null;
|
|
44
|
+
} | {
|
|
45
|
+
refreshToken: any;
|
|
46
|
+
token: string;
|
|
47
|
+
}>;
|
|
48
|
+
export declare function setupAuth(app: express.Application, userModel: UserModel): void;
|
|
49
|
+
export declare function addAuthRoutes(app: express.Application, userModel: UserModel, authOptions?: AuthOptions): void;
|
|
50
|
+
export declare function addMeRoutes(app: express.Application, userModel: UserModel, _authOptions?: AuthOptions): void;
|
package/dist/auth.js
ADDED
|
@@ -0,0 +1,512 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __assign = (this && this.__assign) || function () {
|
|
3
|
+
__assign = Object.assign || function(t) {
|
|
4
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
5
|
+
s = arguments[i];
|
|
6
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
7
|
+
t[p] = s[p];
|
|
8
|
+
}
|
|
9
|
+
return t;
|
|
10
|
+
};
|
|
11
|
+
return __assign.apply(this, arguments);
|
|
12
|
+
};
|
|
13
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
14
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
15
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
16
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
17
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
18
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
19
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
20
|
+
});
|
|
21
|
+
};
|
|
22
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
23
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
24
|
+
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
25
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
26
|
+
function step(op) {
|
|
27
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
28
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
29
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
30
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
31
|
+
switch (op[0]) {
|
|
32
|
+
case 0: case 1: t = op; break;
|
|
33
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
34
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
35
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
36
|
+
default:
|
|
37
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
38
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
39
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
40
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
41
|
+
if (t[2]) _.ops.pop();
|
|
42
|
+
_.trys.pop(); continue;
|
|
43
|
+
}
|
|
44
|
+
op = body.call(thisArg, _);
|
|
45
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
46
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
50
|
+
var t = {};
|
|
51
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
52
|
+
t[p] = s[p];
|
|
53
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
54
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
55
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
56
|
+
t[p[i]] = s[p[i]];
|
|
57
|
+
}
|
|
58
|
+
return t;
|
|
59
|
+
};
|
|
60
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
61
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
62
|
+
};
|
|
63
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
64
|
+
exports.generateTokens = void 0;
|
|
65
|
+
exports.authenticateMiddleware = authenticateMiddleware;
|
|
66
|
+
exports.signupUser = signupUser;
|
|
67
|
+
exports.setupAuth = setupAuth;
|
|
68
|
+
exports.addAuthRoutes = addAuthRoutes;
|
|
69
|
+
exports.addMeRoutes = addMeRoutes;
|
|
70
|
+
var express_1 = __importDefault(require("express"));
|
|
71
|
+
var jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
|
|
72
|
+
var ms_1 = __importDefault(require("ms"));
|
|
73
|
+
var passport_1 = __importDefault(require("passport"));
|
|
74
|
+
var passport_anonymous_1 = require("passport-anonymous");
|
|
75
|
+
var passport_jwt_1 = require("passport-jwt");
|
|
76
|
+
var passport_local_1 = require("passport-local");
|
|
77
|
+
var errors_1 = require("./errors");
|
|
78
|
+
var logger_1 = require("./logger");
|
|
79
|
+
function authenticateMiddleware(anonymous) {
|
|
80
|
+
if (anonymous === void 0) { anonymous = false; }
|
|
81
|
+
var strategies = ["jwt"];
|
|
82
|
+
if (anonymous) {
|
|
83
|
+
strategies.push("anonymous");
|
|
84
|
+
}
|
|
85
|
+
return passport_1.default.authenticate(strategies, {
|
|
86
|
+
failureMessage: false, // this is just avoiding storing the message in the session
|
|
87
|
+
failWithError: true,
|
|
88
|
+
session: false,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
function signupUser(userModel, email, password, body) {
|
|
92
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
93
|
+
var _email, _password, bodyRest, user, error_1, error_2;
|
|
94
|
+
return __generator(this, function (_a) {
|
|
95
|
+
switch (_a.label) {
|
|
96
|
+
case 0:
|
|
97
|
+
_email = body.email, _password = body.password, bodyRest = __rest(body, ["email", "password"]);
|
|
98
|
+
_a.label = 1;
|
|
99
|
+
case 1:
|
|
100
|
+
_a.trys.push([1, 8, , 9]);
|
|
101
|
+
return [4 /*yield*/, userModel.register(__assign({ email: email }, bodyRest), password)];
|
|
102
|
+
case 2:
|
|
103
|
+
user = _a.sent();
|
|
104
|
+
if (!user.postCreate) return [3 /*break*/, 6];
|
|
105
|
+
_a.label = 3;
|
|
106
|
+
case 3:
|
|
107
|
+
_a.trys.push([3, 5, , 6]);
|
|
108
|
+
return [4 /*yield*/, user.postCreate(bodyRest)];
|
|
109
|
+
case 4:
|
|
110
|
+
_a.sent();
|
|
111
|
+
return [3 /*break*/, 6];
|
|
112
|
+
case 5:
|
|
113
|
+
error_1 = _a.sent();
|
|
114
|
+
logger_1.logger.error("Error in user.postCreate: ".concat(error_1));
|
|
115
|
+
throw error_1;
|
|
116
|
+
case 6: return [4 /*yield*/, user.save()];
|
|
117
|
+
case 7:
|
|
118
|
+
_a.sent();
|
|
119
|
+
return [2 /*return*/, user];
|
|
120
|
+
case 8:
|
|
121
|
+
error_2 = _a.sent();
|
|
122
|
+
throw new errors_1.APIError({ title: error_2.message });
|
|
123
|
+
case 9: return [2 /*return*/];
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Generates both an access token (JWT) and a refresh token for a given user.
|
|
130
|
+
*
|
|
131
|
+
* This function:
|
|
132
|
+
* - Signs the user's `_id` into a short-lived JWT (`token`)
|
|
133
|
+
* and a long-lived refresh token (`refreshToken`).
|
|
134
|
+
* - Supports custom expiration logic
|
|
135
|
+
* and payload customization via `AuthOptions`.
|
|
136
|
+
* - Reads token secrets, issuer,
|
|
137
|
+
* and default expirations from environment variables.
|
|
138
|
+
* - Returns `{ token, refreshToken }`,
|
|
139
|
+
* or `{ token: null, refreshToken: null }` if the user is missing.
|
|
140
|
+
*
|
|
141
|
+
* It is exported to allow external implementations (such as OAuth integrations or other
|
|
142
|
+
* authentication providers) to reuse and customize the same token generation logic.
|
|
143
|
+
* This ensures consistent and secure token issuance across different authentication flows.
|
|
144
|
+
*/
|
|
145
|
+
var generateTokens = function (user, authOptions) { return __awaiter(void 0, void 0, void 0, function () {
|
|
146
|
+
var tokenSecretOrKey, payload, tokenOptions, token, refreshTokenSecretOrKey, refreshToken, refreshTokenOptions;
|
|
147
|
+
return __generator(this, function (_a) {
|
|
148
|
+
tokenSecretOrKey = process.env.TOKEN_SECRET;
|
|
149
|
+
if (!tokenSecretOrKey) {
|
|
150
|
+
throw new Error("TOKEN_SECRET must be set in env.");
|
|
151
|
+
}
|
|
152
|
+
if (!(user === null || user === void 0 ? void 0 : user._id)) {
|
|
153
|
+
logger_1.logger.warn("No user found for token generation");
|
|
154
|
+
return [2 /*return*/, { refreshToken: null, token: null }];
|
|
155
|
+
}
|
|
156
|
+
payload = { id: user._id.toString() };
|
|
157
|
+
if (authOptions === null || authOptions === void 0 ? void 0 : authOptions.generateJWTPayload) {
|
|
158
|
+
payload = __assign(__assign({}, authOptions.generateJWTPayload(user)), payload);
|
|
159
|
+
}
|
|
160
|
+
tokenOptions = {
|
|
161
|
+
expiresIn: "15m",
|
|
162
|
+
};
|
|
163
|
+
if (authOptions === null || authOptions === void 0 ? void 0 : authOptions.generateTokenExpiration) {
|
|
164
|
+
tokenOptions.expiresIn = authOptions.generateTokenExpiration(user);
|
|
165
|
+
}
|
|
166
|
+
else if (process.env.TOKEN_EXPIRES_IN) {
|
|
167
|
+
try {
|
|
168
|
+
// this call to ms is purely for validation of the env variable. If it is invalid,
|
|
169
|
+
// we want to be able to log the error and use the default.
|
|
170
|
+
(0, ms_1.default)(process.env.TOKEN_EXPIRES_IN);
|
|
171
|
+
tokenOptions.expiresIn = process.env.TOKEN_EXPIRES_IN;
|
|
172
|
+
}
|
|
173
|
+
catch (error) {
|
|
174
|
+
// This error will result in using the default value above of 15m.
|
|
175
|
+
logger_1.logger.error(error);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
if (process.env.TOKEN_ISSUER) {
|
|
179
|
+
tokenOptions.issuer = process.env.TOKEN_ISSUER;
|
|
180
|
+
}
|
|
181
|
+
token = jsonwebtoken_1.default.sign(payload, tokenSecretOrKey, tokenOptions);
|
|
182
|
+
refreshTokenSecretOrKey = process.env.REFRESH_TOKEN_SECRET;
|
|
183
|
+
if (refreshTokenSecretOrKey) {
|
|
184
|
+
refreshTokenOptions = {
|
|
185
|
+
expiresIn: "30d",
|
|
186
|
+
};
|
|
187
|
+
if (authOptions === null || authOptions === void 0 ? void 0 : authOptions.generateRefreshTokenExpiration) {
|
|
188
|
+
refreshTokenOptions.expiresIn = authOptions.generateRefreshTokenExpiration(user);
|
|
189
|
+
}
|
|
190
|
+
else if (process.env.REFRESH_TOKEN_EXPIRES_IN) {
|
|
191
|
+
try {
|
|
192
|
+
// this call to ms is purely for validation of the env variable. If it is invalid,
|
|
193
|
+
// we want to be able to log the error and use the default.
|
|
194
|
+
(0, ms_1.default)(process.env.REFRESH_TOKEN_EXPIRES_IN);
|
|
195
|
+
refreshTokenOptions.expiresIn = process.env.REFRESH_TOKEN_EXPIRES_IN;
|
|
196
|
+
}
|
|
197
|
+
catch (error) {
|
|
198
|
+
// This error will result in using the default value above of 30d.
|
|
199
|
+
logger_1.logger.error(error);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
refreshToken = jsonwebtoken_1.default.sign(payload, refreshTokenSecretOrKey, refreshTokenOptions);
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
logger_1.logger.info("REFRESH_TOKEN_SECRET not set so refresh tokens will not be issued");
|
|
206
|
+
}
|
|
207
|
+
return [2 /*return*/, { refreshToken: refreshToken, token: token }];
|
|
208
|
+
});
|
|
209
|
+
}); };
|
|
210
|
+
exports.generateTokens = generateTokens;
|
|
211
|
+
// TODO allow customization
|
|
212
|
+
function setupAuth(app, userModel) {
|
|
213
|
+
var _this = this;
|
|
214
|
+
passport_1.default.use(new passport_anonymous_1.Strategy());
|
|
215
|
+
passport_1.default.use(userModel.createStrategy());
|
|
216
|
+
passport_1.default.use("signup", new passport_local_1.Strategy({
|
|
217
|
+
passReqToCallback: true,
|
|
218
|
+
passwordField: "password",
|
|
219
|
+
usernameField: "email",
|
|
220
|
+
}, function (req, email, password, done) { return __awaiter(_this, void 0, void 0, function () {
|
|
221
|
+
var _a, _b, error_3;
|
|
222
|
+
return __generator(this, function (_c) {
|
|
223
|
+
switch (_c.label) {
|
|
224
|
+
case 0:
|
|
225
|
+
_c.trys.push([0, 2, , 3]);
|
|
226
|
+
_a = done;
|
|
227
|
+
_b = [undefined];
|
|
228
|
+
return [4 /*yield*/, signupUser(userModel, email, password, req.body)];
|
|
229
|
+
case 1:
|
|
230
|
+
_a.apply(void 0, _b.concat([_c.sent()]));
|
|
231
|
+
return [3 /*break*/, 3];
|
|
232
|
+
case 2:
|
|
233
|
+
error_3 = _c.sent();
|
|
234
|
+
return [2 /*return*/, done(error_3)];
|
|
235
|
+
case 3: return [2 /*return*/];
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
}); }));
|
|
239
|
+
if (!userModel.createStrategy) {
|
|
240
|
+
throw new Error("setupAuth userModel must have .createStrategy()");
|
|
241
|
+
}
|
|
242
|
+
var customTokenExtractor = function (req) {
|
|
243
|
+
var _a, _b, _c;
|
|
244
|
+
var token = null;
|
|
245
|
+
if ((_a = req === null || req === void 0 ? void 0 : req.cookies) === null || _a === void 0 ? void 0 : _a.jwt) {
|
|
246
|
+
token = req.cookies.jwt;
|
|
247
|
+
}
|
|
248
|
+
else if ((_b = req === null || req === void 0 ? void 0 : req.headers) === null || _b === void 0 ? void 0 : _b.authorization) {
|
|
249
|
+
token = (_c = req === null || req === void 0 ? void 0 : req.headers) === null || _c === void 0 ? void 0 : _c.authorization.split(" ")[1];
|
|
250
|
+
}
|
|
251
|
+
return token;
|
|
252
|
+
};
|
|
253
|
+
if (process.env.TOKEN_SECRET) {
|
|
254
|
+
if (process.env.NODE_ENV !== "test") {
|
|
255
|
+
logger_1.logger.debug("Setting up JWT Authentication");
|
|
256
|
+
}
|
|
257
|
+
var secretOrKey = process.env.TOKEN_SECRET;
|
|
258
|
+
if (!secretOrKey) {
|
|
259
|
+
throw new Error("TOKEN_SECRET must be set in env.");
|
|
260
|
+
}
|
|
261
|
+
var jwtOpts = {
|
|
262
|
+
issuer: process.env.TOKEN_ISSUER,
|
|
263
|
+
jwtFromRequest: customTokenExtractor,
|
|
264
|
+
secretOrKey: secretOrKey,
|
|
265
|
+
};
|
|
266
|
+
passport_1.default.use("jwt", new passport_jwt_1.Strategy(jwtOpts, function (jwtPayload, done) { return __awaiter(_this, void 0, void 0, function () {
|
|
267
|
+
var user, error_4;
|
|
268
|
+
return __generator(this, function (_a) {
|
|
269
|
+
switch (_a.label) {
|
|
270
|
+
case 0:
|
|
271
|
+
if (!jwtPayload) {
|
|
272
|
+
return [2 /*return*/, done(null, false)];
|
|
273
|
+
}
|
|
274
|
+
_a.label = 1;
|
|
275
|
+
case 1:
|
|
276
|
+
_a.trys.push([1, 3, , 4]);
|
|
277
|
+
return [4 /*yield*/, userModel.findById(jwtPayload.id)];
|
|
278
|
+
case 2:
|
|
279
|
+
user = _a.sent();
|
|
280
|
+
return [3 /*break*/, 4];
|
|
281
|
+
case 3:
|
|
282
|
+
error_4 = _a.sent();
|
|
283
|
+
logger_1.logger.warn("[jwt] Error finding user from id: ".concat(error_4));
|
|
284
|
+
return [2 /*return*/, done(error_4, false)];
|
|
285
|
+
case 4:
|
|
286
|
+
if (user) {
|
|
287
|
+
return [2 /*return*/, done(null, user)];
|
|
288
|
+
}
|
|
289
|
+
if (!userModel.createAnonymousUser) return [3 /*break*/, 6];
|
|
290
|
+
logger_1.logger.info("[jwt] Creating anonymous user");
|
|
291
|
+
return [4 /*yield*/, userModel.createAnonymousUser()];
|
|
292
|
+
case 5:
|
|
293
|
+
user = _a.sent();
|
|
294
|
+
return [2 /*return*/, done(null, user)];
|
|
295
|
+
case 6:
|
|
296
|
+
logger_1.logger.info("[jwt] No user found from token");
|
|
297
|
+
return [2 /*return*/, done(null, false)];
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
}); }));
|
|
301
|
+
}
|
|
302
|
+
// Adds req.user to the request. This may wind up duplicating requests with passport,
|
|
303
|
+
// but passport doesn't give us req.user early enough.
|
|
304
|
+
function decodeJWTMiddleware(req, res, next) {
|
|
305
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
306
|
+
var token, decoded, userText, details, _a, error_5;
|
|
307
|
+
var _b, _c, _d, _e;
|
|
308
|
+
return __generator(this, function (_f) {
|
|
309
|
+
switch (_f.label) {
|
|
310
|
+
case 0:
|
|
311
|
+
if (!process.env.TOKEN_SECRET) {
|
|
312
|
+
return [2 /*return*/, next()];
|
|
313
|
+
}
|
|
314
|
+
// Allow requests with a "Secret" prefix to pass through since this is a string value,
|
|
315
|
+
// not a jwt that needs to be decoded
|
|
316
|
+
if (((_c = (_b = req === null || req === void 0 ? void 0 : req.headers) === null || _b === void 0 ? void 0 : _b.authorization) === null || _c === void 0 ? void 0 : _c.split(" ")[0]) === "Secret") {
|
|
317
|
+
return [2 /*return*/, next()];
|
|
318
|
+
}
|
|
319
|
+
token = customTokenExtractor(req);
|
|
320
|
+
// For some reason, our app will happily put null into the authorization header when logging
|
|
321
|
+
// out then back in.
|
|
322
|
+
if (!token || token === "null" || token === "undefined") {
|
|
323
|
+
return [2 /*return*/, next()];
|
|
324
|
+
}
|
|
325
|
+
try {
|
|
326
|
+
decoded = jsonwebtoken_1.default.verify(token, process.env.TOKEN_SECRET, {
|
|
327
|
+
issuer: process.env.TOKEN_ISSUER,
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
catch (error) {
|
|
331
|
+
userText = ((_d = req.user) === null || _d === void 0 ? void 0 : _d._id) ? " for user ".concat(req.user._id, " ") : "";
|
|
332
|
+
details = "[jwt] Error decoding token".concat(userText, ": ").concat(error, ", expired at ").concat(error === null || error === void 0 ? void 0 : error.expiredAt, ", current time: ").concat(Date.now());
|
|
333
|
+
logger_1.logger.debug(details);
|
|
334
|
+
return [2 /*return*/, res.status(401).json({ details: details, message: error === null || error === void 0 ? void 0 : error.message })];
|
|
335
|
+
}
|
|
336
|
+
if (!decoded.id) return [3 /*break*/, 4];
|
|
337
|
+
_f.label = 1;
|
|
338
|
+
case 1:
|
|
339
|
+
_f.trys.push([1, 3, , 4]);
|
|
340
|
+
_a = req;
|
|
341
|
+
return [4 /*yield*/, userModel.findById(decoded.id)];
|
|
342
|
+
case 2:
|
|
343
|
+
_a.user = _f.sent();
|
|
344
|
+
if ((_e = req.user) === null || _e === void 0 ? void 0 : _e.disabled) {
|
|
345
|
+
logger_1.logger.warn("[jwt] User ".concat(req.user.id, " is disabled"));
|
|
346
|
+
return [2 /*return*/, res.status(401).json({ status: 401, title: "User is disabled" })];
|
|
347
|
+
}
|
|
348
|
+
return [3 /*break*/, 4];
|
|
349
|
+
case 3:
|
|
350
|
+
error_5 = _f.sent();
|
|
351
|
+
logger_1.logger.warn("[jwt] Error finding user from id: ".concat(error_5));
|
|
352
|
+
return [3 /*break*/, 4];
|
|
353
|
+
case 4: return [2 /*return*/, next()];
|
|
354
|
+
}
|
|
355
|
+
});
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
app.use(decodeJWTMiddleware);
|
|
359
|
+
app.use(express_1.default.urlencoded({ extended: false }));
|
|
360
|
+
}
|
|
361
|
+
function addAuthRoutes(app, userModel, authOptions) {
|
|
362
|
+
var _this = this;
|
|
363
|
+
var router = express_1.default.Router();
|
|
364
|
+
router.post("/login", function (req, res, next) { return __awaiter(_this, void 0, void 0, function () {
|
|
365
|
+
var _this = this;
|
|
366
|
+
return __generator(this, function (_a) {
|
|
367
|
+
passport_1.default.authenticate("local", { session: false }, function (err, user, info) { return __awaiter(_this, void 0, void 0, function () {
|
|
368
|
+
var tokens;
|
|
369
|
+
return __generator(this, function (_a) {
|
|
370
|
+
switch (_a.label) {
|
|
371
|
+
case 0:
|
|
372
|
+
if (err) {
|
|
373
|
+
logger_1.logger.error("Error logging in: ".concat(err));
|
|
374
|
+
return [2 /*return*/, next(err)];
|
|
375
|
+
}
|
|
376
|
+
if (!user) {
|
|
377
|
+
logger_1.logger.warn("Invalid login: ".concat(info));
|
|
378
|
+
return [2 /*return*/, res.status(401).json({ message: info === null || info === void 0 ? void 0 : info.message })];
|
|
379
|
+
}
|
|
380
|
+
logger_1.logger.info("User logged in: ".concat(user._id, ", type: ").concat(user.type || "N/A"));
|
|
381
|
+
return [4 /*yield*/, (0, exports.generateTokens)(user, authOptions)];
|
|
382
|
+
case 1:
|
|
383
|
+
tokens = _a.sent();
|
|
384
|
+
return [2 /*return*/, res.json({
|
|
385
|
+
data: { refreshToken: tokens.refreshToken, token: tokens.token, userId: user === null || user === void 0 ? void 0 : user._id },
|
|
386
|
+
})];
|
|
387
|
+
}
|
|
388
|
+
});
|
|
389
|
+
}); })(req, res, next);
|
|
390
|
+
return [2 /*return*/];
|
|
391
|
+
});
|
|
392
|
+
}); });
|
|
393
|
+
router.post("/refresh_token", function (req, res) { return __awaiter(_this, void 0, void 0, function () {
|
|
394
|
+
var refreshTokenSecretOrKey, decoded, user, tokens;
|
|
395
|
+
var _a, _b, _c, _d;
|
|
396
|
+
return __generator(this, function (_e) {
|
|
397
|
+
switch (_e.label) {
|
|
398
|
+
case 0:
|
|
399
|
+
if (!req.body.refreshToken) {
|
|
400
|
+
logger_1.logger.error("No refresh token provided, must provide refreshToken in body, user id: ".concat((_a = req.user) === null || _a === void 0 ? void 0 : _a.id));
|
|
401
|
+
return [2 /*return*/, res
|
|
402
|
+
.status(401)
|
|
403
|
+
.json({ message: "No refresh token provided, must provide refreshToken in body" })];
|
|
404
|
+
}
|
|
405
|
+
if (!process.env.REFRESH_TOKEN_SECRET) {
|
|
406
|
+
logger_1.logger.error("No REFRESH_TOKEN_SECRET set, cannot refresh token, user id: ".concat((_b = req.user) === null || _b === void 0 ? void 0 : _b.id));
|
|
407
|
+
return [2 /*return*/, res.status(401).json({ message: "No REFRESH_TOKEN_SECRET set, cannot refresh token" })];
|
|
408
|
+
}
|
|
409
|
+
refreshTokenSecretOrKey = process.env.REFRESH_TOKEN_SECRET;
|
|
410
|
+
try {
|
|
411
|
+
decoded = jsonwebtoken_1.default.verify(req.body.refreshToken, refreshTokenSecretOrKey);
|
|
412
|
+
}
|
|
413
|
+
catch (error) {
|
|
414
|
+
logger_1.logger.error("Error refreshing token for user ".concat((_c = req.user) === null || _c === void 0 ? void 0 : _c.id, ": ").concat(error));
|
|
415
|
+
return [2 /*return*/, res.status(401).json({ message: error === null || error === void 0 ? void 0 : error.message })];
|
|
416
|
+
}
|
|
417
|
+
if (!(decoded === null || decoded === void 0 ? void 0 : decoded.id)) return [3 /*break*/, 3];
|
|
418
|
+
return [4 /*yield*/, userModel.findById(decoded.id)];
|
|
419
|
+
case 1:
|
|
420
|
+
user = _e.sent();
|
|
421
|
+
return [4 /*yield*/, (0, exports.generateTokens)(user, authOptions)];
|
|
422
|
+
case 2:
|
|
423
|
+
tokens = _e.sent();
|
|
424
|
+
logger_1.logger.debug("Refreshed token for ".concat(user === null || user === void 0 ? void 0 : user.id));
|
|
425
|
+
return [2 /*return*/, res.json({ data: { refreshToken: tokens.refreshToken, token: tokens.token } })];
|
|
426
|
+
case 3:
|
|
427
|
+
logger_1.logger.error("Invalid refresh token, user id: ".concat((_d = req.user) === null || _d === void 0 ? void 0 : _d.id));
|
|
428
|
+
return [2 /*return*/, res.status(401).json({ message: "Invalid refresh token" })];
|
|
429
|
+
}
|
|
430
|
+
});
|
|
431
|
+
}); });
|
|
432
|
+
var signupDisabled = process.env.SIGNUP_DISABLED === "true";
|
|
433
|
+
if (!signupDisabled) {
|
|
434
|
+
router.post("/signup", passport_1.default.authenticate("signup", { failWithError: true, session: false }), function (req, res) { return __awaiter(_this, void 0, void 0, function () {
|
|
435
|
+
var tokens;
|
|
436
|
+
return __generator(this, function (_a) {
|
|
437
|
+
switch (_a.label) {
|
|
438
|
+
case 0: return [4 /*yield*/, (0, exports.generateTokens)(req.user, authOptions)];
|
|
439
|
+
case 1:
|
|
440
|
+
tokens = _a.sent();
|
|
441
|
+
return [2 /*return*/, res.json({
|
|
442
|
+
data: { refreshToken: tokens.refreshToken, token: tokens.token, userId: req.user._id },
|
|
443
|
+
})];
|
|
444
|
+
}
|
|
445
|
+
});
|
|
446
|
+
}); });
|
|
447
|
+
}
|
|
448
|
+
app.set("etag", false);
|
|
449
|
+
app.use("/auth", router);
|
|
450
|
+
}
|
|
451
|
+
function addMeRoutes(app, userModel, _authOptions) {
|
|
452
|
+
var _this = this;
|
|
453
|
+
var router = express_1.default.Router();
|
|
454
|
+
router.get("/me", authenticateMiddleware(), function (req, res) { return __awaiter(_this, void 0, void 0, function () {
|
|
455
|
+
var data, dataObject;
|
|
456
|
+
var _a;
|
|
457
|
+
return __generator(this, function (_b) {
|
|
458
|
+
switch (_b.label) {
|
|
459
|
+
case 0:
|
|
460
|
+
if (!((_a = req.user) === null || _a === void 0 ? void 0 : _a.id)) {
|
|
461
|
+
logger_1.logger.debug("Not user found for /me");
|
|
462
|
+
return [2 /*return*/, res.sendStatus(401)];
|
|
463
|
+
}
|
|
464
|
+
return [4 /*yield*/, userModel.findById(req.user.id)];
|
|
465
|
+
case 1:
|
|
466
|
+
data = _b.sent();
|
|
467
|
+
if (!data) {
|
|
468
|
+
logger_1.logger.debug("Not user data found for /me");
|
|
469
|
+
return [2 /*return*/, res.sendStatus(404)];
|
|
470
|
+
}
|
|
471
|
+
dataObject = data.toObject();
|
|
472
|
+
dataObject.id = data._id;
|
|
473
|
+
return [2 /*return*/, res.json({ data: dataObject })];
|
|
474
|
+
}
|
|
475
|
+
});
|
|
476
|
+
}); });
|
|
477
|
+
router.patch("/me", authenticateMiddleware(), function (req, res) { return __awaiter(_this, void 0, void 0, function () {
|
|
478
|
+
var doc, dataObject, error_6;
|
|
479
|
+
var _a;
|
|
480
|
+
return __generator(this, function (_b) {
|
|
481
|
+
switch (_b.label) {
|
|
482
|
+
case 0:
|
|
483
|
+
if (!((_a = req.user) === null || _a === void 0 ? void 0 : _a.id)) {
|
|
484
|
+
return [2 /*return*/, res.sendStatus(401)];
|
|
485
|
+
}
|
|
486
|
+
return [4 /*yield*/, userModel.findById(req.user.id)];
|
|
487
|
+
case 1:
|
|
488
|
+
doc = _b.sent();
|
|
489
|
+
if (!doc) {
|
|
490
|
+
return [2 /*return*/, res.sendStatus(404)];
|
|
491
|
+
}
|
|
492
|
+
_b.label = 2;
|
|
493
|
+
case 2:
|
|
494
|
+
_b.trys.push([2, 4, , 5]);
|
|
495
|
+
Object.assign(doc, req.body);
|
|
496
|
+
return [4 /*yield*/, doc.save()];
|
|
497
|
+
case 3:
|
|
498
|
+
_b.sent();
|
|
499
|
+
dataObject = doc.toObject();
|
|
500
|
+
dataObject.id = doc._id;
|
|
501
|
+
return [2 /*return*/, res.json({ data: dataObject })];
|
|
502
|
+
case 4:
|
|
503
|
+
error_6 = _b.sent();
|
|
504
|
+
return [2 /*return*/, res.status(403).send({ message: error_6.message })];
|
|
505
|
+
case 5: return [2 /*return*/];
|
|
506
|
+
}
|
|
507
|
+
});
|
|
508
|
+
}); });
|
|
509
|
+
app.set("etag", false);
|
|
510
|
+
app.use("/auth", router);
|
|
511
|
+
app.use(errors_1.apiErrorMiddleware);
|
|
512
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|