You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
-[Delegated login from an external identity provider](#delegated-login-from-an-external-identity-provider)
21
23
-[Integration](#integration)
22
24
-[Other flows](#other-flows)
23
25
-[Webapp application with authorization code flow](#webapp-application-with-authorization-code-flow)
@@ -141,7 +143,7 @@ This library provides a robust token caching system out-of-the-box, with support
141
143
142
144
-**Single storage** — Provides temporary or persistent storage that can survive page reloads. You can implement any storage solution.
143
145
-**Composite storage** — Using the `createCompositeStorage` helper, you can combine multiple storage mechanisms (e.g., memory + Redis) to optimize performance and reduce load on the underlying configured storage.
144
-
-**Customer storage** (*sales channel only*) — Optional dedicated storage for customer authentication tokens, separate from guest tokens.
146
+
-**[Customer storage](#customer-storage)** (*sales channel only*) — Optional dedicated storage for customer authentication tokens, separate from guest tokens.
145
147
146
148
Here below an example showing a basic setup with an in-memory storage:
147
149
@@ -247,6 +249,74 @@ flowchart TB
247
249
class ReturnCustomerToken,ReturnGuestToken endState;
248
250
```
249
251
252
+
#### Customer storage
253
+
254
+
Sales channels deal with two kinds of tokens: the **guest** token, which is not tied to any identity and can be safely shared, and the **customer** token, which is personal. The optional `customerStorage` option lets you store customer tokens separately from guest tokens.
255
+
256
+
By default, authorizations are stored using a key derived from the client ID and scope:
257
+
258
+
```
259
+
cl_${type}-${clientId}-${scope}
260
+
```
261
+
262
+
This works out-of-the-box in the browser, where the storage (e.g. `localStorage`) is already scoped to a single visitor.
263
+
264
+
> [!WARNING]
265
+
> The default key does **not** include any customer identity. If customer tokens are cached in a storage shared by all visitors (e.g. Redis or an in-memory map on the server) under the default key, one customer's token would be served to **every** visitor.
266
+
267
+
There are two ways to store customer tokens safely on the server:
268
+
269
+
1.**Use a per-visitor `customerStorage`.** When the customer storage is scoped to the visitor (e.g. cookies), the default key is perfectly fine, because the storage itself belongs to a single visitor. This pairs naturally with server-side rendered storefronts, where a single guest token can be cached server-side and shared by all visitors:
270
+
271
+
```ts
272
+
const salesChannel =makeSalesChannel(
273
+
{
274
+
clientId: "<your_client_id>",
275
+
scope: "market:code:europe"
276
+
},
277
+
{
278
+
// The guest token carries no identity:
279
+
// one token, cached server-side, serves all visitors...
280
+
storage: memoryStorage(),
281
+
// ...while the customer token is personal
282
+
// and lives in a per-visitor storage (e.g. cookies).
283
+
customerStorage: cookieStorageAdapter,
284
+
},
285
+
)
286
+
```
287
+
288
+
2.**Useasharedstoragewithacustomkey.**Ifyou'd rather cache customer tokens in a shared server-side storage too (e.g. Redis), provide a custom `getKey` function that includes a customer or session identifier. Since `getKey` receives only the client ID, scope, and authorization type, the request context must be captured via closure. Create the helper per request, while keeping the storage backend shared:
289
+
290
+
```ts
291
+
import { createCompositeStorage, makeSalesChannel } from "@commercelayer/js-auth"
292
+
293
+
// Shared, process-level storage (this is where tokens are actually cached).
294
+
const sharedStorage = createCompositeStorage({
295
+
name: "bff",
296
+
storages: [memoryStorage(), redisStorage],
297
+
})
298
+
299
+
// Per-request helper: creating it is cheap (no network calls, no timers).
>Thehelpersreturnedby`makeSalesChannel`and`makeIntegration`arelightweightfactories: theyholdnotokenstatethemselves (tokensliveintheconfiguredstorage) andperformnoI/Oatconstructiontime. It's safe to create one per request, as long as the storage backend is shared. The only thing a long-lived instance adds is in-flight deduplication of concurrent `getAuthorization()` calls.
// 3. Hand the customer token over to the `salesChannel` helper
593
+
// created with `makeSalesChannel`.
594
+
// From now on, `getAuthorization()` returns the customer authorization
595
+
// and refreshes it automatically when it expires.
596
+
await salesChannel.setCustomer({
597
+
accessToken: customerCredentials.accessToken,
598
+
scope: customerCredentials.scope,
599
+
refreshToken: customerCredentials.refreshToken,
600
+
})
601
+
```
602
+
480
603
### Integration
481
604
482
605
[Integrations](https://docs.commercelayer.io/core/api-credentials#integration) are used to develop backend integrations with any 3rd-party system.
@@ -744,7 +867,7 @@ The method requires a valid access token (the token can be used with Provisionin
744
867
745
868
## Contributorsguide
746
869
747
-
1.Fork [thisrepository](https://github.com/BolajiAyodeji/commercelayer-js-auth) (learn how to do this [here](https://help.github.com/articles/fork-a-repo)).
870
+
1.Fork [thisrepository](https://github.com/commercelayer/commercelayer-js-auth) (learn how to do this [here](https://help.github.com/articles/fork-a-repo)).
0 commit comments