@simulacrum/auth0-simulator 0.11.1 → 0.11.3

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.
Files changed (105) hide show
  1. package/dist/_virtual/rolldown_runtime.cjs +29 -0
  2. package/dist/_virtual/rolldown_runtime.mjs +7 -0
  3. package/dist/auth/constants.cjs +15 -0
  4. package/dist/auth/constants.mjs +14 -0
  5. package/dist/auth/constants.mjs.map +1 -0
  6. package/dist/auth/date.cjs +8 -0
  7. package/dist/auth/date.mjs +7 -0
  8. package/dist/auth/date.mjs.map +1 -0
  9. package/dist/auth/jwt.cjs +16 -0
  10. package/dist/auth/jwt.mjs +15 -0
  11. package/dist/auth/jwt.mjs.map +1 -0
  12. package/dist/auth/refresh-token.cjs +24 -0
  13. package/dist/auth/refresh-token.mjs +23 -0
  14. package/dist/auth/refresh-token.mjs.map +1 -0
  15. package/dist/config/get-config.cjs +39 -0
  16. package/dist/config/get-config.mjs +39 -0
  17. package/dist/config/get-config.mjs.map +1 -0
  18. package/dist/handlers/auth0-handlers.cjs +207 -0
  19. package/dist/handlers/auth0-handlers.mjs +207 -0
  20. package/dist/handlers/auth0-handlers.mjs.map +1 -0
  21. package/dist/handlers/index.cjs +29 -0
  22. package/dist/handlers/index.mjs +27 -0
  23. package/dist/handlers/index.mjs.map +1 -0
  24. package/dist/handlers/login-redirect.cjs +24 -0
  25. package/dist/handlers/login-redirect.mjs +24 -0
  26. package/dist/handlers/login-redirect.mjs.map +1 -0
  27. package/dist/handlers/oauth-handlers.cjs +144 -0
  28. package/dist/handlers/oauth-handlers.mjs +144 -0
  29. package/dist/handlers/oauth-handlers.mjs.map +1 -0
  30. package/dist/handlers/openid-handlers.cjs +32 -0
  31. package/dist/handlers/openid-handlers.mjs +33 -0
  32. package/dist/handlers/openid-handlers.mjs.map +1 -0
  33. package/dist/handlers/url.cjs +6 -0
  34. package/dist/handlers/url.mjs +6 -0
  35. package/dist/handlers/url.mjs.map +1 -0
  36. package/dist/handlers/utils.cjs +25 -0
  37. package/dist/handlers/utils.mjs +24 -0
  38. package/dist/handlers/utils.mjs.map +1 -0
  39. package/dist/handlers/web-message.cjs +23 -0
  40. package/dist/handlers/web-message.mjs +23 -0
  41. package/dist/handlers/web-message.mjs.map +1 -0
  42. package/dist/index.cjs +15 -38751
  43. package/dist/index.d.cts +7 -175
  44. package/dist/index.d.cts.map +1 -1
  45. package/dist/index.d.mts +21 -0
  46. package/dist/index.d.mts.map +1 -0
  47. package/dist/index.mjs +22 -0
  48. package/dist/index.mjs.map +1 -0
  49. package/dist/middleware/create-cors.cjs +15 -0
  50. package/dist/middleware/create-cors.mjs +14 -0
  51. package/dist/middleware/create-cors.mjs.map +1 -0
  52. package/dist/middleware/error-handling.cjs +25 -0
  53. package/dist/middleware/error-handling.mjs +25 -0
  54. package/dist/middleware/error-handling.mjs.map +1 -0
  55. package/dist/middleware/no-cache.cjs +10 -0
  56. package/dist/middleware/no-cache.mjs +10 -0
  57. package/dist/middleware/no-cache.mjs.map +1 -0
  58. package/dist/middleware/session.cjs +19 -0
  59. package/dist/middleware/session.mjs +18 -0
  60. package/dist/middleware/session.mjs.map +1 -0
  61. package/dist/rules/extensionless-file-name.cjs +6 -0
  62. package/dist/rules/extensionless-file-name.mjs +6 -0
  63. package/dist/rules/extensionless-file-name.mjs.map +1 -0
  64. package/dist/rules/parse-rules-files.cjs +28 -0
  65. package/dist/rules/parse-rules-files.mjs +26 -0
  66. package/dist/rules/parse-rules-files.mjs.map +1 -0
  67. package/dist/rules/rules-runner.cjs +67 -0
  68. package/dist/rules/rules-runner.mjs +65 -0
  69. package/dist/rules/rules-runner.mjs.map +1 -0
  70. package/dist/store/entities.cjs +37 -0
  71. package/dist/store/entities.d.cts +99 -0
  72. package/dist/store/entities.d.cts.map +1 -0
  73. package/dist/store/entities.d.mts +100 -0
  74. package/dist/store/entities.d.mts.map +1 -0
  75. package/dist/store/entities.mjs +34 -0
  76. package/dist/store/entities.mjs.map +1 -0
  77. package/dist/store/index.cjs +44 -0
  78. package/dist/store/index.d.cts +22 -0
  79. package/dist/store/index.d.cts.map +1 -0
  80. package/dist/store/index.d.mts +22 -0
  81. package/dist/store/index.d.mts.map +1 -0
  82. package/dist/store/index.mjs +45 -0
  83. package/dist/store/index.mjs.map +1 -0
  84. package/dist/types.cjs +24 -0
  85. package/dist/types.d.cts +66 -0
  86. package/dist/types.d.cts.map +1 -0
  87. package/dist/types.d.mts +66 -0
  88. package/dist/types.d.mts.map +1 -0
  89. package/dist/types.mjs +24 -0
  90. package/dist/types.mjs.map +1 -0
  91. package/dist/views/login.cjs +145 -0
  92. package/dist/views/login.mjs +146 -0
  93. package/dist/views/login.mjs.map +1 -0
  94. package/dist/views/username-password.cjs +39 -0
  95. package/dist/views/username-password.mjs +39 -0
  96. package/dist/views/username-password.mjs.map +1 -0
  97. package/dist/views/web-message.cjs +66 -0
  98. package/dist/views/web-message.mjs +65 -0
  99. package/dist/views/web-message.mjs.map +1 -0
  100. package/package.json +9 -9
  101. package/dist/index.d.ts +0 -189
  102. package/dist/index.d.ts.map +0 -1
  103. package/dist/index.js +0 -38747
  104. package/dist/index.js.map +0 -1
  105. /package/dist/{public → views/public}/img/frontside-logo.png +0 -0
@@ -0,0 +1,207 @@
1
+ import { createLoginRedirectHandler } from "./login-redirect.mjs";
2
+ import { createWebMessageHandler } from "./web-message.mjs";
3
+ import { createPersonQuery } from "./utils.mjs";
4
+ import { loginView } from "../views/login.mjs";
5
+ import { createTokens } from "./oauth-handlers.mjs";
6
+ import { userNamePasswordForm } from "../views/username-password.mjs";
7
+ import { stringify } from "querystring";
8
+ import { assert } from "assert-ts";
9
+ import { encode } from "base64-url";
10
+ import { decode as decode$1 } from "jsonwebtoken";
11
+
12
+ //#region src/handlers/auth0-handlers.ts
13
+ const createLogger = (debug) => ({ log: (...args) => {
14
+ if (!debug) return;
15
+ console.dir(...args);
16
+ } });
17
+ const createAuth0Handlers = (simulationStore, serviceURL, options, debug) => {
18
+ let { audience, scope, clientID, rulesDirectory } = options;
19
+ let personQuery = createPersonQuery(simulationStore);
20
+ let authorizeHandlers = {
21
+ query: createLoginRedirectHandler(options),
22
+ web_message: createWebMessageHandler()
23
+ };
24
+ let logger = createLogger(debug);
25
+ return {
26
+ ["/heartbeat"]: function(_, res) {
27
+ res.status(200).json({ ok: true });
28
+ },
29
+ ["/authorize"]: function(req, res, next) {
30
+ logger.log({ "/authorize": {
31
+ body: req.body,
32
+ query: req.query,
33
+ session: req.session
34
+ } });
35
+ let currentUser = req.query.currentUser;
36
+ assert(!!req.session, "no session");
37
+ if (currentUser) req.session.username = currentUser;
38
+ let responseMode = req.query.response_mode ?? "query";
39
+ assert(["query", "web_message"].includes(responseMode), `unknown response_mode ${responseMode}`);
40
+ let handler = authorizeHandlers[responseMode];
41
+ handler(req, res, next);
42
+ },
43
+ ["/login"]: function(req, res) {
44
+ logger.log({ "/login": {
45
+ body: req.body,
46
+ query: req.query
47
+ } });
48
+ let query = req.query;
49
+ let responseClientId = query.client_id ?? clientID;
50
+ let responseAudience = query.audience ?? audience;
51
+ assert(!!responseClientId, `no clientID assigned`);
52
+ let html = loginView({
53
+ domain: new URL(serviceURL(req)).host,
54
+ scope,
55
+ redirectUri: query.redirect_uri,
56
+ clientID: responseClientId,
57
+ audience: responseAudience,
58
+ loginFailed: false
59
+ });
60
+ res.set("Content-Type", "text/html");
61
+ res.status(200).send(Buffer.from(html));
62
+ },
63
+ ["/usernamepassword/login"]: function(req, res) {
64
+ logger.log({ "/usernamepassword/login": {
65
+ body: req.body,
66
+ query: req.query
67
+ } });
68
+ let { username, nonce, password } = req.body;
69
+ assert(!!username, "no username in /usernamepassword/login");
70
+ assert(!!nonce, "no nonce in /usernamepassword/login");
71
+ assert(!!req.session, "no session");
72
+ if (!personQuery((person) => person.email?.toLowerCase() === username.toLowerCase() && person.password === password)) {
73
+ let query = req.query;
74
+ let responseClientId = query.client_id ?? clientID;
75
+ let responseAudience = query.audience ?? audience;
76
+ assert(!!clientID, `no clientID assigned`);
77
+ let html = loginView({
78
+ domain: new URL(serviceURL(req)).host,
79
+ scope,
80
+ redirectUri: query.redirect_uri,
81
+ clientID: responseClientId,
82
+ audience: responseAudience,
83
+ loginFailed: true
84
+ });
85
+ res.set("Content-Type", "text/html");
86
+ res.status(400).send(html);
87
+ return;
88
+ }
89
+ req.session.username = username;
90
+ simulationStore.store.dispatch(simulationStore.actions.batchUpdater([simulationStore.schema.sessions.patch({ [nonce]: {
91
+ username,
92
+ nonce
93
+ } })]));
94
+ res.status(200).send(userNamePasswordForm(req.body));
95
+ },
96
+ ["/login/callback"]: function(req, res) {
97
+ let wctx = JSON.parse(req.body.wctx);
98
+ logger.log({ "/login/callback": {
99
+ body: req.body,
100
+ query: req.query,
101
+ wctx
102
+ } });
103
+ let { redirect_uri, nonce } = wctx;
104
+ const { username } = simulationStore.schema.sessions.selectById(simulationStore.store.getState(), { id: nonce }) ?? {};
105
+ let routerUrl = `${redirect_uri}?${stringify({
106
+ code: encode(`${nonce}:${username}`),
107
+ ...wctx
108
+ })}`;
109
+ res.redirect(302, routerUrl);
110
+ },
111
+ ["/oauth/token"]: async function(req, res, next) {
112
+ logger.log({ "/oauth/token": {
113
+ body: req.body,
114
+ query: req.query
115
+ } });
116
+ try {
117
+ let iss = serviceURL(req);
118
+ let responseClientId = req?.body?.client_id ?? clientID;
119
+ let responseAudience = req?.body?.audience ?? audience;
120
+ assert(!!responseClientId, "500::no clientID in options or request body");
121
+ let tokens = await createTokens({
122
+ simulationStore,
123
+ body: req.body,
124
+ iss,
125
+ clientID: responseClientId,
126
+ audience: responseAudience,
127
+ rulesDirectory,
128
+ scope
129
+ });
130
+ res.status(200).json({
131
+ ...tokens,
132
+ expires_in: 86400,
133
+ token_type: "Bearer"
134
+ });
135
+ } catch (error) {
136
+ next(error);
137
+ }
138
+ },
139
+ ["/v2/logout"]: function(req, res) {
140
+ req.session = null;
141
+ let returnToUrl = req.query.returnTo ?? req.headers.referer;
142
+ assert(typeof returnToUrl === "string", `no logical returnTo url`);
143
+ res.redirect(returnToUrl);
144
+ },
145
+ ["/userinfo"]: function(req, res) {
146
+ let token = null;
147
+ if (req.headers.authorization) token = req.headers.authorization?.split(" ")?.[1];
148
+ else token = req?.query?.access_token;
149
+ assert(!!token, "no authorization header or access_token");
150
+ let { sub } = decode$1(token, { json: true });
151
+ let user = personQuery((person) => {
152
+ assert(!!person.id, `no email defined on person scenario`);
153
+ return person.id === sub;
154
+ });
155
+ assert(!!user, "no user in /userinfo");
156
+ let userinfo = {
157
+ sub,
158
+ name: user.name,
159
+ given_name: user.name,
160
+ family_name: user.name,
161
+ email: user.email,
162
+ email_verified: true,
163
+ locale: "en",
164
+ hd: "okta.com"
165
+ };
166
+ res.status(200).json(userinfo);
167
+ },
168
+ ["/passwordless/start"]: function(req, res, next) {
169
+ logger.log({ "/passwordless/start": { body: req.body } });
170
+ try {
171
+ const { client_id, connection, email, phone_number } = req.body;
172
+ if (!client_id) {
173
+ res.status(400).json({ error: "client_id is required" });
174
+ return;
175
+ }
176
+ if (!connection || connection !== "email" && connection !== "sms") {
177
+ res.status(400).json({ error: "connection must be 'email' or 'sms'" });
178
+ return;
179
+ }
180
+ if (connection === "email" && !email) {
181
+ res.status(400).json({ error: "email is required when connection is 'email'" });
182
+ return;
183
+ }
184
+ if (connection === "sms" && !phone_number) {
185
+ res.status(400).json({ error: "phone_number is required when connection is 'sms'" });
186
+ return;
187
+ }
188
+ if (connection === "email") res.status(200).json({
189
+ _id: "000000000000000000000000",
190
+ email,
191
+ email_verified: false
192
+ });
193
+ else res.status(200).json({
194
+ _id: "000000000000000000000000",
195
+ phone_number,
196
+ phone_verified: false
197
+ });
198
+ } catch (error) {
199
+ next(error);
200
+ }
201
+ }
202
+ };
203
+ };
204
+
205
+ //#endregion
206
+ export { createAuth0Handlers };
207
+ //# sourceMappingURL=auth0-handlers.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth0-handlers.mjs","names":["authorizeHandlers: Record<ResponseModes, RequestHandler>","responseClientId: string","responseAudience: string","token: string | null","decodeToken"],"sources":["../../src/handlers/auth0-handlers.ts"],"sourcesContent":["import type { ExtendedSimulationStore } from \"../store/index.ts\";\nimport type { Request, RequestHandler } from \"express\";\nimport type {\n Auth0Configuration,\n QueryParams,\n ResponseModes,\n} from \"../types.ts\";\nimport { createLoginRedirectHandler } from \"./login-redirect.ts\";\nimport { createWebMessageHandler } from \"./web-message.ts\";\nimport { loginView } from \"../views/login.ts\";\nimport { createTokens } from \"./oauth-handlers.ts\";\nimport { assert } from \"assert-ts\";\nimport { stringify } from \"querystring\";\nimport { encode } from \"base64-url\";\nimport { userNamePasswordForm } from \"../views/username-password.ts\";\nimport { decode as decodeToken } from \"jsonwebtoken\";\nimport { createPersonQuery } from \"./utils.ts\";\n\nexport type Routes =\n | \"/heartbeat\"\n | \"/authorize\"\n | \"/login\"\n | \"/usernamepassword/login\"\n | \"/login/callback\"\n | \"/oauth/token\"\n | \"/v2/logout\"\n | \"/userinfo\"\n | \"/passwordless/start\";\n\nexport type AuthSession = { username: string; nonce: string };\n\ntype LoggerArgs = Parameters<typeof console.dir>;\n\nconst createLogger = (debug: boolean) => ({\n log: (...args: LoggerArgs): void => {\n if (!debug) {\n return;\n }\n\n console.dir(...args);\n },\n});\n\nexport const createAuth0Handlers = (\n simulationStore: ExtendedSimulationStore,\n serviceURL: (request: Request) => string,\n options: Auth0Configuration,\n debug: boolean\n): Record<Routes, RequestHandler> => {\n let { audience, scope, clientID, rulesDirectory } = options;\n let personQuery = createPersonQuery(simulationStore);\n\n let authorizeHandlers: Record<ResponseModes, RequestHandler> = {\n query: createLoginRedirectHandler(options),\n web_message: createWebMessageHandler(),\n };\n\n let logger = createLogger(debug);\n\n return {\n [\"/heartbeat\"]: function (_, res) {\n res.status(200).json({ ok: true });\n },\n\n [\"/authorize\"]: function (req, res, next) {\n logger.log({\n \"/authorize\": {\n body: req.body,\n query: req.query,\n session: req.session,\n },\n });\n let currentUser = req.query.currentUser as string | undefined;\n\n assert(!!req.session, \"no session\");\n\n if (currentUser) {\n // the request is a silent login.\n // We fake an existing login by\n // adding the user to the session\n req.session.username = currentUser;\n }\n\n let responseMode = (req.query.response_mode ?? \"query\") as ResponseModes;\n\n assert(\n [\"query\", \"web_message\"].includes(responseMode),\n `unknown response_mode ${responseMode}`\n );\n\n let handler = authorizeHandlers[responseMode];\n\n handler(req, res, next);\n },\n\n [\"/login\"]: function (req, res) {\n logger.log({ \"/login\": { body: req.body, query: req.query } });\n let query = req.query as QueryParams;\n let responseClientId = query.client_id ?? clientID;\n let responseAudience = query.audience ?? audience;\n assert(!!responseClientId, `no clientID assigned`);\n\n let html = loginView({\n domain: new URL(serviceURL(req)).host,\n scope,\n redirectUri: query.redirect_uri,\n clientID: responseClientId,\n audience: responseAudience,\n loginFailed: false,\n });\n\n res.set(\"Content-Type\", \"text/html\");\n\n res.status(200).send(Buffer.from(html));\n },\n\n [\"/usernamepassword/login\"]: function (req, res) {\n logger.log({\n \"/usernamepassword/login\": { body: req.body, query: req.query },\n });\n let { username, nonce, password } = req.body;\n\n assert(!!username, \"no username in /usernamepassword/login\");\n assert(!!nonce, \"no nonce in /usernamepassword/login\");\n assert(!!req.session, \"no session\");\n\n let user = personQuery(\n (person) =>\n person.email?.toLowerCase() === username.toLowerCase() &&\n person.password === password\n );\n\n if (!user) {\n let query = req.query as QueryParams;\n let responseClientId = query.client_id ?? clientID;\n let responseAudience = query.audience ?? audience;\n\n assert(!!clientID, `no clientID assigned`);\n\n let html = loginView({\n domain: new URL(serviceURL(req)).host,\n scope,\n redirectUri: query.redirect_uri,\n clientID: responseClientId,\n audience: responseAudience,\n loginFailed: true,\n });\n\n res.set(\"Content-Type\", \"text/html\");\n\n res.status(400).send(html);\n return;\n }\n\n req.session.username = username;\n\n simulationStore.store.dispatch(\n simulationStore.actions.batchUpdater([\n simulationStore.schema.sessions.patch({\n [nonce]: { username, nonce },\n }),\n ])\n );\n\n res.status(200).send(userNamePasswordForm(req.body));\n },\n\n [\"/login/callback\"]: function (req, res) {\n let wctx = JSON.parse(req.body.wctx);\n logger.log({\n \"/login/callback\": { body: req.body, query: req.query, wctx },\n });\n\n let { redirect_uri, nonce } = wctx;\n\n const session = simulationStore.schema.sessions.selectById(\n simulationStore.store.getState(),\n { id: nonce }\n );\n\n const { username } = session ?? {};\n\n let encodedNonce = encode(`${nonce}:${username}`);\n\n let qs = stringify({ code: encodedNonce, ...wctx });\n\n let routerUrl = `${redirect_uri}?${qs}`;\n\n res.redirect(302, routerUrl);\n },\n\n [\"/oauth/token\"]: async function (req, res, next) {\n logger.log({ \"/oauth/token\": { body: req.body, query: req.query } });\n try {\n let iss = serviceURL(req);\n\n let responseClientId: string =\n (req?.body?.client_id as string) ?? clientID;\n let responseAudience: string =\n (req?.body?.audience as string) ?? audience;\n\n assert(\n !!responseClientId,\n \"500::no clientID in options or request body\"\n );\n\n let tokens = await createTokens({\n simulationStore,\n body: req.body,\n iss,\n clientID: responseClientId,\n audience: responseAudience,\n rulesDirectory,\n scope,\n });\n\n res.status(200).json({\n ...tokens,\n expires_in: 86400,\n token_type: \"Bearer\",\n });\n } catch (error) {\n next(error);\n }\n },\n\n [\"/v2/logout\"]: function (req, res) {\n req.session = null;\n\n let returnToUrl = req.query.returnTo ?? req.headers.referer;\n\n assert(typeof returnToUrl === \"string\", `no logical returnTo url`);\n\n res.redirect(returnToUrl);\n },\n\n [\"/userinfo\"]: function (req, res) {\n let token: string | null = null;\n if (req.headers.authorization) {\n let authorizationHeader = req.headers.authorization;\n token = authorizationHeader?.split(\" \")?.[1];\n } else {\n token = req?.query?.access_token as string;\n }\n\n assert(!!token, \"no authorization header or access_token\");\n let { sub } = decodeToken(token, { json: true }) as { sub: string };\n\n let user = personQuery((person) => {\n assert(!!person.id, `no email defined on person scenario`);\n\n return person.id === sub;\n });\n\n assert(!!user, \"no user in /userinfo\");\n\n let userinfo = {\n sub,\n name: user.name,\n given_name: user.name,\n family_name: user.name,\n email: user.email,\n email_verified: true,\n locale: \"en\",\n hd: \"okta.com\",\n };\n\n res.status(200).json(userinfo);\n },\n\n [\"/passwordless/start\"]: function (req, res, next) {\n logger.log({ \"/passwordless/start\": { body: req.body } });\n\n try {\n const { client_id, connection, email, phone_number } = req.body;\n\n // Validate required fields\n if (!client_id) {\n res.status(400).json({ error: \"client_id is required\" });\n return;\n }\n\n if (!connection || (connection !== \"email\" && connection !== \"sms\")) {\n res.status(400).json({\n error: \"connection must be 'email' or 'sms'\",\n });\n return;\n }\n\n if (connection === \"email\" && !email) {\n res.status(400).json({\n error: \"email is required when connection is 'email'\",\n });\n return;\n }\n\n if (connection === \"sms\" && !phone_number) {\n res.status(400).json({\n error: \"phone_number is required when connection is 'sms'\",\n });\n return;\n }\n\n // Return appropriate response based on connection type\n if (connection === \"email\") {\n res.status(200).json({\n _id: \"000000000000000000000000\",\n email: email,\n email_verified: false,\n });\n } else {\n res.status(200).json({\n _id: \"000000000000000000000000\",\n phone_number: phone_number,\n phone_verified: false,\n });\n }\n } catch (error) {\n next(error);\n }\n },\n };\n};\n"],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');
2
+ const require_create_cors = require('../middleware/create-cors.cjs');
3
+ const require_no_cache = require('../middleware/no-cache.cjs');
4
+ const require_session = require('../middleware/session.cjs');
5
+ const require_error_handling = require('../middleware/error-handling.cjs');
6
+ const require_auth0_handlers = require('./auth0-handlers.cjs');
7
+ const require_openid_handlers = require('./openid-handlers.cjs');
8
+ require('../types.cjs');
9
+ let express = require("express");
10
+ express = require_rolldown_runtime.__toESM(express);
11
+ let path = require("path");
12
+ path = require_rolldown_runtime.__toESM(path);
13
+
14
+ //#region src/handlers/index.ts
15
+ const publicDir = path.default.join(__dirname, "..", "views", "public");
16
+ const extendRouter = (config, extend, debug = false) => (router, simulationStore) => {
17
+ const serviceURL = (request) => `${request.protocol}://${request.get("Host")}/`;
18
+ const auth0 = require_auth0_handlers.createAuth0Handlers(simulationStore, serviceURL, config, debug);
19
+ const openid = require_openid_handlers.createOpenIdHandlers(serviceURL);
20
+ router.use(express.default.static(publicDir)).use(require_session.createSession()).use(require_create_cors.createCors()).use(require_no_cache.noCache());
21
+ if (extend) extend(router, simulationStore);
22
+ router.get("/health", (_, response) => {
23
+ response.send({ status: "ok" });
24
+ }).get("/heartbeat", auth0["/heartbeat"]).get("/authorize", auth0["/authorize"]).get("/login", auth0["/login"]).get("/u/login", auth0["/usernamepassword/login"]).post("/usernamepassword/login", auth0["/usernamepassword/login"]).post("/login/callback", auth0["/login/callback"]).post("/oauth/token", auth0["/oauth/token"]).post("/passwordless/start", auth0["/passwordless/start"]).get("/userinfo", auth0["/userinfo"]).get("/v2/logout", auth0["/v2/logout"]).get("/.well-known/jwks.json", openid["/.well-known/jwks.json"]).get("/.well-known/openid-configuration", openid["/.well-known/openid-configuration"]);
25
+ router.use(require_error_handling.defaultErrorHandler);
26
+ };
27
+
28
+ //#endregion
29
+ exports.extendRouter = extendRouter;
@@ -0,0 +1,27 @@
1
+ import { createCors } from "../middleware/create-cors.mjs";
2
+ import { noCache } from "../middleware/no-cache.mjs";
3
+ import { createSession } from "../middleware/session.mjs";
4
+ import { defaultErrorHandler } from "../middleware/error-handling.mjs";
5
+ import { createAuth0Handlers } from "./auth0-handlers.mjs";
6
+ import { createOpenIdHandlers } from "./openid-handlers.mjs";
7
+ import "../types.mjs";
8
+ import express, { Router } from "express";
9
+ import path from "path";
10
+
11
+ //#region src/handlers/index.ts
12
+ const publicDir = path.join(import.meta.dirname, "..", "views", "public");
13
+ const extendRouter = (config, extend, debug = false) => (router, simulationStore) => {
14
+ const serviceURL = (request) => `${request.protocol}://${request.get("Host")}/`;
15
+ const auth0 = createAuth0Handlers(simulationStore, serviceURL, config, debug);
16
+ const openid = createOpenIdHandlers(serviceURL);
17
+ router.use(express.static(publicDir)).use(createSession()).use(createCors()).use(noCache());
18
+ if (extend) extend(router, simulationStore);
19
+ router.get("/health", (_, response) => {
20
+ response.send({ status: "ok" });
21
+ }).get("/heartbeat", auth0["/heartbeat"]).get("/authorize", auth0["/authorize"]).get("/login", auth0["/login"]).get("/u/login", auth0["/usernamepassword/login"]).post("/usernamepassword/login", auth0["/usernamepassword/login"]).post("/login/callback", auth0["/login/callback"]).post("/oauth/token", auth0["/oauth/token"]).post("/passwordless/start", auth0["/passwordless/start"]).get("/userinfo", auth0["/userinfo"]).get("/v2/logout", auth0["/v2/logout"]).get("/.well-known/jwks.json", openid["/.well-known/jwks.json"]).get("/.well-known/openid-configuration", openid["/.well-known/openid-configuration"]);
22
+ router.use(defaultErrorHandler);
23
+ };
24
+
25
+ //#endregion
26
+ export { extendRouter };
27
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/handlers/index.ts"],"sourcesContent":["import express, { type Request, type Express, Router } from \"express\";\nimport type { ExtendedSimulationStore } from \"../store/index.ts\";\nimport { createCors } from \"../middleware/create-cors.ts\";\nimport { noCache } from \"../middleware/no-cache.ts\";\nimport { createSession } from \"../middleware/session.ts\";\nimport { defaultErrorHandler } from \"../middleware/error-handling.ts\";\nimport { createAuth0Handlers } from \"./auth0-handlers.ts\";\nimport { createOpenIdHandlers } from \"./openid-handlers.ts\";\nimport path from \"path\";\nimport { type Auth0Configuration } from \"../types.ts\";\n\nconst publicDir = path.join(import.meta.dirname, \"..\", \"views\", \"public\");\nexport const extendRouter =\n (\n config: Auth0Configuration,\n extend:\n | ((router: Router, simulationStore: ExtendedSimulationStore) => void)\n | undefined,\n debug = false\n ) =>\n (router: Express, simulationStore: ExtendedSimulationStore) => {\n const serviceURL = (request: Request) =>\n `${request.protocol}://${request.get(\"Host\")}/`;\n const auth0 = createAuth0Handlers(\n simulationStore,\n serviceURL,\n config,\n debug\n );\n const openid = createOpenIdHandlers(serviceURL);\n\n router\n .use(express.static(publicDir))\n .use(createSession())\n .use(createCors())\n .use(noCache());\n\n if (extend) {\n extend(router, simulationStore);\n }\n\n router\n .get(\"/health\", (_, response) => {\n response.send({ status: \"ok\" });\n })\n .get(\"/heartbeat\", auth0[\"/heartbeat\"])\n .get(\"/authorize\", auth0[\"/authorize\"])\n .get(\"/login\", auth0[\"/login\"])\n .get(\"/u/login\", auth0[\"/usernamepassword/login\"])\n .post(\"/usernamepassword/login\", auth0[\"/usernamepassword/login\"])\n .post(\"/login/callback\", auth0[\"/login/callback\"])\n .post(\"/oauth/token\", auth0[\"/oauth/token\"])\n .post(\"/passwordless/start\", auth0[\"/passwordless/start\"])\n .get(\"/userinfo\", auth0[\"/userinfo\"])\n .get(\"/v2/logout\", auth0[\"/v2/logout\"])\n .get(\"/.well-known/jwks.json\", openid[\"/.well-known/jwks.json\"])\n .get(\n \"/.well-known/openid-configuration\",\n openid[\"/.well-known/openid-configuration\"]\n );\n\n // needs to be the last middleware added\n router.use(defaultErrorHandler);\n };\n"],"mappings":";;;;;;;;;;;AAWA,MAAM,YAAY,KAAK,KAAK,OAAO,KAAK,SAAS,MAAM,SAAS,SAAS;AACzE,MAAa,gBAET,QACA,QAGA,QAAQ,WAET,QAAiB,oBAA6C;CAC7D,MAAM,cAAc,YAClB,GAAG,QAAQ,SAAS,KAAK,QAAQ,IAAI,OAAO,CAAC;CAC/C,MAAM,QAAQ,oBACZ,iBACA,YACA,QACA,MACD;CACD,MAAM,SAAS,qBAAqB,WAAW;AAE/C,QACG,IAAI,QAAQ,OAAO,UAAU,CAAC,CAC9B,IAAI,eAAe,CAAC,CACpB,IAAI,YAAY,CAAC,CACjB,IAAI,SAAS,CAAC;AAEjB,KAAI,OACF,QAAO,QAAQ,gBAAgB;AAGjC,QACG,IAAI,YAAY,GAAG,aAAa;AAC/B,WAAS,KAAK,EAAE,QAAQ,MAAM,CAAC;GAC/B,CACD,IAAI,cAAc,MAAM,cAAc,CACtC,IAAI,cAAc,MAAM,cAAc,CACtC,IAAI,UAAU,MAAM,UAAU,CAC9B,IAAI,YAAY,MAAM,2BAA2B,CACjD,KAAK,2BAA2B,MAAM,2BAA2B,CACjE,KAAK,mBAAmB,MAAM,mBAAmB,CACjD,KAAK,gBAAgB,MAAM,gBAAgB,CAC3C,KAAK,uBAAuB,MAAM,uBAAuB,CACzD,IAAI,aAAa,MAAM,aAAa,CACpC,IAAI,cAAc,MAAM,cAAc,CACtC,IAAI,0BAA0B,OAAO,0BAA0B,CAC/D,IACC,qCACA,OAAO,qCACR;AAGH,QAAO,IAAI,oBAAoB"}
@@ -0,0 +1,24 @@
1
+ const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');
2
+ let querystring = require("querystring");
3
+
4
+ //#region src/handlers/login-redirect.ts
5
+ const createLoginRedirectHandler = (options) => function loginRedirect(req, res) {
6
+ let { client_id, audience, redirect_uri, scope, state, nonce, response_mode, code_challenge, code_challenge_method, auth0Client, response_type } = req.query;
7
+ res.status(302).redirect(`/login?${(0, querystring.stringify)({
8
+ state,
9
+ redirect_uri,
10
+ client: client_id || options.clientID,
11
+ protocol: "oauth2",
12
+ scope,
13
+ response_type,
14
+ response_mode,
15
+ nonce,
16
+ code_challenge,
17
+ code_challenge_method,
18
+ auth0Client,
19
+ audience: audience || options.audience
20
+ })}`);
21
+ };
22
+
23
+ //#endregion
24
+ exports.createLoginRedirectHandler = createLoginRedirectHandler;
@@ -0,0 +1,24 @@
1
+ import { stringify } from "querystring";
2
+
3
+ //#region src/handlers/login-redirect.ts
4
+ const createLoginRedirectHandler = (options) => function loginRedirect(req, res) {
5
+ let { client_id, audience, redirect_uri, scope, state, nonce, response_mode, code_challenge, code_challenge_method, auth0Client, response_type } = req.query;
6
+ res.status(302).redirect(`/login?${stringify({
7
+ state,
8
+ redirect_uri,
9
+ client: client_id || options.clientID,
10
+ protocol: "oauth2",
11
+ scope,
12
+ response_type,
13
+ response_mode,
14
+ nonce,
15
+ code_challenge,
16
+ code_challenge_method,
17
+ auth0Client,
18
+ audience: audience || options.audience
19
+ })}`);
20
+ };
21
+
22
+ //#endregion
23
+ export { createLoginRedirectHandler };
24
+ //# sourceMappingURL=login-redirect.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login-redirect.mjs","names":[],"sources":["../../src/handlers/login-redirect.ts"],"sourcesContent":["import type { Request, Response, RequestHandler } from \"express\";\nimport type { Auth0Configuration, QueryParams } from \"../types.ts\";\nimport { stringify } from \"querystring\";\n\nexport const createLoginRedirectHandler = (\n options: Auth0Configuration\n): RequestHandler =>\n function loginRedirect(req: Request, res: Response) {\n let {\n client_id,\n audience,\n redirect_uri,\n scope,\n state,\n nonce,\n response_mode,\n code_challenge,\n code_challenge_method,\n auth0Client,\n response_type,\n } = req.query as QueryParams;\n\n res.status(302).redirect(\n `/login?${stringify({\n state,\n redirect_uri,\n client: client_id || options.clientID,\n protocol: \"oauth2\",\n scope,\n response_type,\n response_mode,\n nonce,\n code_challenge,\n code_challenge_method,\n auth0Client,\n audience: audience || options.audience,\n })}`\n );\n };\n"],"mappings":";;;AAIA,MAAa,8BACX,YAEA,SAAS,cAAc,KAAc,KAAe;CAClD,IAAI,EACF,WACA,UACA,cACA,OACA,OACA,OACA,eACA,gBACA,uBACA,aACA,kBACE,IAAI;AAER,KAAI,OAAO,IAAI,CAAC,SACd,UAAU,UAAU;EAClB;EACA;EACA,QAAQ,aAAa,QAAQ;EAC7B,UAAU;EACV;EACA;EACA;EACA;EACA;EACA;EACA;EACA,UAAU,YAAY,QAAQ;EAC/B,CAAC,GACH"}
@@ -0,0 +1,144 @@
1
+ const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');
2
+ require('../store/entities.cjs');
3
+ require('../store/index.cjs');
4
+ const require_utils = require('./utils.cjs');
5
+ const require_date = require('../auth/date.cjs');
6
+ const require_jwt = require('../auth/jwt.cjs');
7
+ const require_rules_runner = require('../rules/rules-runner.cjs');
8
+ const require_refresh_token = require('../auth/refresh-token.cjs');
9
+ let assert_ts = require("assert-ts");
10
+ let base64_url = require("base64-url");
11
+
12
+ //#region src/handlers/oauth-handlers.ts
13
+ const createTokens = async ({ body, iss, clientID, audience, rulesDirectory, scope: scopeConfig, simulationStore }) => {
14
+ let { grant_type } = body;
15
+ let scope = require_utils.deriveScope({
16
+ scopeConfig,
17
+ clientID,
18
+ audience
19
+ });
20
+ let accessToken = getBaseAccessToken({
21
+ iss,
22
+ grant_type,
23
+ scope,
24
+ audience
25
+ });
26
+ let user;
27
+ let nonce;
28
+ if (grant_type === "client_credentials") return { access_token: require_jwt.createJsonWebToken(accessToken) };
29
+ else if (grant_type === "refresh_token") {
30
+ let { refresh_token: refreshTokenValue } = body;
31
+ let refreshToken = JSON.parse((0, base64_url.decode)(refreshTokenValue));
32
+ user = require_utils.createPersonQuery(simulationStore)((person) => person.id === refreshToken.user.id);
33
+ nonce = refreshToken.nonce;
34
+ (0, assert_ts.assert)(!!nonce, `400::No nonce in request`);
35
+ } else {
36
+ let result = verifyUserExistsInStore({
37
+ simulationStore,
38
+ body,
39
+ grant_type
40
+ });
41
+ user = result.user;
42
+ nonce = result.nonce;
43
+ }
44
+ (0, assert_ts.assert)(!!user, "500::No user found");
45
+ let { idTokenData, userData } = getIdToken({
46
+ body,
47
+ iss,
48
+ user,
49
+ clientID,
50
+ nonce
51
+ });
52
+ let context = {
53
+ clientID,
54
+ accessToken: {
55
+ scope,
56
+ sub: idTokenData.sub
57
+ },
58
+ idToken: idTokenData
59
+ };
60
+ await require_rules_runner.createRulesRunner(rulesDirectory)(userData, context);
61
+ return {
62
+ access_token: require_jwt.createJsonWebToken({
63
+ ...accessToken,
64
+ ...context.accessToken,
65
+ ...scope.split(" ").includes("email") ? { email: user.email } : {}
66
+ }),
67
+ id_token: require_jwt.createJsonWebToken({
68
+ ...userData,
69
+ ...context.idToken
70
+ }),
71
+ refresh_token: require_refresh_token.issueRefreshToken(scope, grant_type) ? require_refresh_token.createRefreshToken({
72
+ exp: idTokenData.exp,
73
+ rotations: 0,
74
+ scope,
75
+ user,
76
+ nonce
77
+ }) : void 0
78
+ };
79
+ };
80
+ const getIdToken = ({ body, iss, user, clientID, nonce }) => {
81
+ let userData = {
82
+ name: body?.name ?? user.name,
83
+ email: body?.email ?? user.email,
84
+ email_verified: true,
85
+ user_id: body?.id ?? user.id,
86
+ nickname: body?.nickname,
87
+ picture: body?.picture ?? user.picture,
88
+ identities: body?.identities
89
+ };
90
+ (0, assert_ts.assert)(!!user.email, "500::User in store requires an email");
91
+ let idTokenData = {
92
+ alg: "RS256",
93
+ typ: "JWT",
94
+ iss,
95
+ exp: require_date.expiresAt(),
96
+ iat: require_date.epochTime(),
97
+ email: user.email,
98
+ aud: clientID,
99
+ sub: user.id
100
+ };
101
+ if (typeof nonce !== "undefined") idTokenData.nonce = nonce;
102
+ return {
103
+ userData,
104
+ idTokenData
105
+ };
106
+ };
107
+ const getBaseAccessToken = ({ iss, grant_type, scope, audience }) => ({
108
+ iss,
109
+ exp: require_date.expiresAt(),
110
+ iat: require_date.epochTime(),
111
+ aud: audience,
112
+ gty: grant_type,
113
+ scope
114
+ });
115
+ const verifyUserExistsInStore = ({ simulationStore, body, grant_type }) => {
116
+ let { code } = body;
117
+ let personQuery = require_utils.createPersonQuery(simulationStore);
118
+ let nonce;
119
+ let username;
120
+ let password;
121
+ if (grant_type === "http://auth0.com/oauth/grant-type/passwordless/otp") username = body.username;
122
+ else if (grant_type === "password") {
123
+ username = body.username;
124
+ password = body.password;
125
+ } else {
126
+ (0, assert_ts.assert)(typeof code !== "undefined", "400::no code in /oauth/token");
127
+ [nonce, username] = (0, base64_url.decode)(code).split(":");
128
+ (0, assert_ts.assert)(!!username, `400::no nonce in store for ${code}`);
129
+ }
130
+ let user = personQuery((person) => {
131
+ (0, assert_ts.assert)(!!person.email, `500::no email defined on person scenario`);
132
+ let valid = person.email.toLowerCase() === username.toLowerCase();
133
+ if (typeof password === "undefined") return valid;
134
+ else return valid && password === person.password;
135
+ });
136
+ (0, assert_ts.assert)(!!user, "401::Unauthorized");
137
+ return {
138
+ user,
139
+ nonce
140
+ };
141
+ };
142
+
143
+ //#endregion
144
+ exports.createTokens = createTokens;
@@ -0,0 +1,144 @@
1
+ import "../store/entities.mjs";
2
+ import "../store/index.mjs";
3
+ import { createPersonQuery, deriveScope } from "./utils.mjs";
4
+ import { epochTime, expiresAt } from "../auth/date.mjs";
5
+ import { createJsonWebToken } from "../auth/jwt.mjs";
6
+ import { createRulesRunner } from "../rules/rules-runner.mjs";
7
+ import { createRefreshToken, issueRefreshToken } from "../auth/refresh-token.mjs";
8
+ import { assert } from "assert-ts";
9
+ import { decode } from "base64-url";
10
+
11
+ //#region src/handlers/oauth-handlers.ts
12
+ const createTokens = async ({ body, iss, clientID, audience, rulesDirectory, scope: scopeConfig, simulationStore }) => {
13
+ let { grant_type } = body;
14
+ let scope = deriveScope({
15
+ scopeConfig,
16
+ clientID,
17
+ audience
18
+ });
19
+ let accessToken = getBaseAccessToken({
20
+ iss,
21
+ grant_type,
22
+ scope,
23
+ audience
24
+ });
25
+ let user;
26
+ let nonce;
27
+ if (grant_type === "client_credentials") return { access_token: createJsonWebToken(accessToken) };
28
+ else if (grant_type === "refresh_token") {
29
+ let { refresh_token: refreshTokenValue } = body;
30
+ let refreshToken = JSON.parse(decode(refreshTokenValue));
31
+ user = createPersonQuery(simulationStore)((person) => person.id === refreshToken.user.id);
32
+ nonce = refreshToken.nonce;
33
+ assert(!!nonce, `400::No nonce in request`);
34
+ } else {
35
+ let result = verifyUserExistsInStore({
36
+ simulationStore,
37
+ body,
38
+ grant_type
39
+ });
40
+ user = result.user;
41
+ nonce = result.nonce;
42
+ }
43
+ assert(!!user, "500::No user found");
44
+ let { idTokenData, userData } = getIdToken({
45
+ body,
46
+ iss,
47
+ user,
48
+ clientID,
49
+ nonce
50
+ });
51
+ let context = {
52
+ clientID,
53
+ accessToken: {
54
+ scope,
55
+ sub: idTokenData.sub
56
+ },
57
+ idToken: idTokenData
58
+ };
59
+ await createRulesRunner(rulesDirectory)(userData, context);
60
+ return {
61
+ access_token: createJsonWebToken({
62
+ ...accessToken,
63
+ ...context.accessToken,
64
+ ...scope.split(" ").includes("email") ? { email: user.email } : {}
65
+ }),
66
+ id_token: createJsonWebToken({
67
+ ...userData,
68
+ ...context.idToken
69
+ }),
70
+ refresh_token: issueRefreshToken(scope, grant_type) ? createRefreshToken({
71
+ exp: idTokenData.exp,
72
+ rotations: 0,
73
+ scope,
74
+ user,
75
+ nonce
76
+ }) : void 0
77
+ };
78
+ };
79
+ const getIdToken = ({ body, iss, user, clientID, nonce }) => {
80
+ let userData = {
81
+ name: body?.name ?? user.name,
82
+ email: body?.email ?? user.email,
83
+ email_verified: true,
84
+ user_id: body?.id ?? user.id,
85
+ nickname: body?.nickname,
86
+ picture: body?.picture ?? user.picture,
87
+ identities: body?.identities
88
+ };
89
+ assert(!!user.email, "500::User in store requires an email");
90
+ let idTokenData = {
91
+ alg: "RS256",
92
+ typ: "JWT",
93
+ iss,
94
+ exp: expiresAt(),
95
+ iat: epochTime(),
96
+ email: user.email,
97
+ aud: clientID,
98
+ sub: user.id
99
+ };
100
+ if (typeof nonce !== "undefined") idTokenData.nonce = nonce;
101
+ return {
102
+ userData,
103
+ idTokenData
104
+ };
105
+ };
106
+ const getBaseAccessToken = ({ iss, grant_type, scope, audience }) => ({
107
+ iss,
108
+ exp: expiresAt(),
109
+ iat: epochTime(),
110
+ aud: audience,
111
+ gty: grant_type,
112
+ scope
113
+ });
114
+ const verifyUserExistsInStore = ({ simulationStore, body, grant_type }) => {
115
+ let { code } = body;
116
+ let personQuery = createPersonQuery(simulationStore);
117
+ let nonce;
118
+ let username;
119
+ let password;
120
+ if (grant_type === "http://auth0.com/oauth/grant-type/passwordless/otp") username = body.username;
121
+ else if (grant_type === "password") {
122
+ username = body.username;
123
+ password = body.password;
124
+ } else {
125
+ assert(typeof code !== "undefined", "400::no code in /oauth/token");
126
+ [nonce, username] = decode(code).split(":");
127
+ assert(!!username, `400::no nonce in store for ${code}`);
128
+ }
129
+ let user = personQuery((person) => {
130
+ assert(!!person.email, `500::no email defined on person scenario`);
131
+ let valid = person.email.toLowerCase() === username.toLowerCase();
132
+ if (typeof password === "undefined") return valid;
133
+ else return valid && password === person.password;
134
+ });
135
+ assert(!!user, "401::Unauthorized");
136
+ return {
137
+ user,
138
+ nonce
139
+ };
140
+ };
141
+
142
+ //#endregion
143
+ export { createTokens };
144
+ //# sourceMappingURL=oauth-handlers.mjs.map