From 3eb7c0ff8182a298c71f588423d58dc32fae5765 Mon Sep 17 00:00:00 2001 From: Giuseppe Ciotola <30926550+gciotola@users.noreply.github.com> Date: Tue, 26 Nov 2024 14:02:49 +0100 Subject: [PATCH 1/3] fix: allow to pass more TokenProvider props in `createApp` --- packages/app-elements/src/providers/createApp.tsx | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/packages/app-elements/src/providers/createApp.tsx b/packages/app-elements/src/providers/createApp.tsx index c63ad838..6c44e8a6 100644 --- a/packages/app-elements/src/providers/createApp.tsx +++ b/packages/app-elements/src/providers/createApp.tsx @@ -28,19 +28,12 @@ declare global { } export interface ClAppProps - extends Pick< - TokenProviderProps, - 'organizationSlug' | 'onAppClose' | 'isInDashboard' | 'extras' - > { + extends Partial> { /** * Base path for internal routing. * Example: `my-app` if you want to serve the app at `https://my-domain.com/my-app/`. */ routerBase?: string - /** - * Callback to be called when the user is not authenticated or the token is invalid/expired. - */ - onInvalidAuth?: () => void } /** From 994c9cd4c8c21ef5e1bfebf07c9ffb2f345a1cb4 Mon Sep 17 00:00:00 2001 From: Giuseppe Ciotola <30926550+gciotola@users.noreply.github.com> Date: Tue, 26 Nov 2024 14:10:56 +0100 Subject: [PATCH 2/3] fix: add custom storage methods to retrieve and save access token instead of relying on internal one (localStorage) --- .../providers/TokenProvider/TokenProvider.tsx | 58 ++++++++++++++----- 1 file changed, 43 insertions(+), 15 deletions(-) diff --git a/packages/app-elements/src/providers/TokenProvider/TokenProvider.tsx b/packages/app-elements/src/providers/TokenProvider/TokenProvider.tsx index 1bbeba52..497aab67 100644 --- a/packages/app-elements/src/providers/TokenProvider/TokenProvider.tsx +++ b/packages/app-elements/src/providers/TokenProvider/TokenProvider.tsx @@ -68,11 +68,24 @@ export interface TokenProviderProps { children: ((props: TokenProviderValue) => ReactNode) | ReactNode /** * If present, only an access token with the same organization slug will be considered valid. - * It will be also used to generate the local storage key when the token is persisted. + * It will be also used to generate the local storage key when the token is persisted (unless a custom `storage` methods are provided). * When empty, the app will use the organization slug decoded from the token * and the token will be persisted using a default key ('commercelayer'). */ organizationSlug?: string + /** + * Optional. Override the internal logic to persist the access token and extra (save and retrieve). + */ + storage?: { + /** Custom method to retrieve the access token. It must return a valid and not expired access token */ + getAccessToken: () => string | null + /** Custom method to save the access token in your preferred storage */ + saveAccessToken: (token: string) => void + /** Custom method to retrieve the encoded `extra` data, if provided */ + getEncodedExtra?: () => string | null + /** Custom method to save the encoded `extra` data, if provided, in your preferred storage */ + saveEncodedExtra?: (encodedExtra: string) => void + } /** * The callback invoked when token is not valid. * Can be used to manually handle the re-authentication flow when `reauthenticateOnInvalidAuth` is false. @@ -130,6 +143,7 @@ export const TokenProvider: React.FC = ({ devMode, children, organizationSlug, + storage, onInvalidAuth, loadingElement, errorElement, @@ -139,15 +153,23 @@ export const TokenProvider: React.FC = ({ extras: extrasFromProp }) => { const [_state, dispatch] = useReducer(reducer, initialTokenProviderState) - const accessToken = accessTokenFromProp ?? getAccessTokenFromUrl() ?? - getPersistentJWT({ appSlug, organizationSlug, itemType: 'accessToken' }) + (storage?.getAccessToken != null + ? storage?.getAccessToken() + : getPersistentJWT({ + appSlug, + organizationSlug, + itemType: 'accessToken' + })) const encodeExtras = getExtrasFromUrl() ?? - getPersistentJWT({ appSlug, organizationSlug, itemType: 'extras' }) + (storage?.getEncodedExtra != null + ? storage?.getEncodedExtra() + : getPersistentJWT({ appSlug, organizationSlug, itemType: 'extras' })) + const extras = extrasFromProp ?? decodeExtras(encodeExtras) const apiBaseEndpoint = @@ -228,23 +250,29 @@ export const TokenProvider: React.FC = ({ }) : null - // all good - savePersistentJWT({ - appSlug, - jwt: accessToken, - organizationSlug, - itemType: 'accessToken' - }) - - if (encodeExtras != null) { + // all good - save token using custom storage method (if provided) or fallback to internal method + if (storage != null) { + storage.saveAccessToken(accessToken) + } else { savePersistentJWT({ appSlug, - jwt: encodeExtras, + jwt: accessToken, organizationSlug, - itemType: 'extras' + itemType: 'accessToken' }) } + if (encodeExtras != null) { + storage != null + ? storage.saveEncodedExtra?.(encodeExtras) + : savePersistentJWT({ + appSlug, + jwt: encodeExtras, + organizationSlug, + itemType: 'extras' + }) + } + removeAuthParamsFromUrl() const userFromExtras = From 4b6b980f8725a677f64c768981102eb06645ccf1 Mon Sep 17 00:00:00 2001 From: Giuseppe Ciotola <30926550+gciotola@users.noreply.github.com> Date: Tue, 26 Nov 2024 15:10:26 +0100 Subject: [PATCH 3/3] ci: add pkg-pr-new workflow --- .github/workflows/pkg-pr-new.yaml | 44 +++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 .github/workflows/pkg-pr-new.yaml diff --git a/.github/workflows/pkg-pr-new.yaml b/.github/workflows/pkg-pr-new.yaml new file mode 100644 index 00000000..407efc35 --- /dev/null +++ b/.github/workflows/pkg-pr-new.yaml @@ -0,0 +1,44 @@ +name: pkg-pr-new + +on: + push: + branches: + - "**" + - "!main" + - "!release" + tags-ignore: + - "v*" + pull_request: + types: [ready_for_review] + branches-ignore: + - release + +jobs: + publish: + runs-on: ubuntu-latest + + steps: + - name: Checkout 🛎️ + uses: actions/checkout@v4 + with: + persist-credentials: false + + - name: pnpm 🧰 + uses: pnpm/action-setup@v3 + with: + version: 9.x + + - name: Node 🧰 + uses: actions/setup-node@v4 + with: + node-version: 22.x + + - name: Install 📦 + run: pnpm install --frozen-lockfile + + - name: Build elements 🛠 + run: pnpm build:elements + + - name: Publish 🚀 pkg.pr.new + run: | + npx pkg-pr-new publish './packages/app-elements' --no-template