oidc-spa 8.4.8 → 8.5.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.
Files changed (68) hide show
  1. package/README.md +2 -5
  2. package/core/createOidc.js +3 -1
  3. package/core/createOidc.js.map +1 -1
  4. package/core/earlyInit.d.ts +45 -7
  5. package/core/earlyInit.js +69 -153
  6. package/core/earlyInit.js.map +1 -1
  7. package/core/oidcClientTsUserToTokens.d.ts +1 -0
  8. package/core/oidcClientTsUserToTokens.js +11 -1
  9. package/core/oidcClientTsUserToTokens.js.map +1 -1
  10. package/core/tokenExfiltrationDefense.d.ts +6 -0
  11. package/core/tokenExfiltrationDefense.js +607 -0
  12. package/core/tokenExfiltrationDefense.js.map +1 -0
  13. package/core/tokenExfiltrationDefense_legacy.d.ts +8 -0
  14. package/core/tokenExfiltrationDefense_legacy.js +133 -0
  15. package/core/tokenExfiltrationDefense_legacy.js.map +1 -0
  16. package/core/tokenPlaceholderSubstitution.d.ts +13 -0
  17. package/core/tokenPlaceholderSubstitution.js +79 -0
  18. package/core/tokenPlaceholderSubstitution.js.map +1 -0
  19. package/esm/core/createOidc.js +3 -1
  20. package/esm/core/createOidc.js.map +1 -1
  21. package/esm/core/earlyInit.d.ts +45 -7
  22. package/esm/core/earlyInit.js +69 -153
  23. package/esm/core/earlyInit.js.map +1 -1
  24. package/esm/core/oidcClientTsUserToTokens.d.ts +1 -0
  25. package/esm/core/oidcClientTsUserToTokens.js +11 -1
  26. package/esm/core/oidcClientTsUserToTokens.js.map +1 -1
  27. package/esm/core/tokenExfiltrationDefense.d.ts +6 -0
  28. package/esm/core/tokenExfiltrationDefense.js +604 -0
  29. package/esm/core/tokenExfiltrationDefense.js.map +1 -0
  30. package/esm/core/tokenExfiltrationDefense_legacy.d.ts +8 -0
  31. package/esm/core/tokenExfiltrationDefense_legacy.js +130 -0
  32. package/esm/core/tokenExfiltrationDefense_legacy.js.map +1 -0
  33. package/esm/core/tokenPlaceholderSubstitution.d.ts +13 -0
  34. package/esm/core/tokenPlaceholderSubstitution.js +73 -0
  35. package/esm/core/tokenPlaceholderSubstitution.js.map +1 -0
  36. package/esm/tools/isDomain.d.ts +1 -0
  37. package/esm/tools/isDomain.js +16 -0
  38. package/esm/tools/isDomain.js.map +1 -0
  39. package/esm/tools/isHostnameAuthorized.d.ts +5 -0
  40. package/esm/tools/isHostnameAuthorized.js +74 -0
  41. package/esm/tools/isHostnameAuthorized.js.map +1 -0
  42. package/esm/tools/isLikelyDevServer.js +18 -10
  43. package/esm/tools/isLikelyDevServer.js.map +1 -1
  44. package/package.json +1 -1
  45. package/src/core/createOidc.ts +2 -0
  46. package/src/core/earlyInit.ts +138 -192
  47. package/src/core/oidcClientTsUserToTokens.ts +14 -0
  48. package/src/core/tokenExfiltrationDefense.ts +862 -0
  49. package/src/core/tokenExfiltrationDefense_legacy.ts +165 -0
  50. package/src/core/tokenPlaceholderSubstitution.ts +105 -0
  51. package/src/tools/isDomain.ts +18 -0
  52. package/src/tools/isHostnameAuthorized.ts +91 -0
  53. package/src/tools/isLikelyDevServer.ts +23 -11
  54. package/src/vite-plugin/handleClientEntrypoint.ts +57 -20
  55. package/src/vite-plugin/vite-plugin.ts +5 -10
  56. package/tools/isDomain.d.ts +1 -0
  57. package/tools/isDomain.js +19 -0
  58. package/tools/isDomain.js.map +1 -0
  59. package/tools/isHostnameAuthorized.d.ts +5 -0
  60. package/tools/isHostnameAuthorized.js +77 -0
  61. package/tools/isHostnameAuthorized.js.map +1 -0
  62. package/tools/isLikelyDevServer.js +18 -10
  63. package/tools/isLikelyDevServer.js.map +1 -1
  64. package/vite-plugin/handleClientEntrypoint.js +36 -17
  65. package/vite-plugin/handleClientEntrypoint.js.map +1 -1
  66. package/vite-plugin/vite-plugin.d.ts +3 -4
  67. package/vite-plugin/vite-plugin.js +1 -5
  68. package/vite-plugin/vite-plugin.js.map +1 -1
@@ -0,0 +1,165 @@
1
+ import { assert } from "../tools/tsafe/assert";
2
+
3
+ export type Params = {
4
+ freezeFetch?: boolean;
5
+ freezeXMLHttpRequest?: boolean;
6
+ freezeWebSocket?: boolean;
7
+ freezePromise?: boolean;
8
+ safeMode?: boolean;
9
+ };
10
+
11
+ export function handleTokenExfiltrationDefense_legacy(params: Params) {
12
+ const {
13
+ freezeFetch,
14
+ freezeXMLHttpRequest,
15
+ freezeWebSocket,
16
+ freezePromise,
17
+ safeMode = false
18
+ } = params;
19
+
20
+ const createWriteError = (target: string) =>
21
+ new Error(
22
+ [
23
+ `oidc-spa: Monkey patching of ${target} has been blocked for security reasons.`,
24
+ "You can disable this restriction by setting `safeMode: false` in `oidcEarlyInit()`",
25
+ "or in your Vite plugin configuration,",
26
+ "but please note this will reduce security.",
27
+ "If you believe this restriction is too strict, please open an issue at:",
28
+ "https://github.com/keycloakify/oidc-spa",
29
+ "We're still identifying real-world blockers and can safely add exceptions where needed.",
30
+ "For now, we prefer to err on the side of hardening rather than exposure."
31
+ ].join(" ")
32
+ );
33
+
34
+ for (const name of [
35
+ "fetch",
36
+ "XMLHttpRequest",
37
+ "WebSocket",
38
+ "Headers",
39
+ "URLSearchParams",
40
+ "String",
41
+ "Object",
42
+ "Promise",
43
+ "Array",
44
+ "RegExp",
45
+ "TextEncoder",
46
+ "Uint8Array",
47
+ "Uint32Array",
48
+ "Response",
49
+ "Reflect",
50
+ "JSON",
51
+ "encodeURIComponent",
52
+ "decodeURIComponent",
53
+ "atob",
54
+ "btoa"
55
+ ] as const) {
56
+ const doSkip = (() => {
57
+ switch (name) {
58
+ case "XMLHttpRequest":
59
+ if (freezeXMLHttpRequest !== undefined) {
60
+ return !freezeXMLHttpRequest;
61
+ }
62
+ break;
63
+ case "fetch":
64
+ if (freezeFetch !== undefined) {
65
+ return !freezeFetch;
66
+ }
67
+ break;
68
+ case "WebSocket":
69
+ if (freezeWebSocket !== undefined) {
70
+ return !freezeWebSocket;
71
+ }
72
+ break;
73
+ case "Promise":
74
+ if (freezePromise !== undefined) {
75
+ return !freezePromise;
76
+ }
77
+ break;
78
+ }
79
+
80
+ return !safeMode;
81
+ })();
82
+
83
+ if (doSkip) {
84
+ continue;
85
+ }
86
+
87
+ const original = window[name];
88
+
89
+ if (!original) {
90
+ continue;
91
+ }
92
+
93
+ if ("prototype" in original) {
94
+ for (const propertyName of Object.getOwnPropertyNames(original.prototype)) {
95
+ if (name === "Object") {
96
+ if (
97
+ propertyName === "toString" ||
98
+ propertyName === "constructor" ||
99
+ propertyName === "valueOf"
100
+ ) {
101
+ continue;
102
+ }
103
+ }
104
+
105
+ if (name === "Array") {
106
+ if (propertyName === "constructor" || propertyName === "concat") {
107
+ continue;
108
+ }
109
+ }
110
+
111
+ const pd = Object.getOwnPropertyDescriptor(original.prototype, propertyName);
112
+
113
+ assert(pd !== undefined);
114
+
115
+ if (!pd.configurable) {
116
+ continue;
117
+ }
118
+
119
+ Object.defineProperty(original.prototype, propertyName, {
120
+ enumerable: pd.enumerable,
121
+ configurable: false,
122
+ ...("value" in pd
123
+ ? {
124
+ get: () => pd.value,
125
+ set: () => {
126
+ throw createWriteError(`window.${name}.prototype.${propertyName}`);
127
+ }
128
+ }
129
+ : {
130
+ get: pd.get,
131
+ set:
132
+ pd.set ??
133
+ (() => {
134
+ throw createWriteError(`window.${name}.prototype.${propertyName}`);
135
+ })
136
+ })
137
+ });
138
+ }
139
+ }
140
+
141
+ Object.defineProperty(window, name, {
142
+ configurable: false,
143
+ enumerable: true,
144
+ get: () => original,
145
+ set: () => {
146
+ throw createWriteError(`window.${name}`);
147
+ }
148
+ });
149
+ }
150
+
151
+ if (safeMode) {
152
+ for (const name of ["call", "apply", "bind"] as const) {
153
+ const original = Function.prototype[name];
154
+
155
+ Object.defineProperty(Function.prototype, name, {
156
+ configurable: false,
157
+ enumerable: true,
158
+ get: () => original,
159
+ set: () => {
160
+ throw createWriteError(`window.Function.prototype.${name})`);
161
+ }
162
+ });
163
+ }
164
+ }
165
+ }
@@ -0,0 +1,105 @@
1
+ import { assert } from "../tools/tsafe/assert";
2
+
3
+ let isTokenSubstitutionEnabled = false;
4
+
5
+ export function markTokenSubstitutionAdEnabled() {
6
+ isTokenSubstitutionEnabled = true;
7
+ }
8
+
9
+ export function getIsTokenSubstitutionEnabled() {
10
+ return isTokenSubstitutionEnabled;
11
+ }
12
+
13
+ type Tokens = {
14
+ accessToken: string;
15
+ idToken: string;
16
+ refreshToken?: string;
17
+ };
18
+
19
+ const entries: {
20
+ configId: string;
21
+ tokens: Tokens;
22
+ id: number;
23
+ }[] = [];
24
+
25
+ let counter = Math.floor(Math.random() * 1_000_000) + 1_000_000;
26
+
27
+ export function getTokensPlaceholders(params: { configId: string; tokens: Tokens }): Tokens {
28
+ const { configId, tokens } = params;
29
+
30
+ assert(isTokenSubstitutionEnabled, "2934482");
31
+
32
+ for (const entry of entries) {
33
+ if (entry.configId !== configId) {
34
+ continue;
35
+ }
36
+
37
+ setTimeout(() => {
38
+ const index = entries.indexOf(entry);
39
+
40
+ if (index === -1) {
41
+ return;
42
+ }
43
+
44
+ entries.splice(index, 1);
45
+ }, 30_000);
46
+ }
47
+
48
+ const id = counter++;
49
+
50
+ const entry_new: (typeof entries)[number] = {
51
+ id,
52
+ configId,
53
+ tokens: {
54
+ accessToken: tokens.accessToken,
55
+ idToken: tokens.idToken,
56
+ refreshToken: tokens.refreshToken
57
+ }
58
+ };
59
+
60
+ entries.push(entry_new);
61
+
62
+ return {
63
+ accessToken: `access_token_placeholder_${id}`,
64
+ idToken: `id_token_placeholder_${id}`,
65
+ refreshToken: tokens.refreshToken === undefined ? undefined : `refresh_token_placeholder_${id}`
66
+ };
67
+ }
68
+
69
+ export function substitutePlaceholderByRealToken(text: string): string {
70
+ let text_modified = text;
71
+
72
+ for (const [tokenType, regExp] of [
73
+ ["access_token", /access_token_placeholder_(\d+)/g],
74
+ ["id_token", /id_token_placeholder_(\d+)/g],
75
+ ["refresh_token", /refresh_token_placeholder_(\d+)/g]
76
+ ] as const) {
77
+ text_modified = text_modified.replace(regExp, (...[, p1]) => {
78
+ const id = parseInt(p1);
79
+
80
+ const entry = entries.find(e => e.id === id);
81
+
82
+ if (!entry) {
83
+ throw new Error(
84
+ [
85
+ "oidc-spa: Outdated token used to make a request.",
86
+ "Token should not be stored at the application level, when a token",
87
+ "is needed, it should be requested and used immediately."
88
+ ].join(" ")
89
+ );
90
+ }
91
+
92
+ switch (tokenType) {
93
+ case "access_token":
94
+ return entry.tokens.accessToken;
95
+ case "id_token":
96
+ return entry.tokens.idToken;
97
+ case "refresh_token":
98
+ assert(entry.tokens.refreshToken !== undefined, "204392284");
99
+ return entry.tokens.refreshToken;
100
+ }
101
+ });
102
+ }
103
+
104
+ return text_modified;
105
+ }
@@ -0,0 +1,18 @@
1
+ export function getIsDomain(hostname: string): boolean {
2
+ // Reject IPv4
3
+ if (/^\d+\.\d+\.\d+\.\d+$/.test(hostname)) {
4
+ return false;
5
+ }
6
+
7
+ // Reject IPv6
8
+ if (hostname.includes(":")) {
9
+ return false;
10
+ }
11
+
12
+ // Must contain at least one dot (e.g., "example.com")
13
+ if (!hostname.includes(".")) {
14
+ return false;
15
+ }
16
+
17
+ return true;
18
+ }
@@ -0,0 +1,91 @@
1
+ import { getIsLikelyDevServer } from "../tools/isLikelyDevServer";
2
+ import { getIsDomain } from "../tools/isDomain";
3
+
4
+ const MULTI_TENANT_DOMAINS = [
5
+ "vercel.app",
6
+ "netlify.app",
7
+ "github.io",
8
+ "pages.dev",
9
+ "web.app",
10
+ "firebaseapp.com",
11
+ "onrender.com",
12
+ "railway.app",
13
+ "fly.dev",
14
+ "herokuapp.com",
15
+ "amplifyapp.com",
16
+ "surge.sh",
17
+ "stackblitz.io",
18
+ "glitch.me",
19
+ "csb.app",
20
+ "codesandbox.io",
21
+ "repl.co",
22
+ "replit.dev",
23
+ "ondigitalocean.app",
24
+ "bubbleapps.io",
25
+ "wixsite.com",
26
+ "webflow.io",
27
+ "framer.app",
28
+ "deno.dev",
29
+ "azurestaticapps.net",
30
+ "run.app",
31
+ "cloudfront.net",
32
+ "qovery.io",
33
+ "northflank.app",
34
+ "cyclic.app",
35
+ "turso.io",
36
+ "koyeb.app",
37
+ "on.fleek.co",
38
+ "back4app.io"
39
+ ];
40
+
41
+ export function getIsHostnameAuthorized(params: {
42
+ allowedHostnames: string[];
43
+ hostname: string;
44
+ extendAuthorizationToParentDomain: boolean;
45
+ }) {
46
+ if (getIsLikelyDevServer()) {
47
+ return true;
48
+ }
49
+
50
+ const { hostname, allowedHostnames, extendAuthorizationToParentDomain } = params;
51
+
52
+ if (hostname === location.host) {
53
+ return true;
54
+ }
55
+
56
+ for (let allowedHost of allowedHostnames) {
57
+ allowedHost = allowedHost.toLocaleLowerCase();
58
+
59
+ if (allowedHost === hostname) {
60
+ return true;
61
+ }
62
+
63
+ if (allowedHost.startsWith("*") && hostname.endsWith(allowedHost.slice(1))) {
64
+ return true;
65
+ }
66
+ }
67
+
68
+ if (!extendAuthorizationToParentDomain) {
69
+ return false;
70
+ }
71
+
72
+ if (!getIsDomain(location.host) || !getIsDomain(hostname)) {
73
+ return false;
74
+ }
75
+
76
+ if (MULTI_TENANT_DOMAINS.find(suffix => location.host.endsWith(`.${suffix}`)) !== undefined) {
77
+ return false;
78
+ }
79
+
80
+ const trustedParentDomain = (() => {
81
+ const [s1, s2] = location.host.split(".").reverse();
82
+
83
+ return `${s2}.${s1}`;
84
+ })();
85
+
86
+ if (hostname.endsWith(`.${trustedParentDomain}`)) {
87
+ return true;
88
+ }
89
+
90
+ return false;
91
+ }
@@ -1,17 +1,29 @@
1
- export function getIsLikelyDevServer(): boolean {
2
- const origin = window.location.origin;
1
+ let isLikelyDevServer_cache: boolean | undefined = undefined;
3
2
 
4
- if (/^https?:\/\/localhost/.test(origin)) {
5
- return true;
3
+ export function getIsLikelyDevServer(): boolean {
4
+ if (isLikelyDevServer_cache !== undefined) {
5
+ return isLikelyDevServer_cache;
6
6
  }
7
7
 
8
- if (/^https?:\/\/\[::\]/.test(origin)) {
9
- return true;
10
- }
8
+ const isLikelyDevServer = (() => {
9
+ const origin = window.location.origin;
11
10
 
12
- if (/^https?:\/\/127.0.0.1/.test(origin)) {
13
- return true;
14
- }
11
+ if (/^https?:\/\/localhost/.test(origin)) {
12
+ return true;
13
+ }
14
+
15
+ if (/^https?:\/\/\[::\]/.test(origin)) {
16
+ return true;
17
+ }
18
+
19
+ if (/^https?:\/\/127.0.0.1/.test(origin)) {
20
+ return true;
21
+ }
22
+
23
+ return false;
24
+ })();
25
+
26
+ isLikelyDevServer_cache = isLikelyDevServer;
15
27
 
16
- return false;
28
+ return isLikelyDevServer;
17
29
  }
@@ -68,29 +68,66 @@ export function createHandleClientEntrypoint(params: {
68
68
 
69
69
  entryResolution.watchFiles.forEach(file => pluginContext.addWatchFile(file));
70
70
 
71
- const { freezeFetch, freezeXMLHttpRequest, freezeWebSocket, freezePromise, safeMode, ...rest } =
72
- oidcSpaVitePluginParams ?? {};
73
-
74
- assert<Equals<typeof rest, {}>>;
75
-
76
71
  return [
77
72
  `import { oidcEarlyInit } from "oidc-spa/entrypoint";`,
78
73
  `const { shouldLoadApp } = oidcEarlyInit({`,
79
- ...[
80
- ` freezeFetch: ${freezeFetch},`,
81
- ` freezeXMLHttpRequest: ${freezeXMLHttpRequest},`,
82
- ` freezeWebSocket: ${freezeWebSocket},`,
83
- ` freezePromise: ${freezePromise},`,
84
- ` safeMode: ${safeMode},`,
85
- ` BASE_URL: ${(() => {
86
- switch (projectType) {
87
- case "nuxt":
88
- return "__NUXT__.config.app.baseURL";
89
- default:
90
- return `"${resolvedConfig.base}"`;
91
- }
92
- })()}`
93
- ],
74
+ ...(() => {
75
+ if ("enableTokenExfiltrationDefense" in oidcSpaVitePluginParams) {
76
+ const {
77
+ enableTokenExfiltrationDefense,
78
+ serviceWorkersAllowedHostnames,
79
+ resourceServersAllowedHostnames,
80
+ ...rest
81
+ } = oidcSpaVitePluginParams ?? {};
82
+
83
+ assert<Equals<typeof rest, {}>>;
84
+
85
+ return [
86
+ ` enableTokenExfiltrationDefense: ${enableTokenExfiltrationDefense},`,
87
+ ` resourceServersAllowedHostnames: ${JSON.stringify(
88
+ resourceServersAllowedHostnames
89
+ )},`,
90
+ ` serviceWorkersAllowedHostnames: ${JSON.stringify(
91
+ serviceWorkersAllowedHostnames
92
+ )},`,
93
+ ` BASE_URL: ${(() => {
94
+ switch (projectType) {
95
+ case "nuxt":
96
+ return "__NUXT__.config.app.baseURL";
97
+ default:
98
+ return `"${resolvedConfig.base}"`;
99
+ }
100
+ })()}`
101
+ ];
102
+ }
103
+
104
+ const {
105
+ freezeFetch,
106
+ freezeXMLHttpRequest,
107
+ freezeWebSocket,
108
+ freezePromise,
109
+ safeMode,
110
+ ...rest
111
+ } = oidcSpaVitePluginParams ?? {};
112
+
113
+ assert<Equals<typeof rest, {}>>;
114
+
115
+ return [
116
+ ` freezeFetch: ${freezeFetch},`,
117
+ ` freezeXMLHttpRequest: ${freezeXMLHttpRequest},`,
118
+ ` freezeWebSocket: ${freezeWebSocket},`,
119
+ ` freezePromise: ${freezePromise},`,
120
+ ` safeMode: ${safeMode},`,
121
+ ` BASE_URL: ${(() => {
122
+ switch (projectType) {
123
+ case "nuxt":
124
+ return "__NUXT__.config.app.baseURL";
125
+ default:
126
+ return `"${resolvedConfig.base}"`;
127
+ }
128
+ })()}`
129
+ ];
130
+ })(),
94
131
  `});`,
95
132
  ``,
96
133
  `if (shouldLoadApp) {`,
@@ -1,22 +1,17 @@
1
1
  import type { Plugin, TransformResult } from "vite";
2
2
  import { assert } from "../tools/tsafe/assert";
3
- import type { Param0 } from "../tools/tsafe/Param0";
4
- import type { oidcEarlyInit } from "../entrypoint";
3
+ import type { ParamsOfEarlyInit, ParamsOfEarlyInit_legacy } from "../core/earlyInit";
5
4
  import { createHandleClientEntrypoint } from "./handleClientEntrypoint";
6
5
  import { createHandleServerEntrypoint } from "./handleServerEntrypoint";
7
6
  import { manageOptimizedDeps } from "./manageOptimizedDeps";
8
7
  import { transformCreateFileRoute } from "./transformTanstackRouterCreateFileRoute";
9
8
  import { getProjectType, type ProjectType } from "./projectType";
10
9
 
11
- export type OidcSpaVitePluginParams = Omit<Param0<typeof oidcEarlyInit>, "BASE_URL">;
10
+ export type OidcSpaVitePluginParams =
11
+ | Omit<ParamsOfEarlyInit, "BASE_URL">
12
+ | Omit<ParamsOfEarlyInit_legacy, "BASE_URL">;
12
13
 
13
- export function oidcSpa(
14
- params: OidcSpaVitePluginParams = {
15
- freezeFetch: true,
16
- freezeXMLHttpRequest: true,
17
- freezeWebSocket: true
18
- }
19
- ) {
14
+ export function oidcSpa(params: OidcSpaVitePluginParams) {
20
15
  let load_handleClientEntrypoint:
21
16
  | ReturnType<typeof createHandleClientEntrypoint>["load_handleClientEntrypoint"]
22
17
  | undefined = undefined;
@@ -0,0 +1 @@
1
+ export declare function getIsDomain(hostname: string): boolean;
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getIsDomain = getIsDomain;
4
+ function getIsDomain(hostname) {
5
+ // Reject IPv4
6
+ if (/^\d+\.\d+\.\d+\.\d+$/.test(hostname)) {
7
+ return false;
8
+ }
9
+ // Reject IPv6
10
+ if (hostname.includes(":")) {
11
+ return false;
12
+ }
13
+ // Must contain at least one dot (e.g., "example.com")
14
+ if (!hostname.includes(".")) {
15
+ return false;
16
+ }
17
+ return true;
18
+ }
19
+ //# sourceMappingURL=isDomain.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"isDomain.js","sourceRoot":"","sources":["../src/tools/isDomain.ts"],"names":[],"mappings":";;AAAA,kCAiBC;AAjBD,SAAgB,WAAW,CAAC,QAAgB;IACxC,cAAc;IACd,IAAI,sBAAsB,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QACxC,OAAO,KAAK,CAAC;IACjB,CAAC;IAED,cAAc;IACd,IAAI,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QACzB,OAAO,KAAK,CAAC;IACjB,CAAC;IAED,sDAAsD;IACtD,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QAC1B,OAAO,KAAK,CAAC;IACjB,CAAC;IAED,OAAO,IAAI,CAAC;AAChB,CAAC"}
@@ -0,0 +1,5 @@
1
+ export declare function getIsHostnameAuthorized(params: {
2
+ allowedHostnames: string[];
3
+ hostname: string;
4
+ extendAuthorizationToParentDomain: boolean;
5
+ }): boolean;
@@ -0,0 +1,77 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getIsHostnameAuthorized = getIsHostnameAuthorized;
4
+ const isLikelyDevServer_1 = require("../tools/isLikelyDevServer");
5
+ const isDomain_1 = require("../tools/isDomain");
6
+ const MULTI_TENANT_DOMAINS = [
7
+ "vercel.app",
8
+ "netlify.app",
9
+ "github.io",
10
+ "pages.dev",
11
+ "web.app",
12
+ "firebaseapp.com",
13
+ "onrender.com",
14
+ "railway.app",
15
+ "fly.dev",
16
+ "herokuapp.com",
17
+ "amplifyapp.com",
18
+ "surge.sh",
19
+ "stackblitz.io",
20
+ "glitch.me",
21
+ "csb.app",
22
+ "codesandbox.io",
23
+ "repl.co",
24
+ "replit.dev",
25
+ "ondigitalocean.app",
26
+ "bubbleapps.io",
27
+ "wixsite.com",
28
+ "webflow.io",
29
+ "framer.app",
30
+ "deno.dev",
31
+ "azurestaticapps.net",
32
+ "run.app",
33
+ "cloudfront.net",
34
+ "qovery.io",
35
+ "northflank.app",
36
+ "cyclic.app",
37
+ "turso.io",
38
+ "koyeb.app",
39
+ "on.fleek.co",
40
+ "back4app.io"
41
+ ];
42
+ function getIsHostnameAuthorized(params) {
43
+ if ((0, isLikelyDevServer_1.getIsLikelyDevServer)()) {
44
+ return true;
45
+ }
46
+ const { hostname, allowedHostnames, extendAuthorizationToParentDomain } = params;
47
+ if (hostname === location.host) {
48
+ return true;
49
+ }
50
+ for (let allowedHost of allowedHostnames) {
51
+ allowedHost = allowedHost.toLocaleLowerCase();
52
+ if (allowedHost === hostname) {
53
+ return true;
54
+ }
55
+ if (allowedHost.startsWith("*") && hostname.endsWith(allowedHost.slice(1))) {
56
+ return true;
57
+ }
58
+ }
59
+ if (!extendAuthorizationToParentDomain) {
60
+ return false;
61
+ }
62
+ if (!(0, isDomain_1.getIsDomain)(location.host) || !(0, isDomain_1.getIsDomain)(hostname)) {
63
+ return false;
64
+ }
65
+ if (MULTI_TENANT_DOMAINS.find(suffix => location.host.endsWith(`.${suffix}`)) !== undefined) {
66
+ return false;
67
+ }
68
+ const trustedParentDomain = (() => {
69
+ const [s1, s2] = location.host.split(".").reverse();
70
+ return `${s2}.${s1}`;
71
+ })();
72
+ if (hostname.endsWith(`.${trustedParentDomain}`)) {
73
+ return true;
74
+ }
75
+ return false;
76
+ }
77
+ //# sourceMappingURL=isHostnameAuthorized.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"isHostnameAuthorized.js","sourceRoot":"","sources":["../src/tools/isHostnameAuthorized.ts"],"names":[],"mappings":";;AAwCA,0DAkDC;AA1FD,kEAAkE;AAClE,gDAAgD;AAEhD,MAAM,oBAAoB,GAAG;IACzB,YAAY;IACZ,aAAa;IACb,WAAW;IACX,WAAW;IACX,SAAS;IACT,iBAAiB;IACjB,cAAc;IACd,aAAa;IACb,SAAS;IACT,eAAe;IACf,gBAAgB;IAChB,UAAU;IACV,eAAe;IACf,WAAW;IACX,SAAS;IACT,gBAAgB;IAChB,SAAS;IACT,YAAY;IACZ,oBAAoB;IACpB,eAAe;IACf,aAAa;IACb,YAAY;IACZ,YAAY;IACZ,UAAU;IACV,qBAAqB;IACrB,SAAS;IACT,gBAAgB;IAChB,WAAW;IACX,gBAAgB;IAChB,YAAY;IACZ,UAAU;IACV,WAAW;IACX,aAAa;IACb,aAAa;CAChB,CAAC;AAEF,SAAgB,uBAAuB,CAAC,MAIvC;IACG,IAAI,IAAA,wCAAoB,GAAE,EAAE,CAAC;QACzB,OAAO,IAAI,CAAC;IAChB,CAAC;IAED,MAAM,EAAE,QAAQ,EAAE,gBAAgB,EAAE,iCAAiC,EAAE,GAAG,MAAM,CAAC;IAEjF,IAAI,QAAQ,KAAK,QAAQ,CAAC,IAAI,EAAE,CAAC;QAC7B,OAAO,IAAI,CAAC;IAChB,CAAC;IAED,KAAK,IAAI,WAAW,IAAI,gBAAgB,EAAE,CAAC;QACvC,WAAW,GAAG,WAAW,CAAC,iBAAiB,EAAE,CAAC;QAE9C,IAAI,WAAW,KAAK,QAAQ,EAAE,CAAC;YAC3B,OAAO,IAAI,CAAC;QAChB,CAAC;QAED,IAAI,WAAW,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YACzE,OAAO,IAAI,CAAC;QAChB,CAAC;IACL,CAAC;IAED,IAAI,CAAC,iCAAiC,EAAE,CAAC;QACrC,OAAO,KAAK,CAAC;IACjB,CAAC;IAED,IAAI,CAAC,IAAA,sBAAW,EAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,IAAA,sBAAW,EAAC,QAAQ,CAAC,EAAE,CAAC;QACxD,OAAO,KAAK,CAAC;IACjB,CAAC;IAED,IAAI,oBAAoB,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,MAAM,EAAE,CAAC,CAAC,KAAK,SAAS,EAAE,CAAC;QAC1F,OAAO,KAAK,CAAC;IACjB,CAAC;IAED,MAAM,mBAAmB,GAAG,CAAC,GAAG,EAAE;QAC9B,MAAM,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC;QAEpD,OAAO,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC;IACzB,CAAC,CAAC,EAAE,CAAC;IAEL,IAAI,QAAQ,CAAC,QAAQ,CAAC,IAAI,mBAAmB,EAAE,CAAC,EAAE,CAAC;QAC/C,OAAO,IAAI,CAAC;IAChB,CAAC;IAED,OAAO,KAAK,CAAC;AACjB,CAAC"}