@osdk/oauth 1.1.0-beta.1 → 1.1.0-beta.10

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/CHANGELOG.md +59 -0
  2. package/build/browser/AuthorizationServer.test.js +53 -0
  3. package/build/browser/AuthorizationServer.test.js.map +1 -0
  4. package/build/browser/BaseOauthClient.js +0 -15
  5. package/build/browser/BaseOauthClient.js.map +1 -1
  6. package/build/browser/ConfidentialOauthClient.js +0 -15
  7. package/build/browser/ConfidentialOauthClient.js.map +1 -1
  8. package/build/browser/PublicOauthClient.js +0 -15
  9. package/build/browser/PublicOauthClient.js.map +1 -1
  10. package/build/browser/Token.js +0 -15
  11. package/build/browser/Token.js.map +1 -1
  12. package/build/browser/__tests__/createPublicOauthClient.test.js +59 -0
  13. package/build/browser/__tests__/createPublicOauthClient.test.js.map +1 -0
  14. package/build/browser/common.js +25 -7
  15. package/build/browser/common.js.map +1 -1
  16. package/build/browser/common.test.js +65 -0
  17. package/build/browser/common.test.js.map +1 -0
  18. package/build/browser/createConfidentialOauthClient.js +4 -2
  19. package/build/browser/createConfidentialOauthClient.js.map +1 -1
  20. package/build/browser/createPublicOauthClient.js +74 -36
  21. package/build/browser/createPublicOauthClient.js.map +1 -1
  22. package/build/browser/index.js +1 -0
  23. package/build/browser/index.js.map +1 -1
  24. package/build/browser/publicOauth.test.js +318 -0
  25. package/build/browser/publicOauth.test.js.map +1 -0
  26. package/build/browser/throwIfError.js +1 -0
  27. package/build/browser/throwIfError.js.map +1 -1
  28. package/build/browser/utils.js +47 -0
  29. package/build/browser/utils.js.map +1 -0
  30. package/build/cjs/index.cjs +320 -0
  31. package/build/cjs/index.cjs.map +1 -0
  32. package/build/cjs/index.d.cts +96 -0
  33. package/build/esm/AuthorizationServer.test.js +53 -0
  34. package/build/esm/AuthorizationServer.test.js.map +1 -0
  35. package/build/esm/BaseOauthClient.js +0 -15
  36. package/build/esm/BaseOauthClient.js.map +1 -1
  37. package/build/esm/ConfidentialOauthClient.js +0 -15
  38. package/build/esm/ConfidentialOauthClient.js.map +1 -1
  39. package/build/esm/PublicOauthClient.js +0 -15
  40. package/build/esm/PublicOauthClient.js.map +1 -1
  41. package/build/esm/Token.js +0 -15
  42. package/build/esm/Token.js.map +1 -1
  43. package/build/esm/__tests__/createPublicOauthClient.test.js +59 -0
  44. package/build/esm/__tests__/createPublicOauthClient.test.js.map +1 -0
  45. package/build/esm/common.js +25 -7
  46. package/build/esm/common.js.map +1 -1
  47. package/build/esm/common.test.js +65 -0
  48. package/build/esm/common.test.js.map +1 -0
  49. package/build/esm/createConfidentialOauthClient.js +4 -2
  50. package/build/esm/createConfidentialOauthClient.js.map +1 -1
  51. package/build/esm/createPublicOauthClient.js +74 -36
  52. package/build/esm/createPublicOauthClient.js.map +1 -1
  53. package/build/esm/index.js +1 -0
  54. package/build/esm/index.js.map +1 -1
  55. package/build/esm/publicOauth.test.js +318 -0
  56. package/build/esm/publicOauth.test.js.map +1 -0
  57. package/build/esm/throwIfError.js +1 -0
  58. package/build/esm/throwIfError.js.map +1 -1
  59. package/build/esm/utils.js +47 -0
  60. package/build/esm/utils.js.map +1 -0
  61. package/build/types/AuthorizationServer.test.d.ts +1 -0
  62. package/build/types/BaseOauthClient.d.ts +14 -0
  63. package/build/{esm → types}/ConfidentialOauthClient.d.ts +1 -3
  64. package/build/{browser → types}/PublicOauthClient.d.ts +1 -2
  65. package/build/types/Token.d.ts +6 -0
  66. package/build/types/__tests__/createPublicOauthClient.test.d.ts +1 -0
  67. package/build/{browser → types}/common.d.ts +20 -25
  68. package/build/types/common.test.d.ts +1 -0
  69. package/build/{browser → types}/createConfidentialOauthClient.d.ts +8 -9
  70. package/build/types/createPublicOauthClient.d.ts +56 -0
  71. package/build/{browser → types}/index.d.ts +1 -2
  72. package/build/types/publicOauth.test.d.ts +1 -0
  73. package/build/{browser → types}/throwIfError.d.ts +0 -1
  74. package/build/types/utils.d.ts +7 -0
  75. package/package.json +23 -9
  76. package/build/browser/BaseOauthClient.d.ts +0 -15
  77. package/build/browser/BaseOauthClient.d.ts.map +0 -1
  78. package/build/browser/ConfidentialOauthClient.d.ts +0 -4
  79. package/build/browser/ConfidentialOauthClient.d.ts.map +0 -1
  80. package/build/browser/PublicOauthClient.d.ts.map +0 -1
  81. package/build/browser/Token.d.ts +0 -7
  82. package/build/browser/Token.d.ts.map +0 -1
  83. package/build/browser/common.d.ts.map +0 -1
  84. package/build/browser/createConfidentialOauthClient.d.ts.map +0 -1
  85. package/build/browser/createPublicOauthClient.d.ts +0 -15
  86. package/build/browser/createPublicOauthClient.d.ts.map +0 -1
  87. package/build/browser/index.d.ts.map +0 -1
  88. package/build/browser/throwIfError.d.ts.map +0 -1
  89. package/build/esm/BaseOauthClient.d.ts +0 -15
  90. package/build/esm/BaseOauthClient.d.ts.map +0 -1
  91. package/build/esm/ConfidentialOauthClient.d.ts.map +0 -1
  92. package/build/esm/PublicOauthClient.d.ts +0 -6
  93. package/build/esm/PublicOauthClient.d.ts.map +0 -1
  94. package/build/esm/Token.d.ts +0 -7
  95. package/build/esm/Token.d.ts.map +0 -1
  96. package/build/esm/common.d.ts +0 -36
  97. package/build/esm/common.d.ts.map +0 -1
  98. package/build/esm/createConfidentialOauthClient.d.ts +0 -12
  99. package/build/esm/createConfidentialOauthClient.d.ts.map +0 -1
  100. package/build/esm/createPublicOauthClient.d.ts +0 -15
  101. package/build/esm/createPublicOauthClient.d.ts.map +0 -1
  102. package/build/esm/index.d.ts +0 -4
  103. package/build/esm/index.d.ts.map +0 -1
  104. package/build/esm/throwIfError.d.ts +0 -6
  105. package/build/esm/throwIfError.d.ts.map +0 -1
@@ -13,23 +13,49 @@
13
13
  * See the License for the specific language governing permissions and
14
14
  * limitations under the License.
15
15
  */
16
+
16
17
  import delay from "delay";
17
18
  import { authorizationCodeGrantRequest, calculatePKCECodeChallenge, customFetch, generateRandomCodeVerifier, generateRandomState, processAuthorizationCodeOAuth2Response, refreshTokenGrantRequest, validateAuthResponse } from "oauth4webapi";
18
- import { common, createAuthorizationServer, readLocal, removeLocal, saveLocal } from "./common.js";
19
+ import { common, createAuthorizationServer, readLocal, readSession, removeLocal, removeSession, saveLocal, saveSession } from "./common.js";
19
20
  import { throwIfError } from "./throwIfError.js";
21
+ import { processOptionsAndAssignDefaults } from "./utils.js";
22
+
20
23
  /**
21
- * @param client_id
22
- * @param url the base url of your foundry server
23
- * @param redirectUrl the url configured for redirect in the oauth configuration on the server
24
- * @param useHistory if true, when possible uses `history.replaceState()`, otherwise uses `window.location.assign()`
25
- * @param loginPage if you want a custom landing page prior to logging in, set this to that url
26
- * @param postLoginPage if you want to customize where you return to after a completed authentication cycle. defaults to the url when the page loaded
27
- * @param scopes
28
- * @param fetchFn
29
- * @param ctxPath
30
- * @returns {PublicOauthClient} which can be used as a token provider
24
+ * Creates a PublicOauthClient for authentication.
25
+ *
26
+ * @param {string} clientId - The client_id from the OAuth configuration on the server
27
+ * @param {string} url - The base URL of your Foundry server
28
+ * @param {string} redirectUrl - The URL configured for redirect in the OAuth configuration on the server
29
+ * @param {PublicOauthClientOptions} options - Additional options for the client
30
+ * @returns {PublicOauthClient} A client that can be used as a token provider
31
+ */
32
+
33
+ /**
34
+ * Creates a PublicOauthClient for authentication.
35
+ *
36
+ * @param {string} clientId - The client_id from the OAuth configuration on the server
37
+ * @param {string} url - The base URL of your Foundry server
38
+ * @param {string} redirectUrl - The URL configured for redirect in the OAuth configuration on the server
39
+ * @param {boolean} useHistory - If true, uses `history.replaceState()`, otherwise uses `window.location.assign()` (defaults to true)
40
+ * @param {string} loginPage - Custom landing page URL prior to logging in
41
+ * @param {string} postLoginPage - URL to return to after completed authentication cycle (defaults to `window.location.toString()`)
42
+ * @param {string[]} scopes - OAuth scopes to request. If not provided, defaults to `["api:read-data", "api:write-data"]`
43
+ * @param {typeof globalThis.fetch} fetchFn - Custom fetch function to use for requests (defaults to `globalThis.fetch`)
44
+ * @param {string} ctxPath - Context path for the authorization server (defaults to "multipass")
45
+ * @returns {PublicOauthClient} A client that can be used as a token provider
31
46
  */
32
- export function createPublicOauthClient(client_id, url, redirectUrl, useHistory = true, loginPage, postLoginPage = window.location.toString(), scopes = [], fetchFn = globalThis.fetch, ctxPath = "/multipass") {
47
+
48
+ export function createPublicOauthClient(client_id, url, redirect_uri, useHistory, loginPage, postLoginPage, scopes, fetchFn, ctxPath) {
49
+ let refreshTokenMarker;
50
+ ({
51
+ useHistory,
52
+ loginPage,
53
+ postLoginPage,
54
+ scopes,
55
+ fetchFn,
56
+ ctxPath,
57
+ refreshTokenMarker
58
+ } = processOptionsAndAssignDefaults(url, redirect_uri, useHistory, loginPage, postLoginPage, scopes, fetchFn, ctxPath));
33
59
  const client = {
34
60
  client_id,
35
61
  token_endpoint_auth_method: "none"
@@ -38,9 +64,6 @@ export function createPublicOauthClient(client_id, url, redirectUrl, useHistory
38
64
  const oauthHttpOptions = {
39
65
  [customFetch]: fetchFn
40
66
  };
41
- if (scopes.length === 0) {
42
- scopes = ["api:read-data", "api:write-data"];
43
- }
44
67
  const {
45
68
  makeTokenAndSaveRefresh,
46
69
  getToken
@@ -52,17 +75,23 @@ export function createPublicOauthClient(client_id, url, redirectUrl, useHistory
52
75
  ) ?? (await maybeHandleAuthReturn())
53
76
  // 3. If we haven't been able to load the token from one of the two above ways, we need to make the initial auth request
54
77
  ?? (await initiateLoginRedirect());
55
- }, oauthHttpOptions, maybeRefresh.bind(globalThis, true));
56
- async function go(x) {
57
- if (useHistory) return window.history.replaceState({}, "", x);else window.location.assign(x);
78
+ }, oauthHttpOptions, maybeRefresh.bind(globalThis, true), refreshTokenMarker);
79
+
80
+ // as an arrow function, `useHistory` is known to be a boolean
81
+ const go = async x => {
82
+ if (useHistory) {
83
+ window.history.replaceState({}, "", x);
84
+ return;
85
+ } else window.location.assign(x);
58
86
  await delay(1000);
59
87
  throw new Error("Unable to redirect");
60
- }
88
+ };
61
89
  async function maybeRefresh(expectRefreshToken) {
62
90
  const {
63
- refresh_token
91
+ refresh_token,
92
+ refreshTokenMarker: lastRefreshTokenMarker
64
93
  } = readLocal(client);
65
- if (!refresh_token) {
94
+ if (!refresh_token || lastRefreshTokenMarker !== refreshTokenMarker) {
66
95
  if (expectRefreshToken) throw new Error("No refresh token found");
67
96
  return;
68
97
  }
@@ -70,11 +99,12 @@ export function createPublicOauthClient(client_id, url, redirectUrl, useHistory
70
99
  // note, we don't use processRefreshTokenResponse because the mp oauth implementation
71
100
  // doesn't do refresh tokens quite right and this lets us skip the id check
72
101
  const result = makeTokenAndSaveRefresh(throwIfError(await processAuthorizationCodeOAuth2Response(authServer, client, await refreshTokenGrantRequest(authServer, client, refresh_token, oauthHttpOptions))), "refresh");
73
- if (result && window.location.pathname === new URL(redirectUrl).pathname) {
102
+ if (result && window.location.pathname === new URL(redirect_uri).pathname) {
74
103
  const {
75
104
  oldUrl
76
- } = readLocal(client);
77
- go(oldUrl ?? "/");
105
+ } = readSession(client);
106
+ // don't block on the redirect
107
+ void go(oldUrl ?? "/");
78
108
  }
79
109
  return result;
80
110
  } catch (e) {
@@ -90,14 +120,14 @@ export function createPublicOauthClient(client_id, url, redirectUrl, useHistory
90
120
  }
91
121
  async function maybeHandleAuthReturn() {
92
122
  const {
93
- codeVerifier,
94
123
  state,
95
- oldUrl
96
- } = readLocal(client);
124
+ oldUrl,
125
+ codeVerifier
126
+ } = readSession(client);
97
127
  if (!codeVerifier) return;
98
128
  try {
99
- const ret = makeTokenAndSaveRefresh(throwIfError(await processAuthorizationCodeOAuth2Response(authServer, client, await authorizationCodeGrantRequest(authServer, client, throwIfError(validateAuthResponse(authServer, client, new URL(window.location.href), state)), redirectUrl, codeVerifier, oauthHttpOptions))), "signIn");
100
- go(oldUrl);
129
+ const ret = makeTokenAndSaveRefresh(throwIfError(await processAuthorizationCodeOAuth2Response(authServer, client, await authorizationCodeGrantRequest(authServer, client, throwIfError(validateAuthResponse(authServer, client, new URL(window.location.href), state)), redirect_uri, codeVerifier, oauthHttpOptions))), "signIn");
130
+ void go(oldUrl);
101
131
  return ret;
102
132
  } catch (e) {
103
133
  if (process.env.NODE_ENV !== "production") {
@@ -105,19 +135,26 @@ export function createPublicOauthClient(client_id, url, redirectUrl, useHistory
105
135
  console.warn("Failed to get OAuth2 token using PKCE, removing PKCE and starting a new auth flow", e);
106
136
  }
107
137
  removeLocal(client);
138
+ removeSession(client);
139
+ throw e;
108
140
  }
109
141
  }
110
- async function initiateLoginRedirect() {
142
+
143
+ // As an arrow function, `scopes` and `postLoginPage` are known at compile time
144
+ const initiateLoginRedirect = async () => {
111
145
  if (loginPage && window.location.href !== loginPage && window.location.pathname !== loginPage) {
112
- saveLocal(client, {
146
+ saveLocal(client, {});
147
+ saveSession(client, {
113
148
  oldUrl: postLoginPage
114
149
  });
115
- return await go(loginPage);
150
+ await go(loginPage);
151
+ return;
116
152
  }
117
153
  const state = generateRandomState();
118
154
  const codeVerifier = generateRandomCodeVerifier();
119
- const oldUrl = readLocal(client).oldUrl ?? window.location.toString();
120
- saveLocal(client, {
155
+ const oldUrl = readSession(client).oldUrl ?? window.location.toString();
156
+ saveLocal(client, {});
157
+ saveSession(client, {
121
158
  codeVerifier,
122
159
  state,
123
160
  oldUrl
@@ -126,15 +163,16 @@ export function createPublicOauthClient(client_id, url, redirectUrl, useHistory
126
163
  client_id,
127
164
  response_type: "code",
128
165
  state,
129
- redirect_uri: redirectUrl,
166
+ redirect_uri,
130
167
  code_challenge: await calculatePKCECodeChallenge(codeVerifier),
131
168
  code_challenge_method: "S256",
132
169
  scope: ["offline_access", ...scopes].join(" ")
133
170
  })}`);
171
+
134
172
  // Give time for redirect to happen
135
173
  await delay(1000);
136
174
  throw new Error("Unable to redirect");
137
- }
175
+ };
138
176
  return getToken;
139
177
  }
140
178
  //# sourceMappingURL=createPublicOauthClient.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"createPublicOauthClient.js","names":["delay","authorizationCodeGrantRequest","calculatePKCECodeChallenge","customFetch","generateRandomCodeVerifier","generateRandomState","processAuthorizationCodeOAuth2Response","refreshTokenGrantRequest","validateAuthResponse","common","createAuthorizationServer","readLocal","removeLocal","saveLocal","throwIfError","createPublicOauthClient","client_id","url","redirectUrl","useHistory","loginPage","postLoginPage","window","location","toString","scopes","fetchFn","globalThis","fetch","ctxPath","client","token_endpoint_auth_method","authServer","oauthHttpOptions","length","makeTokenAndSaveRefresh","getToken","_signIn","maybeRefresh","maybeHandleAuthReturn","initiateLoginRedirect","bind","go","x","history","replaceState","assign","Error","expectRefreshToken","refresh_token","result","pathname","URL","oldUrl","e","process","env","NODE_ENV","console","warn","codeVerifier","state","ret","href","authorization_endpoint","URLSearchParams","response_type","redirect_uri","code_challenge","code_challenge_method","scope","join"],"sources":["createPublicOauthClient.js"],"sourcesContent":["/*\n * Copyright 2024 Palantir Technologies, Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport delay from \"delay\";\nimport { authorizationCodeGrantRequest, calculatePKCECodeChallenge, customFetch, generateRandomCodeVerifier, generateRandomState, processAuthorizationCodeOAuth2Response, refreshTokenGrantRequest, validateAuthResponse, } from \"oauth4webapi\";\nimport { common, createAuthorizationServer, readLocal, removeLocal, saveLocal, } from \"./common.js\";\nimport { throwIfError } from \"./throwIfError.js\";\n/**\n * @param client_id\n * @param url the base url of your foundry server\n * @param redirectUrl the url configured for redirect in the oauth configuration on the server\n * @param useHistory if true, when possible uses `history.replaceState()`, otherwise uses `window.location.assign()`\n * @param loginPage if you want a custom landing page prior to logging in, set this to that url\n * @param postLoginPage if you want to customize where you return to after a completed authentication cycle. defaults to the url when the page loaded\n * @param scopes\n * @param fetchFn\n * @param ctxPath\n * @returns {PublicOauthClient} which can be used as a token provider\n */\nexport function createPublicOauthClient(client_id, url, redirectUrl, useHistory = true, loginPage, postLoginPage = window.location.toString(), scopes = [], fetchFn = globalThis.fetch, ctxPath = \"/multipass\") {\n const client = { client_id, token_endpoint_auth_method: \"none\" };\n const authServer = createAuthorizationServer(ctxPath, url);\n const oauthHttpOptions = { [customFetch]: fetchFn };\n if (scopes.length === 0) {\n scopes = [\"api:read-data\", \"api:write-data\"];\n }\n const { makeTokenAndSaveRefresh, getToken } = common(client, authServer, _signIn, oauthHttpOptions, maybeRefresh.bind(globalThis, true));\n async function go(x) {\n if (useHistory)\n return window.history.replaceState({}, \"\", x);\n else\n window.location.assign(x);\n await delay(1000);\n throw new Error(\"Unable to redirect\");\n }\n async function maybeRefresh(expectRefreshToken) {\n const { refresh_token } = readLocal(client);\n if (!refresh_token) {\n if (expectRefreshToken)\n throw new Error(\"No refresh token found\");\n return;\n }\n try {\n // note, we don't use processRefreshTokenResponse because the mp oauth implementation\n // doesn't do refresh tokens quite right and this lets us skip the id check\n const result = makeTokenAndSaveRefresh(throwIfError(await processAuthorizationCodeOAuth2Response(authServer, client, await refreshTokenGrantRequest(authServer, client, refresh_token, oauthHttpOptions))), \"refresh\");\n if (result && window.location.pathname === new URL(redirectUrl).pathname) {\n const { oldUrl } = readLocal(client);\n go(oldUrl ?? \"/\");\n }\n return result;\n }\n catch (e) {\n if (process.env.NODE_ENV !== \"production\") {\n // eslint-disable-next-line no-console\n console.warn(\"Failed to get OAuth2 refresh token. Removing refresh token\", e);\n }\n removeLocal(client);\n if (expectRefreshToken) {\n throw new Error(\"Could not refresh token\");\n }\n }\n }\n async function maybeHandleAuthReturn() {\n const { codeVerifier, state, oldUrl } = readLocal(client);\n if (!codeVerifier)\n return;\n try {\n const ret = makeTokenAndSaveRefresh(throwIfError(await processAuthorizationCodeOAuth2Response(authServer, client, await authorizationCodeGrantRequest(authServer, client, throwIfError(validateAuthResponse(authServer, client, new URL(window.location.href), state)), redirectUrl, codeVerifier, oauthHttpOptions))), \"signIn\");\n go(oldUrl);\n return ret;\n }\n catch (e) {\n if (process.env.NODE_ENV !== \"production\") {\n // eslint-disable-next-line no-console\n console.warn(\"Failed to get OAuth2 token using PKCE, removing PKCE and starting a new auth flow\", e);\n }\n removeLocal(client);\n }\n }\n async function initiateLoginRedirect() {\n if (loginPage\n && window.location.href !== loginPage\n && window.location.pathname !== loginPage) {\n saveLocal(client, { oldUrl: postLoginPage });\n return await go(loginPage);\n }\n const state = generateRandomState();\n const codeVerifier = generateRandomCodeVerifier();\n const oldUrl = readLocal(client).oldUrl ?? window.location.toString();\n saveLocal(client, { codeVerifier, state, oldUrl });\n window.location.assign(`${authServer\n .authorization_endpoint}?${new URLSearchParams({\n client_id,\n response_type: \"code\",\n state,\n redirect_uri: redirectUrl,\n code_challenge: await calculatePKCECodeChallenge(codeVerifier),\n code_challenge_method: \"S256\",\n scope: [\"offline_access\", ...scopes].join(\" \"),\n })}`);\n // Give time for redirect to happen\n await delay(1000);\n throw new Error(\"Unable to redirect\");\n }\n /** Will throw if there is no token! */\n async function _signIn() {\n // 1. Check if we have a refresh token in local storage\n return await maybeRefresh()\n // 2. If there is no refresh token we are likely trying to perform the callback\n ?? await maybeHandleAuthReturn()\n // 3. If we haven't been able to load the token from one of the two above ways, we need to make the initial auth request\n ?? await initiateLoginRedirect();\n }\n return getToken;\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"createPublicOauthClient.js","names":["delay","authorizationCodeGrantRequest","calculatePKCECodeChallenge","customFetch","generateRandomCodeVerifier","generateRandomState","processAuthorizationCodeOAuth2Response","refreshTokenGrantRequest","validateAuthResponse","common","createAuthorizationServer","readLocal","readSession","removeLocal","removeSession","saveLocal","saveSession","throwIfError","processOptionsAndAssignDefaults","createPublicOauthClient","client_id","url","redirect_uri","useHistory","loginPage","postLoginPage","scopes","fetchFn","ctxPath","refreshTokenMarker","client","token_endpoint_auth_method","authServer","oauthHttpOptions","makeTokenAndSaveRefresh","getToken","_signIn","maybeRefresh","maybeHandleAuthReturn","initiateLoginRedirect","bind","globalThis","go","x","window","history","replaceState","location","assign","Error","expectRefreshToken","refresh_token","lastRefreshTokenMarker","result","pathname","URL","oldUrl","e","process","env","NODE_ENV","console","warn","state","codeVerifier","ret","href","toString","authorization_endpoint","URLSearchParams","response_type","code_challenge","code_challenge_method","scope","join"],"sources":["createPublicOauthClient.ts"],"sourcesContent":["/*\n * Copyright 2024 Palantir Technologies, Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport delay from \"delay\";\nimport type { Client, HttpRequestOptions } from \"oauth4webapi\";\nimport {\n authorizationCodeGrantRequest,\n calculatePKCECodeChallenge,\n customFetch,\n generateRandomCodeVerifier,\n generateRandomState,\n processAuthorizationCodeOAuth2Response,\n refreshTokenGrantRequest,\n validateAuthResponse,\n} from \"oauth4webapi\";\nimport {\n common,\n createAuthorizationServer,\n readLocal,\n readSession,\n removeLocal,\n removeSession,\n saveLocal,\n saveSession,\n} from \"./common.js\";\nimport type { PublicOauthClient } from \"./PublicOauthClient.js\";\nimport { throwIfError } from \"./throwIfError.js\";\nimport type { Token } from \"./Token.js\";\nimport { processOptionsAndAssignDefaults } from \"./utils.js\";\n\ndeclare const process: {\n env: {\n NODE_ENV: \"production\" | \"development\";\n TARGET: \"browser\" | \"node\";\n };\n};\n\nexport interface PublicOauthClientOptions {\n /**\n * If true, uses `history.replaceState()`, otherwise uses `window.location.assign()` (defaults to true)\n */\n useHistory?: boolean;\n\n /**\n * Custom landing page URL prior to logging in\n */\n loginPage?: string;\n\n /**\n * URL to return to after completed authentication cycle (defaults to `window.location.toString()`)\n */\n postLoginPage?: string;\n\n /**\n * * @param {string[]} [scopes=[]] - OAuth scopes to request. If not provided, defaults to `[\"api:read-data\", \"api:write-data\"]`\n */\n scopes?: string[];\n\n /**\n * Custom fetch function to use for requests (defaults to `globalThis.fetch`)\n */\n fetchFn?: typeof globalThis.fetch;\n\n /**\n * Context path for the authorization server (defaults to \"multipass\")\n */\n ctxPath?: string;\n\n /**\n * Allows for an additional value to be appended to the local storage key for the refresh token.\n */\n refreshTokenMarker?: string;\n}\n\n/**\n * Creates a PublicOauthClient for authentication.\n *\n * @param {string} clientId - The client_id from the OAuth configuration on the server\n * @param {string} url - The base URL of your Foundry server\n * @param {string} redirectUrl - The URL configured for redirect in the OAuth configuration on the server\n * @param {PublicOauthClientOptions} options - Additional options for the client\n * @returns {PublicOauthClient} A client that can be used as a token provider\n */\nexport function createPublicOauthClient(\n clientId: string,\n url: string,\n redirectUrl: string,\n options?: PublicOauthClientOptions,\n): PublicOauthClient;\n\n/**\n * Creates a PublicOauthClient for authentication.\n *\n * @param {string} clientId - The client_id from the OAuth configuration on the server\n * @param {string} url - The base URL of your Foundry server\n * @param {string} redirectUrl - The URL configured for redirect in the OAuth configuration on the server\n * @param {boolean} useHistory - If true, uses `history.replaceState()`, otherwise uses `window.location.assign()` (defaults to true)\n * @param {string} loginPage - Custom landing page URL prior to logging in\n * @param {string} postLoginPage - URL to return to after completed authentication cycle (defaults to `window.location.toString()`)\n * @param {string[]} scopes - OAuth scopes to request. If not provided, defaults to `[\"api:read-data\", \"api:write-data\"]`\n * @param {typeof globalThis.fetch} fetchFn - Custom fetch function to use for requests (defaults to `globalThis.fetch`)\n * @param {string} ctxPath - Context path for the authorization server (defaults to \"multipass\")\n * @returns {PublicOauthClient} A client that can be used as a token provider\n */\nexport function createPublicOauthClient(\n client_id: string,\n url: string,\n redirectUrl: string,\n useHistory?: boolean,\n loginPage?: string,\n postLoginPage?: string,\n scopes?: string[],\n fetchFn?: typeof globalThis.fetch,\n ctxPath?: string,\n): PublicOauthClient;\nexport function createPublicOauthClient(\n client_id: string,\n url: string,\n redirect_uri: string,\n useHistory?: boolean | PublicOauthClientOptions,\n loginPage?: string,\n postLoginPage?: string,\n scopes?: string[],\n fetchFn?: typeof globalThis.fetch,\n ctxPath?: string,\n): PublicOauthClient {\n let refreshTokenMarker: string | undefined;\n ({\n useHistory,\n loginPage,\n postLoginPage,\n scopes,\n fetchFn,\n ctxPath,\n refreshTokenMarker,\n } = processOptionsAndAssignDefaults(\n url,\n redirect_uri,\n useHistory,\n loginPage,\n postLoginPage,\n scopes,\n fetchFn,\n ctxPath,\n ));\n\n const client: Client = {\n client_id,\n token_endpoint_auth_method: \"none\",\n };\n const authServer = createAuthorizationServer(ctxPath, url);\n const oauthHttpOptions: HttpRequestOptions = { [customFetch]: fetchFn };\n\n const { makeTokenAndSaveRefresh, getToken } = common(\n client,\n authServer,\n _signIn,\n oauthHttpOptions,\n maybeRefresh.bind(globalThis, true),\n refreshTokenMarker,\n );\n\n // as an arrow function, `useHistory` is known to be a boolean\n const go = async (x: string) => {\n if (useHistory) {\n window.history.replaceState({}, \"\", x);\n return;\n } else window.location.assign(x);\n\n await delay(1000);\n throw new Error(\"Unable to redirect\");\n };\n\n async function maybeRefresh(\n expectRefreshToken?: boolean,\n ): Promise<Token | undefined> {\n const { refresh_token, refreshTokenMarker: lastRefreshTokenMarker } =\n readLocal(client);\n\n if (!refresh_token || lastRefreshTokenMarker !== refreshTokenMarker) {\n if (expectRefreshToken) throw new Error(\"No refresh token found\");\n return;\n }\n\n try {\n // note, we don't use processRefreshTokenResponse because the mp oauth implementation\n // doesn't do refresh tokens quite right and this lets us skip the id check\n const result = makeTokenAndSaveRefresh(\n throwIfError(\n await processAuthorizationCodeOAuth2Response(\n authServer,\n client,\n await refreshTokenGrantRequest(\n authServer,\n client,\n refresh_token,\n oauthHttpOptions,\n ),\n ),\n ),\n \"refresh\",\n );\n\n if (\n result && window.location.pathname === new URL(redirect_uri).pathname\n ) {\n const { oldUrl } = readSession(client);\n // don't block on the redirect\n void go(oldUrl ?? \"/\");\n }\n return result;\n } catch (e) {\n if (process.env.NODE_ENV !== \"production\") {\n // eslint-disable-next-line no-console\n console.warn(\n \"Failed to get OAuth2 refresh token. Removing refresh token\",\n e,\n );\n }\n removeLocal(client);\n if (expectRefreshToken) {\n throw new Error(\"Could not refresh token\");\n }\n }\n }\n\n async function maybeHandleAuthReturn() {\n const { state, oldUrl, codeVerifier } = readSession(client);\n if (!codeVerifier) return;\n\n try {\n const ret = makeTokenAndSaveRefresh(\n throwIfError(\n await processAuthorizationCodeOAuth2Response(\n authServer,\n client,\n await authorizationCodeGrantRequest(\n authServer,\n client,\n throwIfError(\n validateAuthResponse(\n authServer,\n client,\n new URL(window.location.href),\n state,\n ),\n ),\n redirect_uri,\n codeVerifier,\n oauthHttpOptions,\n ),\n ),\n ),\n \"signIn\",\n );\n\n void go(oldUrl);\n return ret;\n } catch (e) {\n if (process.env.NODE_ENV !== \"production\") {\n // eslint-disable-next-line no-console\n console.warn(\n \"Failed to get OAuth2 token using PKCE, removing PKCE and starting a new auth flow\",\n e,\n );\n }\n removeLocal(client);\n removeSession(client);\n throw e;\n }\n }\n\n // As an arrow function, `scopes` and `postLoginPage` are known at compile time\n const initiateLoginRedirect = async (): Promise<void> => {\n if (\n loginPage\n && window.location.href !== loginPage\n && window.location.pathname !== loginPage\n ) {\n saveLocal(client, {});\n saveSession(client, { oldUrl: postLoginPage });\n await go(loginPage);\n return;\n }\n\n const state = generateRandomState()!;\n const codeVerifier = generateRandomCodeVerifier();\n const oldUrl = readSession(client).oldUrl ?? window.location.toString();\n saveLocal(client, {});\n saveSession(client, { codeVerifier, state, oldUrl });\n\n window.location.assign(`${authServer\n .authorization_endpoint!}?${new URLSearchParams({\n client_id,\n response_type: \"code\",\n state,\n redirect_uri,\n code_challenge: await calculatePKCECodeChallenge(codeVerifier),\n code_challenge_method: \"S256\",\n scope: [\"offline_access\", ...scopes].join(\" \"),\n })}`);\n\n // Give time for redirect to happen\n await delay(1000);\n throw new Error(\"Unable to redirect\");\n };\n\n /** Will throw if there is no token! */\n async function _signIn() {\n // 1. Check if we have a refresh token in local storage\n return await maybeRefresh()\n // 2. If there is no refresh token we are likely trying to perform the callback\n ?? await maybeHandleAuthReturn()\n // 3. If we haven't been able to load the token from one of the two above ways, we need to make the initial auth request\n ?? await initiateLoginRedirect() as unknown as Token;\n }\n\n return getToken;\n}\n"],"mappings":"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","ignoreList":[]}
@@ -13,6 +13,7 @@
13
13
  * See the License for the specific language governing permissions and
14
14
  * limitations under the License.
15
15
  */
16
+
16
17
  export { createConfidentialOauthClient } from "./createConfidentialOauthClient.js";
17
18
  export { createPublicOauthClient } from "./createPublicOauthClient.js";
18
19
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["createConfidentialOauthClient","createPublicOauthClient"],"sources":["index.js"],"sourcesContent":["/*\n * Copyright 2024 Palantir Technologies, Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nexport { createConfidentialOauthClient } from \"./createConfidentialOauthClient.js\";\nexport { createPublicOauthClient } from \"./createPublicOauthClient.js\";\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAASA,6BAA6B,QAAQ,oCAAoC;AAClF,SAASC,uBAAuB,QAAQ,8BAA8B","ignoreList":[]}
1
+ {"version":3,"file":"index.js","names":["createConfidentialOauthClient","createPublicOauthClient"],"sources":["index.ts"],"sourcesContent":["/*\n * Copyright 2024 Palantir Technologies, Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nexport { createConfidentialOauthClient } from \"./createConfidentialOauthClient.js\";\nexport { createPublicOauthClient } from \"./createPublicOauthClient.js\";\nexport type { PublicOauthClient } from \"./PublicOauthClient.js\";\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,SAASA,6BAA6B,QAAQ,oCAAoC;AAClF,SAASC,uBAAuB,QAAQ,8BAA8B","ignoreList":[]}
@@ -0,0 +1,318 @@
1
+ /*
2
+ * Copyright 2024 Palantir Technologies, Inc. All rights reserved.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ /* eslint-disable @typescript-eslint/consistent-type-imports */
18
+ import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
19
+ import * as commonJs from "./common.js";
20
+ import { createPublicOauthClient } from "./createPublicOauthClient.js";
21
+ const hoistedMocks = vi.hoisted(() => {
22
+ return {
23
+ makeTokenAndSaveRefresh: vi.fn()
24
+ };
25
+ });
26
+ vi.mock("delay", async importOriginal => {
27
+ const original = await importOriginal();
28
+ return {
29
+ default: vi.fn((ms, opts) => {
30
+ // speed up the tests
31
+ return original.default(ms / 100, opts);
32
+ })
33
+ };
34
+ });
35
+ vi.mock("./common.js", async importOriginal => {
36
+ const original = await importOriginal();
37
+ return {
38
+ ...original,
39
+ common: vi.fn((...args) => {
40
+ const result = original.common(...args);
41
+ return {
42
+ ...result,
43
+ makeTokenAndSaveRefresh: hoistedMocks.makeTokenAndSaveRefresh
44
+ };
45
+ }),
46
+ readLocal: vi.spyOn(original, "readLocal"),
47
+ readSession: vi.spyOn(original, "readSession")
48
+ // vi.fn(original.readLocal),
49
+ };
50
+ });
51
+ vi.mock("oauth4webapi");
52
+ describe(createPublicOauthClient, () => {
53
+ const DEFAULT_CUR_URL = new URL("https://fauxurl");
54
+ const fetchFn = vi.fn();
55
+ const mockWindow = {
56
+ location: {
57
+ toString: vi.fn(() => DEFAULT_CUR_URL),
58
+ assign: vi.fn(),
59
+ href: DEFAULT_CUR_URL.href,
60
+ pathname: DEFAULT_CUR_URL.pathname
61
+ },
62
+ history: {
63
+ replaceState: vi.fn()
64
+ }
65
+ };
66
+ const mockLocalStorage = {
67
+ setItem: vi.fn(),
68
+ getItem: vi.fn(),
69
+ removeItem: vi.fn()
70
+ };
71
+ const mockSessionStorage = {
72
+ setItem: vi.fn(),
73
+ getItem: vi.fn(),
74
+ removeItem: vi.fn()
75
+ };
76
+ beforeEach(() => {
77
+ vi.restoreAllMocks();
78
+ fetchFn.mockRestore();
79
+ });
80
+ afterEach(() => {
81
+ vi.clearAllMocks();
82
+ fetchFn.mockReset();
83
+ });
84
+ beforeAll(() => {
85
+ vi.stubGlobal("window", mockWindow);
86
+ vi.stubGlobal("localStorage", mockLocalStorage);
87
+ vi.stubGlobal("sessionStorage", mockSessionStorage);
88
+ });
89
+ afterAll(() => {
90
+ vi.unstubAllGlobals();
91
+ });
92
+ const BASE_CLIENT_ARGS = {
93
+ clientId: "this-would-be-client-id",
94
+ foundryUrl: "https://a.foundry.url.local",
95
+ redirectUrl: "https://my.redirect.url.local",
96
+ fetchFn
97
+ };
98
+ const fullLoginUrl = new URL("/loginPage", DEFAULT_CUR_URL);
99
+ function setupWindowLocation(currentUrl) {
100
+ mockWindow.location.href = new URL(currentUrl).href;
101
+ mockWindow.location.pathname = new URL(currentUrl).pathname;
102
+ }
103
+ function setupLocalState(initialLocalState) {
104
+ vi.mocked(commonJs.readLocal).mockImplementation(() => initialLocalState);
105
+ }
106
+ let client;
107
+ let clientArgs;
108
+ function setupClient(clientArgs_) {
109
+ clientArgs = clientArgs_;
110
+ if (clientArgs.refreshTokenMarker) {
111
+ client = createPublicOauthClient(clientArgs.clientId, clientArgs.foundryUrl, clientArgs.redirectUrl, clientArgs);
112
+ } else {
113
+ client = createPublicOauthClient(clientArgs.clientId, clientArgs.foundryUrl, clientArgs.redirectUrl, clientArgs.useHistory, clientArgs.loginPage, clientArgs.postLoginPage, clientArgs.scopes, clientArgs.fetchFn, clientArgs.ctxPath);
114
+ }
115
+ }
116
+ beforeEach(() => {
117
+ // prevents mistakes like forgetting to initialize the client with an
118
+ // old value around
119
+ client = undefined;
120
+ clientArgs = undefined;
121
+ });
122
+ async function expectRedirectToMultipass(tokenPromise) {
123
+ // we assume window.location.assign will take you away therefore we
124
+ // also have to expect in this scenario that the thrown error happens
125
+ await expect(tokenPromise).rejects.toThrowError(new Error("Unable to redirect"));
126
+ expect(mockWindow.location.assign).toHaveBeenCalledOnce();
127
+ const url = new URL(mockWindow.location.assign.mock.calls[0][0]);
128
+ expect(url.origin).toEqual(clientArgs.foundryUrl);
129
+ expect(url.pathname).toEqual("/multipass/api/oauth2/authorize");
130
+ expect(Object.fromEntries(url.searchParams.entries())).toEqual(expect.objectContaining({
131
+ client_id: clientArgs.clientId,
132
+ code_challenge: expect.any(String),
133
+ redirect_uri: clientArgs.redirectUrl,
134
+ response_type: "code",
135
+ code_challenge_method: "S256",
136
+ scope: "offline_access api:read-data api:write-data",
137
+ state: expect.any(String)
138
+ }));
139
+ }
140
+ describe("refresh token marker", () => {
141
+ beforeEach(() => {
142
+ setupWindowLocation("https://foundry.local");
143
+ });
144
+ it("should not allow refresh if the refresh markers do not match", async () => {
145
+ setupLocalState({
146
+ refresh_token: "refreshToken",
147
+ refreshTokenMarker: "not-the-right-marker"
148
+ });
149
+ setupClient({
150
+ ...BASE_CLIENT_ARGS,
151
+ refreshTokenMarker: "marker"
152
+ });
153
+ await expectRedirectToMultipass(client());
154
+ });
155
+ it("should try to refresh if refresh tokens do match", async () => {
156
+ setupLocalState({
157
+ refresh_token: "refreshToken",
158
+ refreshTokenMarker: "marker"
159
+ });
160
+ setupClient({
161
+ ...BASE_CLIENT_ARGS,
162
+ refreshTokenMarker: "marker"
163
+ });
164
+ hoistedMocks.makeTokenAndSaveRefresh.mockImplementationOnce(() => ({
165
+ access_token: "some token",
166
+ expires_at: Date.now(),
167
+ expires_in: 10000
168
+ }));
169
+ const tokenPromise = client();
170
+ await expect(tokenPromise).resolves.toEqual("some token");
171
+ expect(hoistedMocks.makeTokenAndSaveRefresh).toBeCalledTimes(1);
172
+ });
173
+ });
174
+ describe.each([{
175
+ name: "Basic Usage",
176
+ currentUrl: DEFAULT_CUR_URL,
177
+ clientArgs: {
178
+ ...BASE_CLIENT_ARGS
179
+ },
180
+ should: {
181
+ redirectToLoginPage: false
182
+ }
183
+ }, {
184
+ name: "Base w/ full loginPage and curUrl is root",
185
+ currentUrl: DEFAULT_CUR_URL,
186
+ clientArgs: {
187
+ ...BASE_CLIENT_ARGS,
188
+ loginPage: fullLoginUrl.toString()
189
+ },
190
+ should: {
191
+ redirectToLoginPage: true
192
+ }
193
+ }, {
194
+ name: "Base w/ full loginPage and curUrl is login page",
195
+ currentUrl: fullLoginUrl,
196
+ clientArgs: {
197
+ ...BASE_CLIENT_ARGS,
198
+ loginPage: fullLoginUrl.toString()
199
+ },
200
+ should: {
201
+ redirectToLoginPage: false
202
+ }
203
+ }, {
204
+ name: "Base w/ short loginPage and curUrl is root",
205
+ currentUrl: DEFAULT_CUR_URL,
206
+ clientArgs: {
207
+ ...BASE_CLIENT_ARGS,
208
+ loginPage: "/loginPage"
209
+ },
210
+ should: {
211
+ redirectToLoginPage: true
212
+ }
213
+ }, {
214
+ name: "Base w/ short loginPage and curUrl is loginPage",
215
+ currentUrl: new URL("/loginPage", DEFAULT_CUR_URL),
216
+ clientArgs: {
217
+ ...BASE_CLIENT_ARGS,
218
+ loginPage: "/loginPage"
219
+ },
220
+ should: {
221
+ redirectToLoginPage: false
222
+ }
223
+ }])("$name", ({
224
+ clientArgs,
225
+ currentUrl,
226
+ should
227
+ }) => {
228
+ beforeEach(() => {
229
+ setupClient(clientArgs);
230
+ setupWindowLocation(currentUrl.toString());
231
+ });
232
+ it("calls common with expected values", () => {
233
+ expect(vi.mocked(commonJs.common)).toHaveBeenCalledWith(expect.objectContaining({
234
+ client_id: clientArgs.clientId,
235
+ token_endpoint_auth_method: "none"
236
+ }), commonJs.createAuthorizationServer(clientArgs.ctxPath ?? "/multipass", clientArgs.foundryUrl), expect.any(Function), expect.anything(), expect.any(Function), undefined);
237
+ });
238
+ describe.each([{
239
+ localStorage: {
240
+ refresh_token: "a-refresh-token"
241
+ },
242
+ sessionStorage: {}
243
+ }, {
244
+ localStorage: {},
245
+ sessionStorage: {
246
+ codeVerifier: "hi",
247
+ state: "mom",
248
+ oldUrl: "https://someoldurl.local"
249
+ }
250
+ }, {
251
+ localStorage: {},
252
+ sessionStorage: {}
253
+ }])("Initial Local State: %s", initialState => {
254
+ const ACCESS_TOKEN = (Math.random() + 1).toString(36).substring(7);
255
+ beforeEach(() => {
256
+ vi.mocked(commonJs.readLocal).mockImplementation(() => initialState.localStorage);
257
+ vi.mocked(commonJs.readSession).mockImplementation(() => initialState.sessionStorage);
258
+ hoistedMocks.makeTokenAndSaveRefresh.mockImplementation(() => ({
259
+ access_token: ACCESS_TOKEN,
260
+ expires_at: Date.now(),
261
+ expires_in: 10000
262
+ }));
263
+ });
264
+ if (Object.keys(initialState.localStorage).length === 0 && Object.keys(initialState.sessionStorage).length === 0) {
265
+ if (should.redirectToLoginPage) {
266
+ it("redirects to login page", async () => {
267
+ const tokenPromise = client();
268
+ expect(vi.mocked(commonJs.common)).toBeCalledTimes(1);
269
+ if (should.redirectToLoginPage) {
270
+ // expect save local
271
+ await expect(tokenPromise).resolves.toBeUndefined();
272
+ expect(mockSessionStorage.setItem).toBeCalledWith(`@osdk/oauth : refresh : ${clientArgs.clientId}`, JSON.stringify({
273
+ oldUrl: window.location.toString()
274
+ }));
275
+ expect(mockWindow.history.replaceState).toHaveBeenCalledWith({}, "", clientArgs.loginPage);
276
+ }
277
+ });
278
+ } else {
279
+ it("redirects to multipass", async () => {
280
+ const tokenPromise = client();
281
+ expect(vi.mocked(commonJs.common)).toBeCalledTimes(1);
282
+ if (should.redirectToLoginPage) {
283
+ // expect save local
284
+ await expect(tokenPromise).resolves.toBeUndefined();
285
+ expect(mockSessionStorage.setItem).toBeCalledWith(`@osdk/oauth : refresh : ${clientArgs.clientId}`, JSON.stringify({
286
+ oldUrl: window.location.toString()
287
+ }));
288
+ expect(mockWindow.history.replaceState).toHaveBeenCalledWith({}, "", clientArgs.loginPage);
289
+ return;
290
+ }
291
+ await expectRedirectToMultipass(tokenPromise);
292
+ });
293
+ }
294
+ }
295
+ if (initialState.sessionStorage.codeVerifier) {
296
+ it("tries to auth with return results", async () => {
297
+ await expect(client()).resolves.toEqual(ACCESS_TOKEN);
298
+ expect(hoistedMocks.makeTokenAndSaveRefresh).toHaveBeenCalledTimes(1);
299
+ expect(hoistedMocks.makeTokenAndSaveRefresh).toHaveBeenCalledWith(undefined,
300
+ // this is only because we didn't mock out the internals
301
+ "signIn");
302
+ expect(mockWindow.history.replaceState).toHaveBeenCalledTimes(1);
303
+ expect(mockWindow.history.replaceState).toBeCalledWith(expect.anything(), expect.anything(), initialState.sessionStorage.oldUrl);
304
+ });
305
+ }
306
+ if (initialState.localStorage.refresh_token) {
307
+ it("refreshes", async () => {
308
+ await expect(client()).resolves.toEqual(ACCESS_TOKEN);
309
+ expect(hoistedMocks.makeTokenAndSaveRefresh).toHaveBeenCalledTimes(1);
310
+ expect(hoistedMocks.makeTokenAndSaveRefresh).toHaveBeenCalledWith(undefined,
311
+ // this is only because we didn't mock out the internals
312
+ "refresh");
313
+ });
314
+ }
315
+ });
316
+ });
317
+ });
318
+ //# sourceMappingURL=publicOauth.test.js.map