Skip to content

Commit 37d2c1d

Browse files
feat: reduce debug logging noise and add helper
1 parent a721ca5 commit 37d2c1d

16 files changed

Lines changed: 619 additions & 44 deletions

File tree

examples/nextjs/src/app/api-credentials/client/page.tsx

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
"use client"
22

3-
import DefaultTemplate from "@/components/DefaultTemplate"
4-
import { CommerceLayerAuthProvider, useCommerceLayerAuth } from "@/contexts/CommerceLayerAuthContext"
53
import { getCoreApiBaseEndpoint, jwtDecode } from "@commercelayer/js-auth"
64
import { useEffect, useMemo, useState } from "react"
5+
import DefaultTemplate from "@/components/DefaultTemplate"
6+
import {
7+
CommerceLayerAuthProvider,
8+
useCommerceLayerAuth,
9+
} from "@/contexts/CommerceLayerAuthContext"
710

811
const scope = process.env.NEXT_PUBLIC_SCOPE as string
912

@@ -153,7 +156,12 @@ function CustomerOrders() {
153156
} else {
154157
setOrders(null)
155158
}
156-
}, [authorization.accessToken, authorization.ownerType, authorization.ownerId, organizationSlug])
159+
}, [
160+
authorization.accessToken,
161+
authorization.ownerType,
162+
authorization.ownerId,
163+
organizationSlug,
164+
])
157165

158166
if (orders == null) {
159167
return null

examples/nextjs/src/app/utils/getServerSideToken.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,11 @@ const salesChannel = makeSalesChannel(
1212
{
1313
clientId,
1414
scope,
15-
debug: true,
15+
debug: "verbose",
1616
},
1717
{
1818
storage: createCompositeStorage({
19+
debug: "verbose",
1920
name: "serverCompositeStorage",
2021
storages: [
2122
createStorage({
@@ -34,7 +35,9 @@ const salesChannel = makeSalesChannel(
3435
},
3536
)
3637

37-
export async function getServerSideAuth(): Promise<ReturnType<typeof makeSalesChannel>> {
38+
export async function getServerSideAuth(): Promise<
39+
ReturnType<typeof makeSalesChannel>
40+
> {
3841
"use server"
3942

4043
return {

examples/nextjs/src/contexts/CommerceLayerAuthContext.tsx

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
authenticate,
66
makeSalesChannel,
77
} from "@commercelayer/js-auth"
8-
import cookies from 'js-cookie'
8+
import cookies from "js-cookie"
99
import {
1010
createContext,
1111
type ReactNode,
@@ -22,11 +22,14 @@ type AuthState = {
2222
accessToken?: string
2323
ownerType?: "customer" | "guest"
2424
ownerId?: string
25-
errors?: Extract<AuthenticateReturn<'password'>, { errors?: unknown }>['errors']
25+
errors?: Extract<
26+
AuthenticateReturn<"password">,
27+
{ errors?: unknown }
28+
>["errors"]
2629
}
2730

2831
interface CommerceLayerAuthContextType extends AuthState {
29-
login: ( email: string, password: string ) => Promise<void>
32+
login: (email: string, password: string) => Promise<void>
3033
logout: () => Promise<void>
3134
}
3235

@@ -46,7 +49,10 @@ export function useCommerceLayerAuth() {
4649
export function CommerceLayerAuthProvider({
4750
children,
4851
scope,
49-
}: { children: ReactNode; scope: string }) {
52+
}: {
53+
children: ReactNode
54+
scope: string
55+
}) {
5056
const [salesChannel, setSalesChannel] =
5157
useState<ReturnType<typeof makeSalesChannel>>()
5258

@@ -111,7 +117,10 @@ export function CommerceLayerAuthProvider({
111117
setAuth({
112118
accessToken: authorization.accessToken,
113119
ownerType: authorization.ownerType,
114-
ownerId: authorization.ownerType === 'customer' ? authorization.ownerId : undefined,
120+
ownerId:
121+
authorization.ownerType === "customer"
122+
? authorization.ownerId
123+
: undefined,
115124
errors: authorization.errors,
116125
})
117126
},
@@ -132,10 +141,7 @@ export function CommerceLayerAuthProvider({
132141
void salesChannel?.getAuthorization().then((auth) => {
133142
setAuth({
134143
ownerType: auth.ownerType,
135-
ownerId:
136-
auth.ownerType === "customer"
137-
? auth.ownerId
138-
: undefined,
144+
ownerId: auth.ownerType === "customer" ? auth.ownerId : undefined,
139145
accessToken: auth.accessToken,
140146
errors: auth.errors,
141147
})

packages/js-auth/README.md

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ It works everywhere — on your browser, server, or at the edge.
2626
- [Utilities](#utilities)
2727
- [Revoking a token](#revoking-a-token)
2828
- [Decoding an access token](#decoding-an-access-token)
29+
- [Checking if an access token is expired](#checking-if-an-access-token-is-expired)
2930
- [Verifying an access token](#verifying-an-access-token)
3031
- [Getting the Core API base endpoint](#getting-the-core-api-base-endpoint)
3132
- [Getting the Provisioning API base endpoint](#getting-the-provisioning-api-base-endpoint)
@@ -249,9 +250,14 @@ flowchart TB
249250

250251
You can enable debugging and assign custom names to your storage instances for better visibility into token operations:
251252

252-
- **`debug`** — When set to `true`, logs detailed information about token operations (creation, retrieval, refresh, etc.).
253+
- **`debug`** — When set to `true`, logs **state transitions** only: cache misses, token refreshes, authorizations stored/removed, and errors. Routine cache hits are silent, so setups that call `getAuthorization()` on every API request (e.g. when implementing the SDK's `getAccessToken` callback) don't flood the logs. Set it to `"verbose"` to also log steady-state operations (every storage read and cache hit).
253254
- **`name`** — A custom identifier for your storage instance, useful when using multiple storages or composite storage configurations. The `name` is an attribute of the storage itself (as shown in the [`memoryStorage` example above](#storage-strategy)).
254255

256+
The `createCompositeStorage` helper accepts its own `debug` option, following the same rule: a hit on the first (fastest) storage is steady state, while falling back to a slower storage is a transition worth logging.
257+
258+
> [!WARNING]
259+
> When debug mode is enabled, full authorization objects (including access tokens and refresh tokens) are logged to the console. It is intended for local development only — in serverless/edge environments, logs may be forwarded to external log aggregation services.
260+
255261
```diff
256262
const salesChannel = makeSalesChannel(
257263
{
@@ -671,6 +677,20 @@ if (jwtIsSalesChannel(decodedJWT.payload)) {
671677
}
672678
```
673679

680+
### Checking if an access token is expired
681+
682+
We offer a helper method to check whether an access token is expired. This is useful when you manage tokens yourself (instead of using `makeSalesChannel` / `makeIntegration`) and want to proactively refresh an expired token before sending an API request that is doomed to fail with a `401` status code.
683+
684+
It accepts an optional `offsetSeconds` (defaults to `0`) to consider the token expired a few seconds ahead of time, as a clock-skew / in-flight request margin. It returns `true` for tokens that cannot be decoded.
685+
686+
```ts
687+
import { jwtIsExpired } from "@commercelayer/js-auth"
688+
689+
if (jwtIsExpired(accessToken, 30)) {
690+
// refresh the token (the token is expired or will expire within 30 seconds)
691+
}
692+
```
693+
674694
### Verifying an access token
675695

676696
We offer an helper method to verify an access token.

0 commit comments

Comments
 (0)