@osdk/oauth 1.1.0-beta.9 → 1.1.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 (49) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/build/browser/AuthorizationServer.test.js +7 -0
  3. package/build/browser/AuthorizationServer.test.js.map +1 -1
  4. package/build/browser/common.js +4 -3
  5. package/build/browser/common.js.map +1 -1
  6. package/build/browser/common.test.js +4 -3
  7. package/build/browser/common.test.js.map +1 -1
  8. package/build/browser/createConfidentialOauthClient.js +3 -2
  9. package/build/browser/createConfidentialOauthClient.js.map +1 -1
  10. package/build/browser/createPublicOauthClient.js +8 -5
  11. package/build/browser/createPublicOauthClient.js.map +1 -1
  12. package/build/browser/publicOauth.test.js +37 -9
  13. package/build/browser/publicOauth.test.js.map +1 -1
  14. package/build/browser/utils.js +1 -1
  15. package/build/browser/utils.js.map +1 -1
  16. package/build/cjs/index.cjs +1019 -35
  17. package/build/cjs/index.cjs.map +1 -1
  18. package/build/esm/AuthorizationServer.test.js +7 -0
  19. package/build/esm/AuthorizationServer.test.js.map +1 -1
  20. package/build/esm/common.js +4 -3
  21. package/build/esm/common.js.map +1 -1
  22. package/build/esm/common.test.js +4 -3
  23. package/build/esm/common.test.js.map +1 -1
  24. package/build/esm/createConfidentialOauthClient.js +3 -2
  25. package/build/esm/createConfidentialOauthClient.js.map +1 -1
  26. package/build/esm/createPublicOauthClient.js +8 -5
  27. package/build/esm/createPublicOauthClient.js.map +1 -1
  28. package/build/esm/publicOauth.test.js +37 -9
  29. package/build/esm/publicOauth.test.js.map +1 -1
  30. package/build/esm/utils.js +1 -1
  31. package/build/esm/utils.js.map +1 -1
  32. package/build/types/AuthorizationServer.test.d.ts.map +1 -0
  33. package/build/types/BaseOauthClient.d.ts +3 -3
  34. package/build/types/BaseOauthClient.d.ts.map +1 -0
  35. package/build/types/ConfidentialOauthClient.d.ts.map +1 -0
  36. package/build/types/PublicOauthClient.d.ts.map +1 -0
  37. package/build/types/Token.d.ts.map +1 -0
  38. package/build/types/__tests__/createPublicOauthClient.test.d.ts.map +1 -0
  39. package/build/types/common.d.ts +15 -12
  40. package/build/types/common.d.ts.map +1 -0
  41. package/build/types/common.test.d.ts.map +1 -0
  42. package/build/types/createConfidentialOauthClient.d.ts.map +1 -0
  43. package/build/types/createPublicOauthClient.d.ts.map +1 -0
  44. package/build/types/index.d.ts.map +1 -0
  45. package/build/types/publicOauth.test.d.ts.map +1 -0
  46. package/build/types/throwIfError.d.ts.map +1 -0
  47. package/build/types/utils.d.ts +2 -1
  48. package/build/types/utils.d.ts.map +1 -0
  49. package/package.json +1 -2
package/CHANGELOG.md CHANGED
@@ -1,5 +1,45 @@
1
1
  # @osdk/oauth
2
2
 
3
+ ## 1.1.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 2354693: Discard refresh token if requested scopes have changed
8
+
9
+ ## 1.1.0
10
+
11
+ ### Minor Changes
12
+
13
+ - 25fd9f0: Updating package.json to have better entry points to support react native bundlers.
14
+ - 0166d14: Fixes an infinite redirect issue on some errors in the OAuth flow
15
+ - e968bc0: Use api:ontologies-X instead of api:X-data
16
+ - 44e62f6: Updating fetch for auth and other packages.
17
+ - 91cfa3a: Fixing url parsing for client.
18
+ - d501104: Adds unit testing of public oauth flow
19
+ - 9d35a1f: Remove unused imports
20
+ - c691c8b: Package now has experimental support for commonjs
21
+ - 9df2d99: Fixes CJS generation for OAuth
22
+ - 33590c7: Add tests for createPublicOauthClient overload
23
+ - 02685ab: Adding return types to functions.
24
+ - c9da74c: Improves code quality by fixing linting errors
25
+ - 91cfa3a: Fixing URL parsing for custom entry points.
26
+ - 3d2ba6f: Revert api:x-read/write back to api:read/write-x
27
+ - d12c92b: Fixes process.env.NODE_ENV handling
28
+ - e00bb29: Overload createPublicOauthClient to support object of options
29
+ - 696c59c: Enables additional metadata to be required to match a refresh token in local storage
30
+
31
+ ## 1.1.0-beta.11
32
+
33
+ ### Minor Changes
34
+
35
+ - 9df2d99: Fixes CJS generation for OAuth
36
+
37
+ ## 1.1.0-beta.10
38
+
39
+ ### Minor Changes
40
+
41
+ - 44e62f6: Updating fetch for auth and other packages.
42
+
3
43
  ## 1.1.0-beta.9
4
44
 
5
45
  ### Minor Changes
@@ -41,6 +41,13 @@ describe("Create authorization server", () => {
41
41
  revocation_endpoint: "https://stack.palantir.com/foo/first/someStuff/multipass/api/oauth2/revoke_token",
42
42
  issuer: "https://stack.palantir.com/foo/first/someStuff/multipass"
43
43
  });
44
+ const authServerCustomWithTrailingSlash = createAuthorizationServer("multipass", "https://stack.palantir.com/foo/first/someStuff/");
45
+ expect(authServerCustomWithTrailingSlash).toEqual({
46
+ token_endpoint: "https://stack.palantir.com/foo/first/someStuff/multipass/api/oauth2/token",
47
+ authorization_endpoint: "https://stack.palantir.com/foo/first/someStuff/multipass/api/oauth2/authorize",
48
+ revocation_endpoint: "https://stack.palantir.com/foo/first/someStuff/multipass/api/oauth2/revoke_token",
49
+ issuer: "https://stack.palantir.com/foo/first/someStuff/multipass"
50
+ });
44
51
  });
45
52
  });
46
53
  //# sourceMappingURL=AuthorizationServer.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"AuthorizationServer.test.js","names":["describe","expect","it","createAuthorizationServer","authServer","toEqual","token_endpoint","authorization_endpoint","revocation_endpoint","issuer","authServerCustom"],"sources":["AuthorizationServer.test.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 { describe, expect, it } from \"vitest\";\n\nimport { createAuthorizationServer } from \"./common.js\";\n\ndescribe(\"Create authorization server\", () => {\n it(\"works with old multipass ctx path\", () => {\n const authServer = createAuthorizationServer(\n \"/multipass\",\n \"https://stack.palantir.com\",\n );\n expect(authServer).toEqual({\n token_endpoint: \"https://stack.palantir.com/multipass/api/oauth2/token\",\n authorization_endpoint:\n \"https://stack.palantir.com/multipass/api/oauth2/authorize\",\n revocation_endpoint:\n \"https://stack.palantir.com/multipass/api/oauth2/revoke_token\",\n issuer: \"https://stack.palantir.com/multipass\",\n });\n });\n\n it(\"works with new multipass ctx path\", () => {\n const authServer = createAuthorizationServer(\n \"multipass\",\n \"https://stack.palantir.com\",\n );\n expect(authServer).toEqual({\n token_endpoint: \"https://stack.palantir.com/multipass/api/oauth2/token\",\n authorization_endpoint:\n \"https://stack.palantir.com/multipass/api/oauth2/authorize\",\n revocation_endpoint:\n \"https://stack.palantir.com/multipass/api/oauth2/revoke_token\",\n issuer: \"https://stack.palantir.com/multipass\",\n });\n\n const authServerCustom = createAuthorizationServer(\n \"multipass\",\n \"https://stack.palantir.com/foo/first/someStuff\",\n );\n expect(authServerCustom).toEqual({\n token_endpoint:\n \"https://stack.palantir.com/foo/first/someStuff/multipass/api/oauth2/token\",\n authorization_endpoint:\n \"https://stack.palantir.com/foo/first/someStuff/multipass/api/oauth2/authorize\",\n revocation_endpoint:\n \"https://stack.palantir.com/foo/first/someStuff/multipass/api/oauth2/revoke_token\",\n issuer: \"https://stack.palantir.com/foo/first/someStuff/multipass\",\n });\n });\n});\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,SAASA,QAAQ,EAAEC,MAAM,EAAEC,EAAE,QAAQ,QAAQ;AAE7C,SAASC,yBAAyB,QAAQ,aAAa;AAEvDH,QAAQ,CAAC,6BAA6B,EAAE,MAAM;EAC5CE,EAAE,CAAC,mCAAmC,EAAE,MAAM;IAC5C,MAAME,UAAU,GAAGD,yBAAyB,CAC1C,YAAY,EACZ,4BACF,CAAC;IACDF,MAAM,CAACG,UAAU,CAAC,CAACC,OAAO,CAAC;MACzBC,cAAc,EAAE,uDAAuD;MACvEC,sBAAsB,EACpB,2DAA2D;MAC7DC,mBAAmB,EACjB,8DAA8D;MAChEC,MAAM,EAAE;IACV,CAAC,CAAC;EACJ,CAAC,CAAC;EAEFP,EAAE,CAAC,mCAAmC,EAAE,MAAM;IAC5C,MAAME,UAAU,GAAGD,yBAAyB,CAC1C,WAAW,EACX,4BACF,CAAC;IACDF,MAAM,CAACG,UAAU,CAAC,CAACC,OAAO,CAAC;MACzBC,cAAc,EAAE,uDAAuD;MACvEC,sBAAsB,EACpB,2DAA2D;MAC7DC,mBAAmB,EACjB,8DAA8D;MAChEC,MAAM,EAAE;IACV,CAAC,CAAC;IAEF,MAAMC,gBAAgB,GAAGP,yBAAyB,CAChD,WAAW,EACX,gDACF,CAAC;IACDF,MAAM,CAACS,gBAAgB,CAAC,CAACL,OAAO,CAAC;MAC/BC,cAAc,EACZ,2EAA2E;MAC7EC,sBAAsB,EACpB,+EAA+E;MACjFC,mBAAmB,EACjB,kFAAkF;MACpFC,MAAM,EAAE;IACV,CAAC,CAAC;EACJ,CAAC,CAAC;AACJ,CAAC,CAAC","ignoreList":[]}
1
+ {"version":3,"file":"AuthorizationServer.test.js","names":["describe","expect","it","createAuthorizationServer","authServer","toEqual","token_endpoint","authorization_endpoint","revocation_endpoint","issuer","authServerCustom","authServerCustomWithTrailingSlash"],"sources":["AuthorizationServer.test.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 { describe, expect, it } from \"vitest\";\n\nimport { createAuthorizationServer } from \"./common.js\";\n\ndescribe(\"Create authorization server\", () => {\n it(\"works with old multipass ctx path\", () => {\n const authServer = createAuthorizationServer(\n \"/multipass\",\n \"https://stack.palantir.com\",\n );\n expect(authServer).toEqual({\n token_endpoint: \"https://stack.palantir.com/multipass/api/oauth2/token\",\n authorization_endpoint:\n \"https://stack.palantir.com/multipass/api/oauth2/authorize\",\n revocation_endpoint:\n \"https://stack.palantir.com/multipass/api/oauth2/revoke_token\",\n issuer: \"https://stack.palantir.com/multipass\",\n });\n });\n\n it(\"works with new multipass ctx path\", () => {\n const authServer = createAuthorizationServer(\n \"multipass\",\n \"https://stack.palantir.com\",\n );\n expect(authServer).toEqual({\n token_endpoint: \"https://stack.palantir.com/multipass/api/oauth2/token\",\n authorization_endpoint:\n \"https://stack.palantir.com/multipass/api/oauth2/authorize\",\n revocation_endpoint:\n \"https://stack.palantir.com/multipass/api/oauth2/revoke_token\",\n issuer: \"https://stack.palantir.com/multipass\",\n });\n\n const authServerCustom = createAuthorizationServer(\n \"multipass\",\n \"https://stack.palantir.com/foo/first/someStuff\",\n );\n expect(authServerCustom).toEqual({\n token_endpoint:\n \"https://stack.palantir.com/foo/first/someStuff/multipass/api/oauth2/token\",\n authorization_endpoint:\n \"https://stack.palantir.com/foo/first/someStuff/multipass/api/oauth2/authorize\",\n revocation_endpoint:\n \"https://stack.palantir.com/foo/first/someStuff/multipass/api/oauth2/revoke_token\",\n issuer: \"https://stack.palantir.com/foo/first/someStuff/multipass\",\n });\n const authServerCustomWithTrailingSlash = createAuthorizationServer(\n \"multipass\",\n \"https://stack.palantir.com/foo/first/someStuff/\",\n );\n expect(authServerCustomWithTrailingSlash).toEqual({\n token_endpoint:\n \"https://stack.palantir.com/foo/first/someStuff/multipass/api/oauth2/token\",\n authorization_endpoint:\n \"https://stack.palantir.com/foo/first/someStuff/multipass/api/oauth2/authorize\",\n revocation_endpoint:\n \"https://stack.palantir.com/foo/first/someStuff/multipass/api/oauth2/revoke_token\",\n issuer: \"https://stack.palantir.com/foo/first/someStuff/multipass\",\n });\n });\n});\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,SAASA,QAAQ,EAAEC,MAAM,EAAEC,EAAE,QAAQ,QAAQ;AAE7C,SAASC,yBAAyB,QAAQ,aAAa;AAEvDH,QAAQ,CAAC,6BAA6B,EAAE,MAAM;EAC5CE,EAAE,CAAC,mCAAmC,EAAE,MAAM;IAC5C,MAAME,UAAU,GAAGD,yBAAyB,CAC1C,YAAY,EACZ,4BACF,CAAC;IACDF,MAAM,CAACG,UAAU,CAAC,CAACC,OAAO,CAAC;MACzBC,cAAc,EAAE,uDAAuD;MACvEC,sBAAsB,EACpB,2DAA2D;MAC7DC,mBAAmB,EACjB,8DAA8D;MAChEC,MAAM,EAAE;IACV,CAAC,CAAC;EACJ,CAAC,CAAC;EAEFP,EAAE,CAAC,mCAAmC,EAAE,MAAM;IAC5C,MAAME,UAAU,GAAGD,yBAAyB,CAC1C,WAAW,EACX,4BACF,CAAC;IACDF,MAAM,CAACG,UAAU,CAAC,CAACC,OAAO,CAAC;MACzBC,cAAc,EAAE,uDAAuD;MACvEC,sBAAsB,EACpB,2DAA2D;MAC7DC,mBAAmB,EACjB,8DAA8D;MAChEC,MAAM,EAAE;IACV,CAAC,CAAC;IAEF,MAAMC,gBAAgB,GAAGP,yBAAyB,CAChD,WAAW,EACX,gDACF,CAAC;IACDF,MAAM,CAACS,gBAAgB,CAAC,CAACL,OAAO,CAAC;MAC/BC,cAAc,EACZ,2EAA2E;MAC7EC,sBAAsB,EACpB,+EAA+E;MACjFC,mBAAmB,EACjB,kFAAkF;MACpFC,MAAM,EAAE;IACV,CAAC,CAAC;IACF,MAAME,iCAAiC,GAAGR,yBAAyB,CACjE,WAAW,EACX,iDACF,CAAC;IACDF,MAAM,CAACU,iCAAiC,CAAC,CAACN,OAAO,CAAC;MAChDC,cAAc,EACZ,2EAA2E;MAC7EC,sBAAsB,EACpB,+EAA+E;MACjFC,mBAAmB,EACjB,kFAAkF;MACpFC,MAAM,EAAE;IACV,CAAC,CAAC;EACJ,CAAC,CAAC;AACJ,CAAC,CAAC","ignoreList":[]}
@@ -50,7 +50,7 @@ export function readSession(client) {
50
50
  // MUST `sessionStorage?` as nodejs does not have sessionStorage
51
51
  globalThis.sessionStorage?.getItem(localStorageKey(client)) ?? "{}");
52
52
  }
53
- export function common(client, as, _signIn, oauthHttpOptions, refresh, refreshTokenMarker) {
53
+ export function common(client, as, _signIn, oauthHttpOptions, refresh, refreshTokenMarker, scopes) {
54
54
  let token;
55
55
  const eventTarget = new TypedEventTarget();
56
56
  function makeTokenAndSaveRefresh(resp, type) {
@@ -62,7 +62,8 @@ export function common(client, as, _signIn, oauthHttpOptions, refresh, refreshTo
62
62
  !(expires_in != null) ? process.env.NODE_ENV !== "production" ? invariant(false) : invariant(false) : void 0;
63
63
  saveLocal(client, {
64
64
  refresh_token,
65
- refreshTokenMarker
65
+ refreshTokenMarker,
66
+ requestedScopes: scopes
66
67
  });
67
68
  token = {
68
69
  refresh_token,
@@ -135,7 +136,7 @@ export function common(client, as, _signIn, oauthHttpOptions, refresh, refreshTo
135
136
  };
136
137
  }
137
138
  export function createAuthorizationServer(ctxPath, url) {
138
- const issuer = `${new URL(ctxPath, url + "/")}`;
139
+ const issuer = `${new URL(ctxPath, url.endsWith("/") ? url : url + "/")}`;
139
140
  return {
140
141
  token_endpoint: `${issuer}/api/oauth2/token`,
141
142
  authorization_endpoint: `${issuer}/api/oauth2/authorize`,
@@ -1 +1 @@
1
- {"version":3,"file":"common.js","names":["processRevocationResponse","revocationRequest","invariant","TypedEventTarget","throwIfError","CustomEvent","globalThis","localStorageKey","client","client_id","saveLocal","x","localStorage","setItem","JSON","stringify","removeLocal","removeItem","readLocal","parse","getItem","saveSession","sessionStorage","removeSession","readSession","common","as","_signIn","oauthHttpOptions","refresh","refreshTokenMarker","token","eventTarget","makeTokenAndSaveRefresh","resp","type","refresh_token","expires_in","access_token","process","env","NODE_ENV","expires_at","Date","now","dispatchTypedEvent","detail","refreshTimeout","rmTimeout","clearTimeout","restartRefreshTimer","evt","setTimeout","pendingSignIn","signIn","undefined","addEventListener","getTokenOrUndefined","getToken","Object","assign","signOut","result","Event","bind","removeEventListener","createAuthorizationServer","ctxPath","url","issuer","URL","token_endpoint","authorization_endpoint","revocation_endpoint"],"sources":["common.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 type {\n AuthorizationServer,\n Client,\n HttpRequestOptions,\n OAuth2TokenEndpointResponse,\n} from \"oauth4webapi\";\nimport { processRevocationResponse, revocationRequest } from \"oauth4webapi\";\nimport invariant from \"tiny-invariant\";\nimport { TypedEventTarget } from \"typescript-event-target\";\nimport type { BaseOauthClient, Events } from \"./BaseOauthClient.js\";\nimport { throwIfError } from \"./throwIfError.js\";\nimport type { Token } from \"./Token.js\";\n\n// Node 18 is supposed to have a `CustomEvent` but it is not exposed on `globalThis`\n// which creates a problem for making a single codebase for node and browser. This polyfill works around it\nconst CustomEvent = process.env.TARGET === \"browser\"\n ? globalThis.CustomEvent\n : globalThis.CustomEvent\n ?? class CustomEvent<T> extends Event {\n #detail: T | null;\n\n constructor(type: string, options: EventInit & { detail: T }) {\n super(type, options);\n this.#detail = options?.detail ?? null;\n }\n\n get detail() {\n return this.#detail;\n }\n };\n\ndeclare const process: {\n env: Record<string, string | undefined>;\n};\n\nexport interface LocalStorageState {\n refresh_token?: string;\n refreshTokenMarker?: string;\n}\n\nexport type SessionStorageState =\n // when we are going to the login page\n | {\n codeVerifier?: never;\n state?: never;\n oldUrl: string;\n }\n // when we are redirecting to oauth login\n | {\n codeVerifier: string;\n state: string;\n oldUrl: string;\n }\n // when we have the refresh token\n | {\n codeVerifier?: never;\n state?: never;\n oldUrl?: never;\n };\n\nfunction localStorageKey(client: Client) {\n return `@osdk/oauth : refresh : ${client.client_id}`;\n}\n\nexport function saveLocal(client: Client, x: LocalStorageState): void {\n // MUST `localStorage?` as nodejs does not have localStorage\n globalThis.localStorage?.setItem(\n localStorageKey(client),\n JSON.stringify(x),\n );\n}\n\nexport function removeLocal(client: Client): void {\n // MUST `localStorage?` as nodejs does not have localStorage\n globalThis.localStorage?.removeItem(\n localStorageKey(client),\n );\n}\n\nexport function readLocal(client: Client): LocalStorageState {\n return JSON.parse(\n // MUST `localStorage?` as nodejs does not have localStorage\n globalThis.localStorage?.getItem(\n localStorageKey(client),\n )\n ?? \"{}\",\n );\n}\n\nexport function saveSession(client: Client, x: SessionStorageState): void {\n // MUST `sessionStorage?` as nodejs does not have sessionStorage\n globalThis.sessionStorage?.setItem(\n localStorageKey(client),\n JSON.stringify(x),\n );\n}\n\nexport function removeSession(client: Client): void {\n // MUST `sessionStorage?` as nodejs does not have sessionStorage\n globalThis.sessionStorage?.removeItem(\n localStorageKey(client),\n );\n}\n\nexport function readSession(client: Client): SessionStorageState {\n return JSON.parse(\n // MUST `sessionStorage?` as nodejs does not have sessionStorage\n globalThis.sessionStorage?.getItem(\n localStorageKey(client),\n )\n ?? \"{}\",\n );\n}\n\nexport function common<\n R extends undefined | (() => Promise<Token | undefined>),\n>(\n client: Client,\n as: AuthorizationServer,\n _signIn: () => Promise<Token>,\n oauthHttpOptions: HttpRequestOptions,\n refresh: R,\n refreshTokenMarker: string | undefined,\n): {\n getToken: BaseOauthClient<keyof Events & string> & { refresh: R };\n makeTokenAndSaveRefresh: (\n resp: OAuth2TokenEndpointResponse,\n type: \"signIn\" | \"refresh\",\n ) => Token;\n} {\n let token: Token | undefined;\n const eventTarget = new TypedEventTarget<Events>();\n\n function makeTokenAndSaveRefresh(\n resp: OAuth2TokenEndpointResponse,\n type: \"signIn\" | \"refresh\",\n ): Token {\n const { refresh_token, expires_in, access_token } = resp;\n invariant(expires_in != null);\n saveLocal(client, {\n refresh_token,\n refreshTokenMarker,\n });\n token = {\n refresh_token,\n expires_in,\n access_token,\n expires_at: Date.now() + expires_in * 1000,\n };\n\n eventTarget.dispatchTypedEvent(\n type,\n new CustomEvent(\n type,\n { detail: token },\n ),\n );\n return token;\n }\n\n let refreshTimeout: ReturnType<typeof setTimeout>;\n function rmTimeout() {\n if (refreshTimeout) clearTimeout(refreshTimeout);\n }\n function restartRefreshTimer(evt: CustomEvent<Token>) {\n if (refresh) {\n rmTimeout();\n refreshTimeout = setTimeout(\n refresh,\n evt.detail.expires_in * 1000 - 60 * 1000,\n );\n }\n }\n\n async function signOut() {\n invariant(token, \"not signed in\");\n\n const result = await processRevocationResponse(\n await revocationRequest(\n as,\n client,\n token.access_token,\n oauthHttpOptions,\n ),\n );\n\n rmTimeout();\n\n // Clean up\n removeLocal(client);\n token = undefined;\n throwIfError(result);\n eventTarget.dispatchTypedEvent(\"signOut\", new Event(\"signOut\"));\n }\n\n let pendingSignIn: Promise<Token> | undefined;\n async function signIn() {\n if (pendingSignIn) {\n return pendingSignIn;\n }\n try {\n pendingSignIn = _signIn();\n return await pendingSignIn;\n } finally {\n pendingSignIn = undefined;\n }\n }\n\n eventTarget.addEventListener(\"signIn\", restartRefreshTimer);\n eventTarget.addEventListener(\"refresh\", restartRefreshTimer);\n\n function getTokenOrUndefined() {\n if (!token || Date.now() >= token.expires_at) {\n return undefined;\n }\n return token?.access_token;\n }\n\n const getToken = Object.assign(async function getToken() {\n if (!token || Date.now() >= token.expires_at) {\n token = await signIn();\n }\n return token?.access_token;\n }, {\n signIn,\n refresh,\n signOut,\n rmTimeout,\n getTokenOrUndefined,\n addEventListener: eventTarget.addEventListener.bind(\n eventTarget,\n ) as typeof eventTarget.addEventListener,\n removeEventListener: eventTarget.removeEventListener.bind(\n eventTarget,\n ) as typeof eventTarget.removeEventListener,\n });\n\n return { getToken, makeTokenAndSaveRefresh };\n}\n\nexport function createAuthorizationServer(\n ctxPath: string,\n url: string,\n): Required<\n Pick<\n AuthorizationServer,\n | \"issuer\"\n | \"token_endpoint\"\n | \"authorization_endpoint\"\n | \"revocation_endpoint\"\n >\n> {\n const issuer = `${new URL(ctxPath, url + \"/\")}`;\n return {\n token_endpoint: `${issuer}/api/oauth2/token`,\n authorization_endpoint: `${issuer}/api/oauth2/authorize`,\n revocation_endpoint: `${issuer}/api/oauth2/revoke_token`,\n issuer,\n };\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"common.js","names":["processRevocationResponse","revocationRequest","invariant","TypedEventTarget","throwIfError","CustomEvent","globalThis","localStorageKey","client","client_id","saveLocal","x","localStorage","setItem","JSON","stringify","removeLocal","removeItem","readLocal","parse","getItem","saveSession","sessionStorage","removeSession","readSession","common","as","_signIn","oauthHttpOptions","refresh","refreshTokenMarker","scopes","token","eventTarget","makeTokenAndSaveRefresh","resp","type","refresh_token","expires_in","access_token","process","env","NODE_ENV","requestedScopes","expires_at","Date","now","dispatchTypedEvent","detail","refreshTimeout","rmTimeout","clearTimeout","restartRefreshTimer","evt","setTimeout","pendingSignIn","signIn","undefined","addEventListener","getTokenOrUndefined","getToken","Object","assign","signOut","result","Event","bind","removeEventListener","createAuthorizationServer","ctxPath","url","issuer","URL","endsWith","token_endpoint","authorization_endpoint","revocation_endpoint"],"sources":["common.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 type {\n AuthorizationServer,\n Client,\n HttpRequestOptions,\n OAuth2TokenEndpointResponse,\n} from \"oauth4webapi\";\nimport { processRevocationResponse, revocationRequest } from \"oauth4webapi\";\nimport invariant from \"tiny-invariant\";\nimport { TypedEventTarget } from \"typescript-event-target\";\nimport type { BaseOauthClient, Events } from \"./BaseOauthClient.js\";\nimport { throwIfError } from \"./throwIfError.js\";\nimport type { Token } from \"./Token.js\";\n\n// Node 18 is supposed to have a `CustomEvent` but it is not exposed on `globalThis`\n// which creates a problem for making a single codebase for node and browser. This polyfill works around it\nconst CustomEvent = process.env.TARGET === \"browser\"\n ? globalThis.CustomEvent\n : globalThis.CustomEvent\n ?? class CustomEvent<T> extends Event {\n #detail: T | null;\n\n constructor(type: string, options: EventInit & { detail: T }) {\n super(type, options);\n this.#detail = options?.detail ?? null;\n }\n\n get detail() {\n return this.#detail;\n }\n };\n\ndeclare const process: {\n env: Record<string, string | undefined>;\n};\n\nexport interface LocalStorageState {\n refresh_token?: string;\n refreshTokenMarker?: string;\n // The stringified space-separated list of scopes requested during the initial auth grant, which our refresh token is still valid for\n // Note any or none of these scopes may have actually been granted when we received our last access token\n requestedScopes?: string;\n}\n\nexport type SessionStorageState =\n // when we are going to the login page\n | {\n codeVerifier?: never;\n state?: never;\n oldUrl: string;\n }\n // when we are redirecting to oauth login\n | {\n codeVerifier: string;\n state: string;\n oldUrl: string;\n }\n // when we have the refresh token\n | {\n codeVerifier?: never;\n state?: never;\n oldUrl?: never;\n };\n\nfunction localStorageKey(client: Client) {\n return `@osdk/oauth : refresh : ${client.client_id}`;\n}\n\nexport function saveLocal(client: Client, x: LocalStorageState): void {\n // MUST `localStorage?` as nodejs does not have localStorage\n globalThis.localStorage?.setItem(\n localStorageKey(client),\n JSON.stringify(x),\n );\n}\n\nexport function removeLocal(client: Client): void {\n // MUST `localStorage?` as nodejs does not have localStorage\n globalThis.localStorage?.removeItem(\n localStorageKey(client),\n );\n}\n\nexport function readLocal(client: Client): LocalStorageState {\n return JSON.parse(\n // MUST `localStorage?` as nodejs does not have localStorage\n globalThis.localStorage?.getItem(\n localStorageKey(client),\n )\n ?? \"{}\",\n );\n}\n\nexport function saveSession(client: Client, x: SessionStorageState): void {\n // MUST `sessionStorage?` as nodejs does not have sessionStorage\n globalThis.sessionStorage?.setItem(\n localStorageKey(client),\n JSON.stringify(x),\n );\n}\n\nexport function removeSession(client: Client): void {\n // MUST `sessionStorage?` as nodejs does not have sessionStorage\n globalThis.sessionStorage?.removeItem(\n localStorageKey(client),\n );\n}\n\nexport function readSession(client: Client): SessionStorageState {\n return JSON.parse(\n // MUST `sessionStorage?` as nodejs does not have sessionStorage\n globalThis.sessionStorage?.getItem(\n localStorageKey(client),\n )\n ?? \"{}\",\n );\n}\n\nexport function common<\n R extends undefined | (() => Promise<Token | undefined>),\n>(\n client: Client,\n as: AuthorizationServer,\n _signIn: () => Promise<Token>,\n oauthHttpOptions: HttpRequestOptions,\n refresh: R,\n refreshTokenMarker: string | undefined,\n scopes: string,\n): {\n getToken: BaseOauthClient<keyof Events & string> & { refresh: R };\n makeTokenAndSaveRefresh: (\n resp: OAuth2TokenEndpointResponse,\n type: \"signIn\" | \"refresh\",\n ) => Token;\n} {\n let token: Token | undefined;\n const eventTarget = new TypedEventTarget<Events>();\n\n function makeTokenAndSaveRefresh(\n resp: OAuth2TokenEndpointResponse,\n type: \"signIn\" | \"refresh\",\n ): Token {\n const { refresh_token, expires_in, access_token } = resp;\n invariant(expires_in != null);\n saveLocal(client, {\n refresh_token,\n refreshTokenMarker,\n requestedScopes: scopes,\n });\n token = {\n refresh_token,\n expires_in,\n access_token,\n expires_at: Date.now() + expires_in * 1000,\n };\n\n eventTarget.dispatchTypedEvent(\n type,\n new CustomEvent(\n type,\n { detail: token },\n ),\n );\n return token;\n }\n\n let refreshTimeout: ReturnType<typeof setTimeout>;\n function rmTimeout() {\n if (refreshTimeout) clearTimeout(refreshTimeout);\n }\n function restartRefreshTimer(evt: CustomEvent<Token>) {\n if (refresh) {\n rmTimeout();\n refreshTimeout = setTimeout(\n refresh,\n evt.detail.expires_in * 1000 - 60 * 1000,\n );\n }\n }\n\n async function signOut() {\n invariant(token, \"not signed in\");\n\n const result = await processRevocationResponse(\n await revocationRequest(\n as,\n client,\n token.access_token,\n oauthHttpOptions,\n ),\n );\n\n rmTimeout();\n\n // Clean up\n removeLocal(client);\n token = undefined;\n throwIfError(result);\n eventTarget.dispatchTypedEvent(\"signOut\", new Event(\"signOut\"));\n }\n\n let pendingSignIn: Promise<Token> | undefined;\n async function signIn() {\n if (pendingSignIn) {\n return pendingSignIn;\n }\n try {\n pendingSignIn = _signIn();\n return await pendingSignIn;\n } finally {\n pendingSignIn = undefined;\n }\n }\n\n eventTarget.addEventListener(\"signIn\", restartRefreshTimer);\n eventTarget.addEventListener(\"refresh\", restartRefreshTimer);\n\n function getTokenOrUndefined() {\n if (!token || Date.now() >= token.expires_at) {\n return undefined;\n }\n return token?.access_token;\n }\n\n const getToken = Object.assign(async function getToken() {\n if (!token || Date.now() >= token.expires_at) {\n token = await signIn();\n }\n return token?.access_token;\n }, {\n signIn,\n refresh,\n signOut,\n rmTimeout,\n getTokenOrUndefined,\n addEventListener: eventTarget.addEventListener.bind(\n eventTarget,\n ) as typeof eventTarget.addEventListener,\n removeEventListener: eventTarget.removeEventListener.bind(\n eventTarget,\n ) as typeof eventTarget.removeEventListener,\n });\n\n return { getToken, makeTokenAndSaveRefresh };\n}\n\nexport function createAuthorizationServer(\n ctxPath: string,\n url: string,\n): Required<\n Pick<\n AuthorizationServer,\n | \"issuer\"\n | \"token_endpoint\"\n | \"authorization_endpoint\"\n | \"revocation_endpoint\"\n >\n> {\n const issuer = `${new URL(ctxPath, url.endsWith(\"/\") ? url : url + \"/\")}`;\n return {\n token_endpoint: `${issuer}/api/oauth2/token`,\n authorization_endpoint: `${issuer}/api/oauth2/authorize`,\n revocation_endpoint: `${issuer}/api/oauth2/revoke_token`,\n issuer,\n };\n}\n"],"mappings":"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","ignoreList":[]}
@@ -40,7 +40,7 @@ describe("local functions", () => {
40
40
  expect(vi.mocked(globalThis.localStorage.setItem)).toBeCalledWith(oldLocalStorageKey, expect.anything());
41
41
  expect(vi.mocked(globalThis.localStorage.removeItem)).toBeCalledWith(oldLocalStorageKey);
42
42
  });
43
- it("should save the refreshTokenMaker if it exists", () => {
43
+ it("should save the refreshTokenMaker if it exists, as well as the requested scopes", () => {
44
44
  const client = {
45
45
  client_id: "hi_mom"
46
46
  };
@@ -49,7 +49,7 @@ describe("local functions", () => {
49
49
  const authServer = createAuthorizationServer("multipass", "https://stack.palantir.com");
50
50
  const {
51
51
  makeTokenAndSaveRefresh
52
- } = common(client, authServer, signIn, {}, refresh, "marker marker");
52
+ } = common(client, authServer, signIn, {}, refresh, "marker marker", "yay:my-fun-scope sad:my-boring-scope");
53
53
  makeTokenAndSaveRefresh({
54
54
  refresh_token: "refresh",
55
55
  access_token: "access",
@@ -58,7 +58,8 @@ describe("local functions", () => {
58
58
  }, "signIn");
59
59
  expect(globalThis.localStorage.setItem).toBeCalledWith(`@osdk/oauth : refresh : ${client.client_id}`, JSON.stringify({
60
60
  refresh_token: "refresh",
61
- refreshTokenMarker: "marker marker"
61
+ refreshTokenMarker: "marker marker",
62
+ requestedScopes: "yay:my-fun-scope sad:my-boring-scope"
62
63
  }));
63
64
  });
64
65
  });
@@ -1 +1 @@
1
- {"version":3,"file":"common.test.js","names":["afterEach","beforeEach","describe","expect","it","vi","common","createAuthorizationServer","readLocal","removeLocal","saveLocal","stubGlobal","setItem","fn","removeItem","getItem","unstubAllGlobals","client","client_id","$refreshTokenMarker","undefined","oldLocalStorageKey","mocked","globalThis","localStorage","toBeCalledWith","anything","signIn","refresh","authServer","makeTokenAndSaveRefresh","refresh_token","access_token","token_type","expires_in","JSON","stringify","refreshTokenMarker"],"sources":["common.test.ts"],"sourcesContent":["/*\n * Copyright 2025 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 { afterEach, beforeEach, describe, expect, it, vi } from \"vitest\";\nimport {\n common,\n createAuthorizationServer,\n readLocal,\n removeLocal,\n saveLocal,\n} from \"./common.js\";\n\ndescribe(\"local functions\", () => {\n beforeEach(() => {\n vi.stubGlobal(\"localStorage\", {\n setItem: vi.fn(),\n removeItem: vi.fn(),\n getItem: vi.fn(),\n });\n });\n\n afterEach(() => {\n vi.unstubAllGlobals();\n });\n\n it(\"should use the old style keys when there is no refresh token marker\", () => {\n const client = {\n client_id: \"hi_mom\",\n $refreshTokenMarker: undefined,\n };\n\n const oldLocalStorageKey = `@osdk/oauth : refresh : ${client.client_id}`;\n\n readLocal(client);\n removeLocal(client);\n saveLocal(client, {});\n\n expect(vi.mocked(globalThis.localStorage.getItem)).toBeCalledWith(\n oldLocalStorageKey,\n );\n\n expect(vi.mocked(globalThis.localStorage.setItem)).toBeCalledWith(\n oldLocalStorageKey,\n expect.anything(),\n );\n\n expect(vi.mocked(globalThis.localStorage.removeItem)).toBeCalledWith(\n oldLocalStorageKey,\n );\n });\n\n it(\"should save the refreshTokenMaker if it exists\", () => {\n const client = {\n client_id: \"hi_mom\",\n };\n\n const signIn = vi.fn();\n const refresh = vi.fn();\n\n const authServer = createAuthorizationServer(\n \"multipass\",\n \"https://stack.palantir.com\",\n );\n\n const { makeTokenAndSaveRefresh } = common(\n client,\n authServer,\n signIn,\n {},\n refresh,\n \"marker marker\",\n );\n\n makeTokenAndSaveRefresh({\n refresh_token: \"refresh\",\n access_token: \"access\",\n token_type: \"idk\",\n expires_in: 10_000,\n }, \"signIn\");\n\n expect(globalThis.localStorage.setItem).toBeCalledWith(\n `@osdk/oauth : refresh : ${client.client_id}`,\n JSON.stringify({\n refresh_token: \"refresh\",\n refreshTokenMarker: \"marker marker\",\n }),\n );\n });\n});\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,SAASA,SAAS,EAAEC,UAAU,EAAEC,QAAQ,EAAEC,MAAM,EAAEC,EAAE,EAAEC,EAAE,QAAQ,QAAQ;AACxE,SACEC,MAAM,EACNC,yBAAyB,EACzBC,SAAS,EACTC,WAAW,EACXC,SAAS,QACJ,aAAa;AAEpBR,QAAQ,CAAC,iBAAiB,EAAE,MAAM;EAChCD,UAAU,CAAC,MAAM;IACfI,EAAE,CAACM,UAAU,CAAC,cAAc,EAAE;MAC5BC,OAAO,EAAEP,EAAE,CAACQ,EAAE,CAAC,CAAC;MAChBC,UAAU,EAAET,EAAE,CAACQ,EAAE,CAAC,CAAC;MACnBE,OAAO,EAAEV,EAAE,CAACQ,EAAE,CAAC;IACjB,CAAC,CAAC;EACJ,CAAC,CAAC;EAEFb,SAAS,CAAC,MAAM;IACdK,EAAE,CAACW,gBAAgB,CAAC,CAAC;EACvB,CAAC,CAAC;EAEFZ,EAAE,CAAC,qEAAqE,EAAE,MAAM;IAC9E,MAAMa,MAAM,GAAG;MACbC,SAAS,EAAE,QAAQ;MACnBC,mBAAmB,EAAEC;IACvB,CAAC;IAED,MAAMC,kBAAkB,GAAG,2BAA2BJ,MAAM,CAACC,SAAS,EAAE;IAExEV,SAAS,CAACS,MAAM,CAAC;IACjBR,WAAW,CAACQ,MAAM,CAAC;IACnBP,SAAS,CAACO,MAAM,EAAE,CAAC,CAAC,CAAC;IAErBd,MAAM,CAACE,EAAE,CAACiB,MAAM,CAACC,UAAU,CAACC,YAAY,CAACT,OAAO,CAAC,CAAC,CAACU,cAAc,CAC/DJ,kBACF,CAAC;IAEDlB,MAAM,CAACE,EAAE,CAACiB,MAAM,CAACC,UAAU,CAACC,YAAY,CAACZ,OAAO,CAAC,CAAC,CAACa,cAAc,CAC/DJ,kBAAkB,EAClBlB,MAAM,CAACuB,QAAQ,CAAC,CAClB,CAAC;IAEDvB,MAAM,CAACE,EAAE,CAACiB,MAAM,CAACC,UAAU,CAACC,YAAY,CAACV,UAAU,CAAC,CAAC,CAACW,cAAc,CAClEJ,kBACF,CAAC;EACH,CAAC,CAAC;EAEFjB,EAAE,CAAC,gDAAgD,EAAE,MAAM;IACzD,MAAMa,MAAM,GAAG;MACbC,SAAS,EAAE;IACb,CAAC;IAED,MAAMS,MAAM,GAAGtB,EAAE,CAACQ,EAAE,CAAC,CAAC;IACtB,MAAMe,OAAO,GAAGvB,EAAE,CAACQ,EAAE,CAAC,CAAC;IAEvB,MAAMgB,UAAU,GAAGtB,yBAAyB,CAC1C,WAAW,EACX,4BACF,CAAC;IAED,MAAM;MAAEuB;IAAwB,CAAC,GAAGxB,MAAM,CACxCW,MAAM,EACNY,UAAU,EACVF,MAAM,EACN,CAAC,CAAC,EACFC,OAAO,EACP,eACF,CAAC;IAEDE,uBAAuB,CAAC;MACtBC,aAAa,EAAE,SAAS;MACxBC,YAAY,EAAE,QAAQ;MACtBC,UAAU,EAAE,KAAK;MACjBC,UAAU,EAAE;IACd,CAAC,EAAE,QAAQ,CAAC;IAEZ/B,MAAM,CAACoB,UAAU,CAACC,YAAY,CAACZ,OAAO,CAAC,CAACa,cAAc,CACpD,2BAA2BR,MAAM,CAACC,SAAS,EAAE,EAC7CiB,IAAI,CAACC,SAAS,CAAC;MACbL,aAAa,EAAE,SAAS;MACxBM,kBAAkB,EAAE;IACtB,CAAC,CACH,CAAC;EACH,CAAC,CAAC;AACJ,CAAC,CAAC","ignoreList":[]}
1
+ {"version":3,"file":"common.test.js","names":["afterEach","beforeEach","describe","expect","it","vi","common","createAuthorizationServer","readLocal","removeLocal","saveLocal","stubGlobal","setItem","fn","removeItem","getItem","unstubAllGlobals","client","client_id","$refreshTokenMarker","undefined","oldLocalStorageKey","mocked","globalThis","localStorage","toBeCalledWith","anything","signIn","refresh","authServer","makeTokenAndSaveRefresh","refresh_token","access_token","token_type","expires_in","JSON","stringify","refreshTokenMarker","requestedScopes"],"sources":["common.test.ts"],"sourcesContent":["/*\n * Copyright 2025 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 { afterEach, beforeEach, describe, expect, it, vi } from \"vitest\";\nimport {\n common,\n createAuthorizationServer,\n readLocal,\n removeLocal,\n saveLocal,\n} from \"./common.js\";\n\ndescribe(\"local functions\", () => {\n beforeEach(() => {\n vi.stubGlobal(\"localStorage\", {\n setItem: vi.fn(),\n removeItem: vi.fn(),\n getItem: vi.fn(),\n });\n });\n\n afterEach(() => {\n vi.unstubAllGlobals();\n });\n\n it(\"should use the old style keys when there is no refresh token marker\", () => {\n const client = {\n client_id: \"hi_mom\",\n $refreshTokenMarker: undefined,\n };\n\n const oldLocalStorageKey = `@osdk/oauth : refresh : ${client.client_id}`;\n\n readLocal(client);\n removeLocal(client);\n saveLocal(client, {});\n\n expect(vi.mocked(globalThis.localStorage.getItem)).toBeCalledWith(\n oldLocalStorageKey,\n );\n\n expect(vi.mocked(globalThis.localStorage.setItem)).toBeCalledWith(\n oldLocalStorageKey,\n expect.anything(),\n );\n\n expect(vi.mocked(globalThis.localStorage.removeItem)).toBeCalledWith(\n oldLocalStorageKey,\n );\n });\n\n it(\"should save the refreshTokenMaker if it exists, as well as the requested scopes\", () => {\n const client = {\n client_id: \"hi_mom\",\n };\n\n const signIn = vi.fn();\n const refresh = vi.fn();\n\n const authServer = createAuthorizationServer(\n \"multipass\",\n \"https://stack.palantir.com\",\n );\n\n const { makeTokenAndSaveRefresh } = common(\n client,\n authServer,\n signIn,\n {},\n refresh,\n \"marker marker\",\n \"yay:my-fun-scope sad:my-boring-scope\",\n );\n\n makeTokenAndSaveRefresh({\n refresh_token: \"refresh\",\n access_token: \"access\",\n token_type: \"idk\",\n expires_in: 10_000,\n }, \"signIn\");\n\n expect(globalThis.localStorage.setItem).toBeCalledWith(\n `@osdk/oauth : refresh : ${client.client_id}`,\n JSON.stringify({\n refresh_token: \"refresh\",\n refreshTokenMarker: \"marker marker\",\n requestedScopes: \"yay:my-fun-scope sad:my-boring-scope\",\n }),\n );\n });\n});\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,SAASA,SAAS,EAAEC,UAAU,EAAEC,QAAQ,EAAEC,MAAM,EAAEC,EAAE,EAAEC,EAAE,QAAQ,QAAQ;AACxE,SACEC,MAAM,EACNC,yBAAyB,EACzBC,SAAS,EACTC,WAAW,EACXC,SAAS,QACJ,aAAa;AAEpBR,QAAQ,CAAC,iBAAiB,EAAE,MAAM;EAChCD,UAAU,CAAC,MAAM;IACfI,EAAE,CAACM,UAAU,CAAC,cAAc,EAAE;MAC5BC,OAAO,EAAEP,EAAE,CAACQ,EAAE,CAAC,CAAC;MAChBC,UAAU,EAAET,EAAE,CAACQ,EAAE,CAAC,CAAC;MACnBE,OAAO,EAAEV,EAAE,CAACQ,EAAE,CAAC;IACjB,CAAC,CAAC;EACJ,CAAC,CAAC;EAEFb,SAAS,CAAC,MAAM;IACdK,EAAE,CAACW,gBAAgB,CAAC,CAAC;EACvB,CAAC,CAAC;EAEFZ,EAAE,CAAC,qEAAqE,EAAE,MAAM;IAC9E,MAAMa,MAAM,GAAG;MACbC,SAAS,EAAE,QAAQ;MACnBC,mBAAmB,EAAEC;IACvB,CAAC;IAED,MAAMC,kBAAkB,GAAG,2BAA2BJ,MAAM,CAACC,SAAS,EAAE;IAExEV,SAAS,CAACS,MAAM,CAAC;IACjBR,WAAW,CAACQ,MAAM,CAAC;IACnBP,SAAS,CAACO,MAAM,EAAE,CAAC,CAAC,CAAC;IAErBd,MAAM,CAACE,EAAE,CAACiB,MAAM,CAACC,UAAU,CAACC,YAAY,CAACT,OAAO,CAAC,CAAC,CAACU,cAAc,CAC/DJ,kBACF,CAAC;IAEDlB,MAAM,CAACE,EAAE,CAACiB,MAAM,CAACC,UAAU,CAACC,YAAY,CAACZ,OAAO,CAAC,CAAC,CAACa,cAAc,CAC/DJ,kBAAkB,EAClBlB,MAAM,CAACuB,QAAQ,CAAC,CAClB,CAAC;IAEDvB,MAAM,CAACE,EAAE,CAACiB,MAAM,CAACC,UAAU,CAACC,YAAY,CAACV,UAAU,CAAC,CAAC,CAACW,cAAc,CAClEJ,kBACF,CAAC;EACH,CAAC,CAAC;EAEFjB,EAAE,CAAC,iFAAiF,EAAE,MAAM;IAC1F,MAAMa,MAAM,GAAG;MACbC,SAAS,EAAE;IACb,CAAC;IAED,MAAMS,MAAM,GAAGtB,EAAE,CAACQ,EAAE,CAAC,CAAC;IACtB,MAAMe,OAAO,GAAGvB,EAAE,CAACQ,EAAE,CAAC,CAAC;IAEvB,MAAMgB,UAAU,GAAGtB,yBAAyB,CAC1C,WAAW,EACX,4BACF,CAAC;IAED,MAAM;MAAEuB;IAAwB,CAAC,GAAGxB,MAAM,CACxCW,MAAM,EACNY,UAAU,EACVF,MAAM,EACN,CAAC,CAAC,EACFC,OAAO,EACP,eAAe,EACf,sCACF,CAAC;IAEDE,uBAAuB,CAAC;MACtBC,aAAa,EAAE,SAAS;MACxBC,YAAY,EAAE,QAAQ;MACtBC,UAAU,EAAE,KAAK;MACjBC,UAAU,EAAE;IACd,CAAC,EAAE,QAAQ,CAAC;IAEZ/B,MAAM,CAACoB,UAAU,CAACC,YAAY,CAACZ,OAAO,CAAC,CAACa,cAAc,CACpD,2BAA2BR,MAAM,CAACC,SAAS,EAAE,EAC7CiB,IAAI,CAACC,SAAS,CAAC;MACbL,aAAa,EAAE,SAAS;MACxBM,kBAAkB,EAAE,eAAe;MACnCC,eAAe,EAAE;IACnB,CAAC,CACH,CAAC;EACH,CAAC,CAAC;AACJ,CAAC,CAAC","ignoreList":[]}
@@ -36,14 +36,15 @@ export function createConfidentialOauthClient(client_id, client_secret, url, sco
36
36
  const oauthHttpOptions = {
37
37
  [customFetch]: fetchFn
38
38
  };
39
+ const joinedScopes = scopes.join(" ");
39
40
  const {
40
41
  getToken,
41
42
  makeTokenAndSaveRefresh
42
43
  } = common(client, authServer, async function () {
43
44
  return makeTokenAndSaveRefresh(throwIfError(await processClientCredentialsResponse(authServer, client, await clientCredentialsGrantRequest(authServer, client, new URLSearchParams({
44
- scope: scopes.join(" ")
45
+ scope: joinedScopes
45
46
  }), oauthHttpOptions))), "signIn");
46
- }, oauthHttpOptions, undefined, undefined);
47
+ }, oauthHttpOptions, undefined, undefined, joinedScopes);
47
48
  return getToken;
48
49
  }
49
50
  //# sourceMappingURL=createConfidentialOauthClient.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"createConfidentialOauthClient.js","names":["clientCredentialsGrantRequest","customFetch","processClientCredentialsResponse","common","createAuthorizationServer","throwIfError","createConfidentialOauthClient","client_id","client_secret","url","scopes","fetchFn","globalThis","fetch","ctxPath","client","authServer","oauthHttpOptions","getToken","makeTokenAndSaveRefresh","URLSearchParams","scope","join","undefined"],"sources":["createConfidentialOauthClient.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 type { Client, HttpRequestOptions } from \"oauth4webapi\";\nimport {\n clientCredentialsGrantRequest,\n customFetch,\n processClientCredentialsResponse,\n} from \"oauth4webapi\";\nimport { common, createAuthorizationServer } from \"./common.js\";\nimport type { ConfidentialOauthClient } from \"./ConfidentialOauthClient.js\";\nimport { throwIfError } from \"./throwIfError.js\";\n\n/**\n * @param client_id\n * @param client_secret\n * @param url the base url of your foundry server\n * @param scopes\n * @param fetchFn\n * @param ctxPath\n * @returns which can be used as a token provider\n */\nexport function createConfidentialOauthClient(\n client_id: string,\n client_secret: string,\n url: string,\n scopes: string[] = [\"api:read-data\", \"api:write-data\"],\n fetchFn: typeof globalThis.fetch = globalThis.fetch,\n ctxPath: string = \"multipass\",\n): ConfidentialOauthClient {\n const client: Client = { client_id, client_secret };\n const authServer = createAuthorizationServer(ctxPath, url);\n const oauthHttpOptions: HttpRequestOptions = { [customFetch]: fetchFn };\n\n const { getToken, makeTokenAndSaveRefresh } = common(\n client,\n authServer,\n _signIn,\n oauthHttpOptions,\n undefined,\n undefined,\n );\n\n async function _signIn() {\n return makeTokenAndSaveRefresh(\n throwIfError(\n await processClientCredentialsResponse(\n authServer,\n client,\n await clientCredentialsGrantRequest(\n authServer,\n client,\n new URLSearchParams({ scope: scopes.join(\" \") }),\n oauthHttpOptions,\n ),\n ),\n ),\n \"signIn\",\n );\n }\n\n return getToken;\n}\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAGA,SACEA,6BAA6B,EAC7BC,WAAW,EACXC,gCAAgC,QAC3B,cAAc;AACrB,SAASC,MAAM,EAAEC,yBAAyB,QAAQ,aAAa;AAE/D,SAASC,YAAY,QAAQ,mBAAmB;;AAEhD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,6BAA6BA,CAC3CC,SAAiB,EACjBC,aAAqB,EACrBC,GAAW,EACXC,MAAgB,GAAG,CAAC,eAAe,EAAE,gBAAgB,CAAC,EACtDC,OAAgC,GAAGC,UAAU,CAACC,KAAK,EACnDC,OAAe,GAAG,WAAW,EACJ;EACzB,MAAMC,MAAc,GAAG;IAAER,SAAS;IAAEC;EAAc,CAAC;EACnD,MAAMQ,UAAU,GAAGZ,yBAAyB,CAACU,OAAO,EAAEL,GAAG,CAAC;EAC1D,MAAMQ,gBAAoC,GAAG;IAAE,CAAChB,WAAW,GAAGU;EAAQ,CAAC;EAEvE,MAAM;IAAEO,QAAQ;IAAEC;EAAwB,CAAC,GAAGhB,MAAM,CAClDY,MAAM,EACNC,UAAU,EAOZ,kBAAyB;IACvB,OAAOG,uBAAuB,CAC5Bd,YAAY,CACV,MAAMH,gCAAgC,CACpCc,UAAU,EACVD,MAAM,EACN,MAAMf,6BAA6B,CACjCgB,UAAU,EACVD,MAAM,EACN,IAAIK,eAAe,CAAC;MAAEC,KAAK,EAAEX,MAAM,CAACY,IAAI,CAAC,GAAG;IAAE,CAAC,CAAC,EAChDL,gBACF,CACF,CACF,CAAC,EACD,QACF,CAAC;EACH,CAAC,EArBCA,gBAAgB,EAChBM,SAAS,EACTA,SACF,CAAC;EAoBD,OAAOL,QAAQ;AACjB","ignoreList":[]}
1
+ {"version":3,"file":"createConfidentialOauthClient.js","names":["clientCredentialsGrantRequest","customFetch","processClientCredentialsResponse","common","createAuthorizationServer","throwIfError","createConfidentialOauthClient","client_id","client_secret","url","scopes","fetchFn","globalThis","fetch","ctxPath","client","authServer","oauthHttpOptions","joinedScopes","join","getToken","makeTokenAndSaveRefresh","URLSearchParams","scope","undefined"],"sources":["createConfidentialOauthClient.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 type { Client, HttpRequestOptions } from \"oauth4webapi\";\nimport {\n clientCredentialsGrantRequest,\n customFetch,\n processClientCredentialsResponse,\n} from \"oauth4webapi\";\nimport { common, createAuthorizationServer } from \"./common.js\";\nimport type { ConfidentialOauthClient } from \"./ConfidentialOauthClient.js\";\nimport { throwIfError } from \"./throwIfError.js\";\n\n/**\n * @param client_id\n * @param client_secret\n * @param url the base url of your foundry server\n * @param scopes\n * @param fetchFn\n * @param ctxPath\n * @returns which can be used as a token provider\n */\nexport function createConfidentialOauthClient(\n client_id: string,\n client_secret: string,\n url: string,\n scopes: string[] = [\"api:read-data\", \"api:write-data\"],\n fetchFn: typeof globalThis.fetch = globalThis.fetch,\n ctxPath: string = \"multipass\",\n): ConfidentialOauthClient {\n const client: Client = { client_id, client_secret };\n const authServer = createAuthorizationServer(ctxPath, url);\n const oauthHttpOptions: HttpRequestOptions = { [customFetch]: fetchFn };\n const joinedScopes = scopes.join(\" \");\n\n const { getToken, makeTokenAndSaveRefresh } = common(\n client,\n authServer,\n _signIn,\n oauthHttpOptions,\n undefined,\n undefined,\n joinedScopes,\n );\n\n async function _signIn() {\n return makeTokenAndSaveRefresh(\n throwIfError(\n await processClientCredentialsResponse(\n authServer,\n client,\n await clientCredentialsGrantRequest(\n authServer,\n client,\n new URLSearchParams({ scope: joinedScopes }),\n oauthHttpOptions,\n ),\n ),\n ),\n \"signIn\",\n );\n }\n\n return getToken;\n}\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAGA,SACEA,6BAA6B,EAC7BC,WAAW,EACXC,gCAAgC,QAC3B,cAAc;AACrB,SAASC,MAAM,EAAEC,yBAAyB,QAAQ,aAAa;AAE/D,SAASC,YAAY,QAAQ,mBAAmB;;AAEhD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,6BAA6BA,CAC3CC,SAAiB,EACjBC,aAAqB,EACrBC,GAAW,EACXC,MAAgB,GAAG,CAAC,eAAe,EAAE,gBAAgB,CAAC,EACtDC,OAAgC,GAAGC,UAAU,CAACC,KAAK,EACnDC,OAAe,GAAG,WAAW,EACJ;EACzB,MAAMC,MAAc,GAAG;IAAER,SAAS;IAAEC;EAAc,CAAC;EACnD,MAAMQ,UAAU,GAAGZ,yBAAyB,CAACU,OAAO,EAAEL,GAAG,CAAC;EAC1D,MAAMQ,gBAAoC,GAAG;IAAE,CAAChB,WAAW,GAAGU;EAAQ,CAAC;EACvE,MAAMO,YAAY,GAAGR,MAAM,CAACS,IAAI,CAAC,GAAG,CAAC;EAErC,MAAM;IAAEC,QAAQ;IAAEC;EAAwB,CAAC,GAAGlB,MAAM,CAClDY,MAAM,EACNC,UAAU,EAQZ,kBAAyB;IACvB,OAAOK,uBAAuB,CAC5BhB,YAAY,CACV,MAAMH,gCAAgC,CACpCc,UAAU,EACVD,MAAM,EACN,MAAMf,6BAA6B,CACjCgB,UAAU,EACVD,MAAM,EACN,IAAIO,eAAe,CAAC;MAAEC,KAAK,EAAEL;IAAa,CAAC,CAAC,EAC5CD,gBACF,CACF,CACF,CAAC,EACD,QACF,CAAC;EACH,CAAC,EAtBCA,gBAAgB,EAChBO,SAAS,EACTA,SAAS,EACTN,YACF,CAAC;EAoBD,OAAOE,QAAQ;AACjB","ignoreList":[]}
@@ -47,11 +47,12 @@ import { processOptionsAndAssignDefaults } from "./utils.js";
47
47
 
48
48
  export function createPublicOauthClient(client_id, url, redirect_uri, useHistory, loginPage, postLoginPage, scopes, fetchFn, ctxPath) {
49
49
  let refreshTokenMarker;
50
+ let joinedScopes;
50
51
  ({
51
52
  useHistory,
52
53
  loginPage,
53
54
  postLoginPage,
54
- scopes,
55
+ joinedScopes,
55
56
  fetchFn,
56
57
  ctxPath,
57
58
  refreshTokenMarker
@@ -75,7 +76,7 @@ export function createPublicOauthClient(client_id, url, redirect_uri, useHistory
75
76
  ) ?? (await maybeHandleAuthReturn())
76
77
  // 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
77
78
  ?? (await initiateLoginRedirect());
78
- }, oauthHttpOptions, maybeRefresh.bind(globalThis, true), refreshTokenMarker);
79
+ }, oauthHttpOptions, maybeRefresh.bind(globalThis, true), refreshTokenMarker, joinedScopes);
79
80
 
80
81
  // as an arrow function, `useHistory` is known to be a boolean
81
82
  const go = async x => {
@@ -89,9 +90,11 @@ export function createPublicOauthClient(client_id, url, redirect_uri, useHistory
89
90
  async function maybeRefresh(expectRefreshToken) {
90
91
  const {
91
92
  refresh_token,
92
- refreshTokenMarker: lastRefreshTokenMarker
93
+ refreshTokenMarker: lastRefreshTokenMarker,
94
+ requestedScopes: initialRequestedScopes
93
95
  } = readLocal(client);
94
- if (!refresh_token || lastRefreshTokenMarker !== refreshTokenMarker) {
96
+ const areScopesEqual = initialRequestedScopes != null && joinedScopes === initialRequestedScopes;
97
+ if (!refresh_token || lastRefreshTokenMarker !== refreshTokenMarker || !areScopesEqual) {
95
98
  if (expectRefreshToken) throw new Error("No refresh token found");
96
99
  return;
97
100
  }
@@ -166,7 +169,7 @@ export function createPublicOauthClient(client_id, url, redirect_uri, useHistory
166
169
  redirect_uri,
167
170
  code_challenge: await calculatePKCECodeChallenge(codeVerifier),
168
171
  code_challenge_method: "S256",
169
- scope: ["offline_access", ...scopes].join(" ")
172
+ scope: `offline_access ${joinedScopes}`
170
173
  })}`);
171
174
 
172
175
  // Give time for redirect to happen
@@ -1 +1 @@
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":[]}
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","joinedScopes","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","requestedScopes","initialRequestedScopes","areScopesEqual","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"],"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 let joinedScopes: string;\n ({\n useHistory,\n loginPage,\n postLoginPage,\n joinedScopes,\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 joinedScopes,\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 {\n refresh_token,\n refreshTokenMarker: lastRefreshTokenMarker,\n requestedScopes: initialRequestedScopes,\n } = readLocal(client);\n\n const areScopesEqual = initialRequestedScopes != null\n && joinedScopes === initialRequestedScopes;\n\n if (\n !refresh_token || lastRefreshTokenMarker !== refreshTokenMarker\n || !areScopesEqual\n ) {\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 ${joinedScopes}`,\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":[]}
@@ -133,33 +133,60 @@ describe(createPublicOauthClient, () => {
133
133
  redirect_uri: clientArgs.redirectUrl,
134
134
  response_type: "code",
135
135
  code_challenge_method: "S256",
136
- scope: "offline_access api:read-data api:write-data",
136
+ scope: ["offline_access", ...(clientArgs.scopes ?? ["api:read-data", "api:write-data"].sort())].join(" "),
137
137
  state: expect.any(String)
138
138
  }));
139
139
  }
140
- describe("refresh token marker", () => {
140
+ describe("refresh token marker and scopes", () => {
141
141
  beforeEach(() => {
142
142
  setupWindowLocation("https://foundry.local");
143
143
  });
144
+
145
+ // Backwards compatibility from when we didn't store scopes
146
+ it("should not allow refresh if scopes in local storage is undefined", async () => {
147
+ setupLocalState({
148
+ refresh_token: "refreshToken"
149
+ });
150
+ setupClient({
151
+ ...BASE_CLIENT_ARGS
152
+ // Testing with default scopes
153
+ });
154
+ await expectRedirectToMultipass(client());
155
+ });
156
+ it("should not allow refresh if requested scopes are different", async () => {
157
+ setupLocalState({
158
+ refresh_token: "refreshToken",
159
+ requestedScopes: "api:read-data"
160
+ });
161
+ setupClient({
162
+ ...BASE_CLIENT_ARGS,
163
+ scopes: ["api:write-data"]
164
+ });
165
+ await expectRedirectToMultipass(client());
166
+ });
144
167
  it("should not allow refresh if the refresh markers do not match", async () => {
145
168
  setupLocalState({
146
169
  refresh_token: "refreshToken",
147
- refreshTokenMarker: "not-the-right-marker"
170
+ refreshTokenMarker: "not-the-right-marker",
171
+ requestedScopes: "api:read-data"
148
172
  });
149
173
  setupClient({
150
174
  ...BASE_CLIENT_ARGS,
151
- refreshTokenMarker: "marker"
175
+ refreshTokenMarker: "marker",
176
+ scopes: ["api:read-data"]
152
177
  });
153
178
  await expectRedirectToMultipass(client());
154
179
  });
155
- it("should try to refresh if refresh tokens do match", async () => {
180
+ it("should try to refresh if refresh markers match and requested scopes are equal", async () => {
156
181
  setupLocalState({
157
182
  refresh_token: "refreshToken",
158
- refreshTokenMarker: "marker"
183
+ refreshTokenMarker: "marker",
184
+ requestedScopes: "api:admin-read api:datasets-read"
159
185
  });
160
186
  setupClient({
161
187
  ...BASE_CLIENT_ARGS,
162
- refreshTokenMarker: "marker"
188
+ refreshTokenMarker: "marker",
189
+ scopes: ["api:datasets-read", "api:admin-read"]
163
190
  });
164
191
  hoistedMocks.makeTokenAndSaveRefresh.mockImplementationOnce(() => ({
165
192
  access_token: "some token",
@@ -233,11 +260,12 @@ describe(createPublicOauthClient, () => {
233
260
  expect(vi.mocked(commonJs.common)).toHaveBeenCalledWith(expect.objectContaining({
234
261
  client_id: clientArgs.clientId,
235
262
  token_endpoint_auth_method: "none"
236
- }), commonJs.createAuthorizationServer(clientArgs.ctxPath ?? "/multipass", clientArgs.foundryUrl), expect.any(Function), expect.anything(), expect.any(Function), undefined);
263
+ }), commonJs.createAuthorizationServer(clientArgs.ctxPath ?? "/multipass", clientArgs.foundryUrl), expect.any(Function), expect.anything(), expect.any(Function), undefined, (clientArgs.scopes ?? ["api:read-data", "api:write-data"]).sort().join(" "));
237
264
  });
238
265
  describe.each([{
239
266
  localStorage: {
240
- refresh_token: "a-refresh-token"
267
+ refresh_token: "a-refresh-token",
268
+ requestedScopes: (clientArgs.scopes ?? ["api:read-data", "api:write-data"]).sort().join(" ")
241
269
  },
242
270
  sessionStorage: {}
243
271
  }, {