-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #99 from commercelayer/get-api-base-endpoint
Add `getProvisioningApiBaseEndpoint` helper method
- Loading branch information
Showing
7 changed files
with
274 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
65 changes: 65 additions & 0 deletions
65
packages/js-auth/src/getProvisioningApiBaseEndpoint.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import { InvalidTokenError } from './errors/InvalidTokenError.js' | ||
import { TokenError } from './errors/TokenError.js' | ||
import { getProvisioningApiBaseEndpoint } from './getProvisioningApiBaseEndpoint.js' | ||
|
||
describe('getProvisioningApiBaseEndpoint', () => { | ||
it('should throw when the access token is not valid.', () => { | ||
const accessToken = | ||
'eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCIsImtpZCI6ImFiYTRjYzYyOGQxZmNlM2ZiOTNhM2VlNTU4MjZlNDFjZmFmMThkYzJkZmYzYjA3MjIyNzQwMzgwZTkxOTlkNWQifQ.eyJvcmdhbml6YXRpb24iOnsiaWQiOiJlbldveEZNT25wIiwic2x1ZyI6InRoZS1ibHVlLWJyYW5kLTMiLCJlbnRlcnByaXNlIjpmYWxzZSwicmVnaW9uIjoiZXUtd2VzdC0xIn0sImFwcGxpY2F0aW9uIjp7ImlkIjoibkdWcWFpRVlOQSIsImNsaWVudF9pZCI6IjF1bEs3ZXdkUUZyak90LVlxZ0RQeXBRRzFYSmVtUGdaUDFvd3NpSG9tRDQiLCJraW5kIjoic2FsZXNfY2hhbm5lbCIsInB1YmxpYyI6dHJ1ZX0sInNjb3BlIjoibWFya2V0OmFsbCIsImV4cCI6MTcyNTM5OTc3OCwidGVzdCI6dHJ1ZSwicmFuZCI6MC4yMTIzMDUzMTE1NDg3Njk5NywiaWF0IjoxNzI1MzkyNTc4LCJpc3MiOiJodHRwczovL2F1dGguY29tbWVyY2VsYXllci5jbyJ9.aOjfJXPDQ-jY__XlZuaAhpboDCGPuaszSMjgBlGJ7ubjWmmHGIvgOewNusTKRcrvhgWsUPeGeBii9SdkxP0tHMejXb0qS8RZ7lAxhvfHlekytdhizLrTUoqAwGckgX9FJTzRh0rA57cFXLSuXXUvB5aUtFZ3CfxZ_YIRWUNSvbAsknYmH1WAy54QjpvM9jFD3iXv3ak9Q9DnxC80N98lgWfgjEgoW67jQSLHQT0r-cgbT8dbVUDOkUp4PnUQkjLWHcoejRFMlORTlQTSGjW8pYsYJyKhUa8FJ9UWcRGp55xIJLmdqbVcdYo0R2ESz32ek-Uu3OTO-JAbABSC-oN5Dzo_BHxS7ct_TcZV9Qxr-fGdZXD0C5PljMqmAQMeSRoZjdSLSLLKq5dyUx714NT9urE_BVLTkZ-jU-15iyS3aWy0qgmMKu3NOBmF_j-P7ohNNjDsdMz2ofZ8r2E_0QBgpC1LwdWt4r0S4eabiTskM4r-wrQSbctZae0veIrKQ4C3k1Wr1wR9b6CvZXr-IqaYg1VilMiffmVhbYl60iWg3IK5REKJFJ2hNm18A78OiBO4A5KiL6lk3N_26C9dJa9HolUbpopiIVgZxXUXRw6EohnU0e5IBnQES-3q9T_37lNgAKlbHpqDKKEWjdwqb3GRMdxzMj0rxELyBRBbx-W8_9s' | ||
|
||
expect(() => getProvisioningApiBaseEndpoint(accessToken)).toThrow( | ||
InvalidTokenError | ||
) | ||
expect(() => getProvisioningApiBaseEndpoint(accessToken)).toThrow( | ||
TokenError | ||
) | ||
expect(() => getProvisioningApiBaseEndpoint(accessToken)).toThrow( | ||
'Invalid token format' | ||
) | ||
}) | ||
|
||
it('should return `null` when the access token does not have an "organization" and shouldThrow option is set to `false`.', () => { | ||
const accessToken = | ||
'eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCIsImtpZCI6ImFiYTRjYzYyOGQxZmNlM2ZiOTNhM2VlNTU4MjZlNDFjZmFmMThkYzJkZmYzYjA3MjIyNzQwMzgwZTkxOTlkNWQifQ.eyJvcmdhbml6YXRpb24iOnsiaWQiOiJlbldveEZNT25wIiwic2x1ZyI6InRoZS1ibHVlLWJyYW5kLTMiLCJlbnRlcnByaXNlIjpmYWxzZSwicmVnaW9uIjoiZXUtd2VzdC0xIn0sImFwcGxpY2F0aW9uIjp7ImlkIjoibkdWcWFpRVlOQSIsImNsaWVudF9pZCI6IjF1bEs3ZXdkUUZyak90LVlxZ0RQeXBRRzFYSmVtUGdaUDFvd3NpSG9tRDQiLCJraW5kIjoic2FsZXNfY2hhbm5lbCIsInB1YmxpYyI6dHJ1ZX0sInNjb3BlIjoibWFya2V0OmFsbCIsImV4cCI6MTcyNTM5OTc3OCwidGVzdCI6dHJ1ZSwicmFuZCI6MC4yMTIzMDUzMTE1NDg3Njk5NywiaWF0IjoxNzI1MzkyNTc4LCJpc3MiOiJodHRwczovL2F1dGguY29tbWVyY2VsYXllci5jbyJ9.aOjfJXPDQ-jY__XlZuaAhpboDCGPuaszSMjgBlGJ7ubjWmmHGIvgOewNusTKRcrvhgWsUPeGeBii9SdkxP0tHMejXb0qS8RZ7lAxhvfHlekytdhizLrTUoqAwGckgX9FJTzRh0rA57cFXLSuXXUvB5aUtFZ3CfxZ_YIRWUNSvbAsknYmH1WAy54QjpvM9jFD3iXv3ak9Q9DnxC80N98lgWfgjEgoW67jQSLHQT0r-cgbT8dbVUDOkUp4PnUQkjLWHcoejRFMlORTlQTSGjW8pYsYJyKhUa8FJ9UWcRGp55xIJLmdqbVcdYo0R2ESz32ek-Uu3OTO-JAbABSC-oN5Dzo_BHxS7ct_TcZV9Qxr-fGdZXD0C5PljMqmAQMeSRoZjdSLSLLKq5dyUx714NT9urE_BVLTkZ-jU-15iyS3aWy0qgmMKu3NOBmF_j-P7ohNNjDsdMz2ofZ8r2E_0QBgpC1LwdWt4r0S4eabiTskM4r-wrQSbctZae0veIrKQ4C3k1Wr1wR9b6CvZXr-IqaYg1VilMiffmVhbYl60iWg3IK5REKJFJ2hNm18A78OiBO4A5KiL6lk3N_26C9dJa9HolUbpopiIVgZxXUXRw6EohnU0e5IBnQES-3q9T_37lNgAKlbHpqDKKEWjdwqb3GRMdxzMj0rxELyBRBbx-W8_9s' | ||
|
||
expect( | ||
getProvisioningApiBaseEndpoint(accessToken, { shouldThrow: false }) | ||
).toBe(null) | ||
}) | ||
|
||
it('should return the provisioning api endpoint when access token contains "provisioning-api" scope.', () => { | ||
const accessToken = | ||
'eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCIsImtpZCI6ImFiYTRjYzYyOGQxZmNlM2ZiOTNhM2VlNTU4MjZlNDFjZmFmMThkYzJkZmYzYjA3MjIyNzQwMzgwZTkxOTlkNWQifQ.eyJ1c2VyIjp7ImlkIjoiZ2Jsb3dTeVZlcSJ9LCJhcHBsaWNhdGlvbiI6eyJpZCI6Im5HVnFhaWxWeU4iLCJraW5kIjoidXNlciIsInB1YmxpYyI6ZmFsc2V9LCJzY29wZSI6InByb3Zpc2lvbmluZy1hcGkiLCJleHAiOjE3MTA5NTA0MzAsInRlc3QiOmZhbHNlLCJyYW5kIjowLjY2OTAzODQ1MzY1NjE5MzMsImlhdCI6MTcxMDk0MzIzMCwiaXNzIjoiaHR0cHM6Ly9hdXRoLmNvbW1lcmNlbGF5ZXIuaW8ifQ.VdTMLw3DD627vI6Xiulo-bKc-yRsAJJ2yNMWC4idbZzzucnKhM0743BzmOrHiMie-z6BkIYLkosKDtrCl08qNFqezQBUAngylyNpwkyh_xg10_oZnmujrf7dCyhWLlk6mw0yOFwGiI9-7D47xnqjB55zSqR5nBqxAD_2ldWM2I29PYBcaTQIk3N6WZWXYI11obtTUoV-B-VVjUTb9Kf9o39TVyh0M4KGmDvI04WCHZd1VNmxskg9w72uAK_0tUc9vQy6xpdRAqeEEd3g71D_y9NEx05OBuWpFEredVGaHjqw6c2WNGfsJxMiDqmhmAqwlOc7ur2zCskWRiYpp-RyXrdTozKKxYngLpMA8lTnT21nes6UUOivtISutHGic2OFFQXdSf-tj4BiQ6YuePoURN48ZJ1zxnDKhg93NluWQX5Ny8n09L22uDDRCDtV8B4tEJ0Z_mAT0UhhQyDWYOTE5DlB67PpBS5M4CIq7CdB8zKawgMputHAI0YmlVIQ9R7BS8EPyuaVRRb0STmg1h0CcJ6BeLiBaFacTd17hMvQPhxuNi6QRSxbzzJiz104e71XPa7eTMJ0BCYjBvvV5pgNGRNlrbQolIm-yDWs7GExzXFIhzw5jbh8i316eZLo4FTprL4UGosnR-3dkTtib0HmHQoIdosjx9yaj8UJhE9--jA' | ||
|
||
expect(getProvisioningApiBaseEndpoint(accessToken)).toEqual( | ||
'https://provisioning.commercelayer.io' | ||
) | ||
}) | ||
|
||
it('should return the provisioning api endpoint when access token contains "provisioning-api" scope (stg env).', () => { | ||
const accessToken = | ||
'eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCIsImtpZCI6ImFiYTRjYzYyOGQxZmNlM2ZiOTNhM2VlNTU4MjZlNDFjZmFmMThkYzJkZmYzYjA3MjIyNzQwMzgwZTkxOTlkNWQifQ.eyJ1c2VyIjp7ImlkIjoiZ2Jsb3dTeVZlcSJ9LCJhcHBsaWNhdGlvbiI6eyJpZCI6Im5HVnFhaWxWeU4iLCJraW5kIjoidXNlciIsInB1YmxpYyI6ZmFsc2V9LCJzY29wZSI6InByb3Zpc2lvbmluZy1hcGkiLCJleHAiOjE3MTA5NTA0MzAsInRlc3QiOmZhbHNlLCJyYW5kIjowLjY2OTAzODQ1MzY1NjE5MzMsImlhdCI6MTcxMDk0MzIzMCwiaXNzIjoiaHR0cHM6Ly9hdXRoLmNvbW1lcmNlbGF5ZXIuY28ifQ.EEOTNia5Er_ObuwC91AxXC1y8ORnQPA4d4i4girU_u1VEyzXdVzdgZXFY0rx993TR4m7b7AOD-O3XJHpQPNENBIDkA6nAiAA0yltsslXzgWoJGzk3xnMCS8jrdOjWmgHXOeMtkRjPiZxdPC_jv705bihqmmjDIg-oT9Ez_p7rlssfE2nkc7-YzOcFRKlFlZqIehWuNNykQ9_fTjYV_L5eaApsRYEXvPIiC4UAWOAOA0liIjFBwS1dMAK3xpnhXjBZV1yFeqqiyV7MXdyRhBernd-Htt72Z3_S9It-ke8vmUyFxKmr9H4OFRn7PNVS_cEQvBk5-8pondWrHl5RlNDMGhXWIzsvAKrjYtUEeLogDphjO9x7oaaC770FkiTlbqV54Mt1IslgoV_2J9tjF9aAln4OoenUxBGFVVcrVPwfoJGuQFUdQ8EkUWf7z6ecZFhtBopSN_-zf9dOeukb--SQ-85gZGX_CJ8EcrKcqbBso2OBc6qrTFHNbXHfInyuRK70iVLjZNlbPDKcKE9Z2CFswmoXz1BY75mWewSFYK04R1IcVhhmrJEZdmGWt3-p22lOp5BMj_EjYDrXDRyGT4LiBIG9W3ovLYun3L_Te5_fGlXk2WzEWoX3WtWKZF9EO9aYWsBFiPKNnUiR0c8-5gj3dUTExkix9p4NwZW_9QlK-E' | ||
|
||
expect(getProvisioningApiBaseEndpoint(accessToken)).toEqual( | ||
'https://provisioning.commercelayer.co' | ||
) | ||
}) | ||
|
||
it('should fallback to "commercelayer.io" when "iss" is expressed in an old format.', () => { | ||
const accessToken = | ||
'eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCIsImtpZCI6ImFiYTRjYzYyOGQxZmNlM2ZiOTNhM2VlNTU4MjZlNDFjZmFmMThkYzJkZmYzYjA3MjIyNzQwMzgwZTkxOTlkNWQifQ.eyJ1c2VyIjp7ImlkIjoiZ2Jsb3dTeVZlcSJ9LCJhcHBsaWNhdGlvbiI6eyJpZCI6Im5HVnFhaWxWeU4iLCJraW5kIjoidXNlciIsInB1YmxpYyI6ZmFsc2V9LCJzY29wZSI6InByb3Zpc2lvbmluZy1hcGkiLCJleHAiOjE3MTA5NTA0MzAsInRlc3QiOmZhbHNlLCJyYW5kIjowLjY2OTAzODQ1MzY1NjE5MzMsImlhdCI6MTcxMDk0MzIzMCwiaXNzIjoiaHR0cHM6Ly9jb21tZXJjZWxheWVyLmlvIn0.NB1PVDXU-CbatAkOKUyKDVo4e1YrracajM1JXUZCnDqGPyD2oMzCQC9ztqtOXrbvlV3FHIWm0yzd8yQvKokFjvPDDH9TvfuWgi_hFN-Dh_7IZBj0tUBfUmF694QfrUOoRfX5OX-jBkRk0IrlYUi2WleiilkSbTV9YdAiLNDWFA1MjeK7YS-QLzrrYL6RsUcII4qrDb7UZZOWiZiXTbZ1HFiSZacrZfu3Eu1BGKVUl8ZhhgYOJ1mCPlVmqn4OTnMfZby8M8Jvo3z7HDbC1-lCWMhoQ7o_PH-duA4DnaMyVrchw1S_3aSmVx6rWykvZ80d9Qz-8oSvqZwhkmnMFvUKvQ' | ||
|
||
expect(getProvisioningApiBaseEndpoint(accessToken)).toEqual( | ||
'https://provisioning.commercelayer.io' | ||
) | ||
}) | ||
|
||
it('should fallback to "commercelayer.io" when "iss" is not defined.', () => { | ||
const accessToken = | ||
'eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCIsImtpZCI6ImFiYTRjYzYyOGQxZmNlM2ZiOTNhM2VlNTU4MjZlNDFjZmFmMThkYzJkZmYzYjA3MjIyNzQwMzgwZTkxOTlkNWQifQ.eyJ1c2VyIjp7ImlkIjoiZ2Jsb3dTeVZlcSJ9LCJhcHBsaWNhdGlvbiI6eyJpZCI6Im5HVnFhaWxWeU4iLCJraW5kIjoidXNlciIsInB1YmxpYyI6ZmFsc2V9LCJzY29wZSI6InByb3Zpc2lvbmluZy1hcGkiLCJleHAiOjE3MTA5NTA0MzAsInRlc3QiOmZhbHNlLCJyYW5kIjowLjY2OTAzODQ1MzY1NjE5MzMsImlhdCI6MTcxMDk0MzIzMH0.XAoaaiIW8QTMc8nwktP3VrAhhw4-zZQRWyGv08B3D91-ajCE71LFJe5lHKjL-0AhMdHwIWa6ZcmoFYQTl72Rp_AitFJeRv978b_6Ep1Qa9XFcf3qjt6DXH4qHfZn2lB7KtvaYANgVHHgGMrcnO8PkaMCY2qSBaD9OJjnZE8013uc1IyUXdvRk-HLTKmGWdjYSXFdl92FfSC-fL0BGva7snUkj0E9pptJxmxVJ3_Vm1M2SP8TSDHo1TTLybGXgZffCVrGRN-kCLkDhgSZlDNvyRyNj604VQJhV3q362z6UQ92LV_wKZVXAWLPXcY3WIp6rEkHOfSXWzxxQVWT82Mm0WLAdsLccc9EIWG3D8_ezpfKfr3HWzz42vny7GwkA9YAo-Cu9tmwmE0_DsFGJpnPUAUqMfC-gCuJMYX1u5aRrqo_j6VQkAhbBZXW77FpZ8FiHObkFYxAtdik2C_eg2gBCz0TBgO3vfJpji--4vLJt9-InRjb1EFy1xjQ4NdM6xaw8eEy1gK4OpyJj1z_jWc80Wm02uUIfeCTrrcrQdcP6wUG7fk60UoHEw8FkQLoRg79TgwE3TcvfVDROqwfmCE9Bu3K_lurWJHojaJV2kSU-xx6X15L7qdSbdvwsQbhhTDAZFscaMTzUiUw6gyYvm0-8wWG7C33AsrSgUWq_TYud3E' | ||
|
||
expect(getProvisioningApiBaseEndpoint(accessToken)).toEqual( | ||
'https://provisioning.commercelayer.io' | ||
) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
import { InvalidTokenError } from './errors/InvalidTokenError.js' | ||
import { jwtDecode } from './jwtDecode.js' | ||
import { extractIssuer } from './utils/extractIssuer.js' | ||
|
||
/** | ||
* Returns the [Provisioning API base endpoint](https://docs.commercelayer.io/provisioning/getting-started/api-specification#base-endpoint) given a valid access token. | ||
* | ||
* @example | ||
* ```ts | ||
* getProvisioningApiBaseEndpoint('eyJhbGciOiJS...') //= "https://provisioning.commercelayer.io" | ||
* ``` | ||
* | ||
* The method requires a valid access token for Provisioning API. | ||
* | ||
* @param accessToken - The access token to decode. | ||
* @param options - An options object to configure behavior. | ||
* @returns The provisioning API base endpoint as a string, or `null` if the token is invalid and `shouldThrow` is `false`. | ||
* @throws InvalidTokenError - If the token is invalid and `shouldThrow` is true. | ||
*/ | ||
export function getProvisioningApiBaseEndpoint( | ||
accessToken: string, | ||
options?: { | ||
/** | ||
* Whether to throw an error if the token is invalid. | ||
* @default true | ||
*/ | ||
shouldThrow?: true | ||
} | ||
): string | ||
|
||
/** | ||
* Returns the [Provisioning API base endpoint](https://docs.commercelayer.io/provisioning/getting-started/api-specification#base-endpoint) given a valid access token. | ||
* | ||
* @example | ||
* ```ts | ||
* getProvisioningApiBaseEndpoint('eyJhbGciOiJS...') //= "https://provisioning.commercelayer.io" | ||
* ``` | ||
* | ||
* The method requires a valid access token for Provisioning API. | ||
* | ||
* @param accessToken - The access token to decode. | ||
* @param options - An options object to configure behavior. | ||
* @returns The provisioning API base endpoint as a string, or `null` if the token is invalid and `shouldThrow` is `false`. | ||
* @throws InvalidTokenError - If the token is invalid and `shouldThrow` is true. | ||
*/ | ||
export function getProvisioningApiBaseEndpoint( | ||
accessToken: string, | ||
options: { | ||
/** | ||
* Whether to throw an error if the token is invalid. | ||
* @default true | ||
*/ | ||
shouldThrow: false | ||
} | ||
): string | null | ||
|
||
export function getProvisioningApiBaseEndpoint( | ||
accessToken: string, | ||
options: { | ||
shouldThrow?: boolean | ||
} = {} | ||
): string | null { | ||
const { shouldThrow = true } = options | ||
const decodedJWT = jwtDecode(accessToken) | ||
|
||
if (!decodedJWT?.payload?.scope?.includes('provisioning-api')) { | ||
if (shouldThrow) { | ||
throw new InvalidTokenError('Invalid token format') | ||
} | ||
|
||
return null | ||
} | ||
|
||
return extractIssuer(decodedJWT).replace('auth', 'provisioning') | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters