@ph-cms/client-sdk 0.1.6 → 0.1.7

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.
@@ -0,0 +1,151 @@
1
+ import { AuthProvider } from './interfaces';
2
+ /**
3
+ * Abstract base class that implements the common token-management logic
4
+ * shared by all {@link AuthProvider} implementations (e.g. `LocalAuthProvider`,
5
+ * `FirebaseAuthProvider`).
6
+ *
7
+ * Subclasses **must** implement:
8
+ * - `type` — the discriminator literal (`'LOCAL'` | `'FIREBASE'`).
9
+ * - `getToken()` — provider-specific logic for returning a valid access token
10
+ * (including any fallback strategies such as Firebase ID tokens).
11
+ *
12
+ * Subclasses **may** override:
13
+ * - `logout()` — to perform additional cleanup (e.g. signing out of Firebase).
14
+ * Always call `super.logout()` to ensure in-memory and localStorage tokens
15
+ * are cleared.
16
+ */
17
+ export declare abstract class BaseAuthProvider implements AuthProvider {
18
+ /** Discriminator that identifies the provider type. */
19
+ abstract readonly type: 'FIREBASE' | 'LOCAL';
20
+ /**
21
+ * Returns the current valid access token.
22
+ *
23
+ * Implementations should check token expiration and attempt a refresh
24
+ * automatically before returning the token. If the token is expired
25
+ * and cannot be refreshed, return `null`.
26
+ */
27
+ abstract getToken(): Promise<string | null>;
28
+ /** The current PH-CMS access token (JWT), or `null` if not authenticated. */
29
+ protected accessToken: string | null;
30
+ /** The current refresh token, or `null` if not authenticated. */
31
+ protected refreshToken: string | null;
32
+ /**
33
+ * Callback invoked when the token is known to be expired and automatic
34
+ * refresh has failed. Typically used to redirect the user to a login page.
35
+ */
36
+ protected onExpiredCallback: (() => Promise<void>) | null;
37
+ /**
38
+ * Function that performs the actual token refresh against the PH-CMS
39
+ * server. Registered by `PHCMSClient` after construction via
40
+ * {@link setRefreshFn}.
41
+ */
42
+ protected refreshFn: ((refreshToken: string) => Promise<{
43
+ accessToken: string;
44
+ refreshToken: string;
45
+ }>) | null;
46
+ /**
47
+ * In-flight refresh promise used to de-duplicate concurrent refresh
48
+ * requests triggered by parallel `getToken()` calls.
49
+ */
50
+ protected refreshPromise: Promise<string | null> | null;
51
+ /**
52
+ * How many milliseconds before actual JWT expiration the token is
53
+ * considered "expired" so the caller can refresh proactively.
54
+ */
55
+ protected expiryBufferMs: number;
56
+ /**
57
+ * Prefix applied to all `localStorage` keys managed by this provider
58
+ * (e.g. `'ph_cms_'` → keys `ph_cms_access_token`, `ph_cms_refresh_token`).
59
+ */
60
+ protected readonly storageKeyPrefix: string;
61
+ /**
62
+ * @param storageKeyPrefix Prefix for `localStorage` keys (e.g. `'ph_cms_'`).
63
+ * @param expiryBufferMs How many ms before actual expiration the token is
64
+ * considered expired. Defaults to {@link DEFAULT_EXPIRY_BUFFER_MS}
65
+ * (60 000 ms / 1 minute).
66
+ */
67
+ constructor(storageKeyPrefix: string, expiryBufferMs?: number);
68
+ /**
69
+ * Returns `true` if the provider currently holds any token (access **or**
70
+ * refresh). This is a synchronous check used to decide whether to attempt
71
+ * API calls that require authentication (e.g. `/auth/me`).
72
+ */
73
+ hasToken(): boolean;
74
+ /**
75
+ * Returns the current refresh token, or `null` if none is stored.
76
+ */
77
+ getRefreshToken(): string | null;
78
+ /**
79
+ * Registers the function that performs the actual token refresh against
80
+ * the server. Called by `PHCMSClient` after construction so that the
81
+ * provider can refresh tokens autonomously inside {@link getToken} without
82
+ * a circular dependency on the client/module layer.
83
+ *
84
+ * @param fn A function that receives the current refresh token and returns
85
+ * a fresh access/refresh token pair.
86
+ */
87
+ setRefreshFn(fn: (refreshToken: string) => Promise<{
88
+ accessToken: string;
89
+ refreshToken: string;
90
+ }>): void;
91
+ /**
92
+ * Stores a new pair of access/refresh tokens in memory **and**
93
+ * `localStorage` (when running in a browser).
94
+ *
95
+ * @param accessToken The new JWT access token.
96
+ * @param refreshToken The new refresh token.
97
+ */
98
+ setTokens(accessToken: string, refreshToken: string): void;
99
+ /**
100
+ * Sets a callback to be invoked when the token is known to be expired
101
+ * and automatic refresh has failed.
102
+ *
103
+ * @param callback An async function executed on unrecoverable expiry
104
+ * (e.g. redirect the user to a login page).
105
+ */
106
+ onTokenExpired(callback: () => Promise<void>): void;
107
+ /**
108
+ * Clears the current session by removing tokens from memory and
109
+ * `localStorage`.
110
+ *
111
+ * Subclasses may override this method to perform additional cleanup
112
+ * (e.g. signing out of Firebase) but **must** call `super.logout()`.
113
+ */
114
+ logout(): Promise<void>;
115
+ /**
116
+ * Attempt to refresh the token pair using the registered {@link refreshFn}.
117
+ *
118
+ * If a refresh is already in progress (e.g. from a concurrent
119
+ * `getToken()` call) the existing promise is returned so we don't fire
120
+ * multiple refresh requests simultaneously.
121
+ *
122
+ * @returns The new access token on success, or `null` on failure.
123
+ */
124
+ protected tryRefresh(): Promise<string | null>;
125
+ /**
126
+ * Executes the actual refresh call via {@link refreshFn}.
127
+ *
128
+ * On success the new tokens are persisted via {@link setTokens}.
129
+ * On failure all tokens are cleared and the {@link onExpiredCallback}
130
+ * is invoked.
131
+ *
132
+ * @returns The new access token on success, or `null` on failure.
133
+ */
134
+ protected executeRefresh(): Promise<string | null>;
135
+ /**
136
+ * Removes the access and refresh tokens from both in-memory state and
137
+ * `localStorage`.
138
+ */
139
+ protected clearTokens(): void;
140
+ /**
141
+ * Checks whether the current access token is expired (or will expire
142
+ * within {@link expiryBufferMs}).
143
+ *
144
+ * @returns
145
+ * - `true` — the token is expired or will expire within the buffer.
146
+ * - `false` — the token is still valid beyond the buffer window.
147
+ * - `null` — the token could not be parsed (caller should treat this
148
+ * as potentially valid and let the server decide).
149
+ */
150
+ protected isCurrentTokenExpired(): boolean | null;
151
+ }
@@ -0,0 +1,210 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BaseAuthProvider = void 0;
4
+ const jwt_utils_1 = require("./jwt-utils");
5
+ /**
6
+ * Buffer time (ms) before actual expiration at which we consider the token
7
+ * "expired" and proactively refresh. Default: 60 seconds.
8
+ */
9
+ const DEFAULT_EXPIRY_BUFFER_MS = 60000;
10
+ /**
11
+ * Abstract base class that implements the common token-management logic
12
+ * shared by all {@link AuthProvider} implementations (e.g. `LocalAuthProvider`,
13
+ * `FirebaseAuthProvider`).
14
+ *
15
+ * Subclasses **must** implement:
16
+ * - `type` — the discriminator literal (`'LOCAL'` | `'FIREBASE'`).
17
+ * - `getToken()` — provider-specific logic for returning a valid access token
18
+ * (including any fallback strategies such as Firebase ID tokens).
19
+ *
20
+ * Subclasses **may** override:
21
+ * - `logout()` — to perform additional cleanup (e.g. signing out of Firebase).
22
+ * Always call `super.logout()` to ensure in-memory and localStorage tokens
23
+ * are cleared.
24
+ */
25
+ class BaseAuthProvider {
26
+ // -----------------------------------------------------------------------
27
+ // Constructor
28
+ // -----------------------------------------------------------------------
29
+ /**
30
+ * @param storageKeyPrefix Prefix for `localStorage` keys (e.g. `'ph_cms_'`).
31
+ * @param expiryBufferMs How many ms before actual expiration the token is
32
+ * considered expired. Defaults to {@link DEFAULT_EXPIRY_BUFFER_MS}
33
+ * (60 000 ms / 1 minute).
34
+ */
35
+ constructor(storageKeyPrefix, expiryBufferMs) {
36
+ // -----------------------------------------------------------------------
37
+ // Protected state — accessible to subclasses
38
+ // -----------------------------------------------------------------------
39
+ /** The current PH-CMS access token (JWT), or `null` if not authenticated. */
40
+ this.accessToken = null;
41
+ /** The current refresh token, or `null` if not authenticated. */
42
+ this.refreshToken = null;
43
+ /**
44
+ * Callback invoked when the token is known to be expired and automatic
45
+ * refresh has failed. Typically used to redirect the user to a login page.
46
+ */
47
+ this.onExpiredCallback = null;
48
+ /**
49
+ * Function that performs the actual token refresh against the PH-CMS
50
+ * server. Registered by `PHCMSClient` after construction via
51
+ * {@link setRefreshFn}.
52
+ */
53
+ this.refreshFn = null;
54
+ /**
55
+ * In-flight refresh promise used to de-duplicate concurrent refresh
56
+ * requests triggered by parallel `getToken()` calls.
57
+ */
58
+ this.refreshPromise = null;
59
+ this.storageKeyPrefix = storageKeyPrefix;
60
+ this.expiryBufferMs = expiryBufferMs ?? DEFAULT_EXPIRY_BUFFER_MS;
61
+ // Hydrate tokens from localStorage when running in a browser context.
62
+ if (typeof window !== 'undefined') {
63
+ this.accessToken = localStorage.getItem(`${this.storageKeyPrefix}access_token`);
64
+ this.refreshToken = localStorage.getItem(`${this.storageKeyPrefix}refresh_token`);
65
+ }
66
+ }
67
+ // -----------------------------------------------------------------------
68
+ // Concrete AuthProvider methods
69
+ // -----------------------------------------------------------------------
70
+ /**
71
+ * Returns `true` if the provider currently holds any token (access **or**
72
+ * refresh). This is a synchronous check used to decide whether to attempt
73
+ * API calls that require authentication (e.g. `/auth/me`).
74
+ */
75
+ hasToken() {
76
+ return this.accessToken !== null || this.refreshToken !== null;
77
+ }
78
+ /**
79
+ * Returns the current refresh token, or `null` if none is stored.
80
+ */
81
+ getRefreshToken() {
82
+ return this.refreshToken;
83
+ }
84
+ /**
85
+ * Registers the function that performs the actual token refresh against
86
+ * the server. Called by `PHCMSClient` after construction so that the
87
+ * provider can refresh tokens autonomously inside {@link getToken} without
88
+ * a circular dependency on the client/module layer.
89
+ *
90
+ * @param fn A function that receives the current refresh token and returns
91
+ * a fresh access/refresh token pair.
92
+ */
93
+ setRefreshFn(fn) {
94
+ this.refreshFn = fn;
95
+ }
96
+ /**
97
+ * Stores a new pair of access/refresh tokens in memory **and**
98
+ * `localStorage` (when running in a browser).
99
+ *
100
+ * @param accessToken The new JWT access token.
101
+ * @param refreshToken The new refresh token.
102
+ */
103
+ setTokens(accessToken, refreshToken) {
104
+ this.accessToken = accessToken;
105
+ this.refreshToken = refreshToken;
106
+ if (typeof window !== 'undefined') {
107
+ localStorage.setItem(`${this.storageKeyPrefix}access_token`, accessToken);
108
+ localStorage.setItem(`${this.storageKeyPrefix}refresh_token`, refreshToken);
109
+ }
110
+ }
111
+ /**
112
+ * Sets a callback to be invoked when the token is known to be expired
113
+ * and automatic refresh has failed.
114
+ *
115
+ * @param callback An async function executed on unrecoverable expiry
116
+ * (e.g. redirect the user to a login page).
117
+ */
118
+ onTokenExpired(callback) {
119
+ this.onExpiredCallback = callback;
120
+ }
121
+ /**
122
+ * Clears the current session by removing tokens from memory and
123
+ * `localStorage`.
124
+ *
125
+ * Subclasses may override this method to perform additional cleanup
126
+ * (e.g. signing out of Firebase) but **must** call `super.logout()`.
127
+ */
128
+ async logout() {
129
+ this.clearTokens();
130
+ }
131
+ // -----------------------------------------------------------------------
132
+ // Protected helpers — available to subclasses
133
+ // -----------------------------------------------------------------------
134
+ /**
135
+ * Attempt to refresh the token pair using the registered {@link refreshFn}.
136
+ *
137
+ * If a refresh is already in progress (e.g. from a concurrent
138
+ * `getToken()` call) the existing promise is returned so we don't fire
139
+ * multiple refresh requests simultaneously.
140
+ *
141
+ * @returns The new access token on success, or `null` on failure.
142
+ */
143
+ async tryRefresh() {
144
+ if (!this.refreshFn || !this.refreshToken) {
145
+ // Cannot refresh — notify the expired callback if set.
146
+ await this.onExpiredCallback?.();
147
+ return null;
148
+ }
149
+ // De-duplicate concurrent refreshes.
150
+ if (this.refreshPromise) {
151
+ return this.refreshPromise;
152
+ }
153
+ this.refreshPromise = this.executeRefresh();
154
+ try {
155
+ return await this.refreshPromise;
156
+ }
157
+ finally {
158
+ this.refreshPromise = null;
159
+ }
160
+ }
161
+ /**
162
+ * Executes the actual refresh call via {@link refreshFn}.
163
+ *
164
+ * On success the new tokens are persisted via {@link setTokens}.
165
+ * On failure all tokens are cleared and the {@link onExpiredCallback}
166
+ * is invoked.
167
+ *
168
+ * @returns The new access token on success, or `null` on failure.
169
+ */
170
+ async executeRefresh() {
171
+ try {
172
+ const result = await this.refreshFn(this.refreshToken);
173
+ this.setTokens(result.accessToken, result.refreshToken);
174
+ return result.accessToken;
175
+ }
176
+ catch {
177
+ // Refresh failed (e.g. refresh token also expired).
178
+ // Clear tokens and notify.
179
+ this.clearTokens();
180
+ await this.onExpiredCallback?.();
181
+ return null;
182
+ }
183
+ }
184
+ /**
185
+ * Removes the access and refresh tokens from both in-memory state and
186
+ * `localStorage`.
187
+ */
188
+ clearTokens() {
189
+ this.accessToken = null;
190
+ this.refreshToken = null;
191
+ if (typeof window !== 'undefined') {
192
+ localStorage.removeItem(`${this.storageKeyPrefix}access_token`);
193
+ localStorage.removeItem(`${this.storageKeyPrefix}refresh_token`);
194
+ }
195
+ }
196
+ /**
197
+ * Checks whether the current access token is expired (or will expire
198
+ * within {@link expiryBufferMs}).
199
+ *
200
+ * @returns
201
+ * - `true` — the token is expired or will expire within the buffer.
202
+ * - `false` — the token is still valid beyond the buffer window.
203
+ * - `null` — the token could not be parsed (caller should treat this
204
+ * as potentially valid and let the server decide).
205
+ */
206
+ isCurrentTokenExpired() {
207
+ return (0, jwt_utils_1.isTokenExpired)(this.accessToken, this.expiryBufferMs);
208
+ }
209
+ }
210
+ exports.BaseAuthProvider = BaseAuthProvider;
@@ -1,18 +1,52 @@
1
1
  import type { Auth } from "firebase/auth";
2
- import { AuthProvider } from "./interfaces";
3
- export declare class FirebaseAuthProvider implements AuthProvider {
2
+ import { BaseAuthProvider } from "./base-provider";
3
+ /**
4
+ * Authentication provider that integrates Firebase Authentication with PH-CMS.
5
+ *
6
+ * Uses Firebase as the identity provider while managing PH-CMS-specific
7
+ * access/refresh tokens. When no valid PH-CMS token is available, falls back
8
+ * to the Firebase ID token (useful for the initial token exchange call).
9
+ *
10
+ * Extends {@link BaseAuthProvider} which handles all common token management
11
+ * logic (storage, refresh de-duplication, expiry checks, etc.).
12
+ */
13
+ export declare class FirebaseAuthProvider extends BaseAuthProvider {
4
14
  private readonly auth;
5
- private readonly storageKeyPrefix;
6
- readonly type = "FIREBASE";
7
- private accessToken;
8
- private refreshToken;
9
- private onExpiredCallback;
10
- constructor(auth: Auth, storageKeyPrefix?: string);
11
- hasToken(): boolean;
12
- setTokens(accessToken: string, refreshToken: string): void;
15
+ readonly type: "FIREBASE";
16
+ /**
17
+ * @param auth The Firebase `Auth` instance.
18
+ * @param storageKeyPrefix Prefix for `localStorage` keys.
19
+ * Defaults to `'ph_cms_fb_'`.
20
+ * @param options Optional configuration.
21
+ * @param options.expiryBufferMs How many ms before actual expiration the
22
+ * token is considered expired. Defaults to
23
+ * 60 000 ms (1 minute).
24
+ */
25
+ constructor(auth: Auth, storageKeyPrefix?: string, options?: {
26
+ /**
27
+ * How many ms before actual expiration the token is considered expired.
28
+ * @default 60_000 (1 minute)
29
+ */
30
+ expiryBufferMs?: number;
31
+ });
32
+ /**
33
+ * Returns a valid access token for PH-CMS API calls.
34
+ *
35
+ * 1. If a PH-CMS access token exists and is still valid, return it.
36
+ * 2. If the PH-CMS access token is expired (or will expire within
37
+ * `expiryBufferMs`), attempt an automatic refresh using the stored
38
+ * refresh token.
39
+ * 3. If no PH-CMS token exists at all (or refresh failed), fall back to
40
+ * the Firebase ID token (useful for the initial exchange call or if the
41
+ * server supports Firebase tokens directly).
42
+ */
13
43
  getToken(): Promise<string | null>;
14
- onTokenExpired(callback: () => Promise<void>): void;
44
+ /**
45
+ * Clears PH-CMS tokens **and** signs the user out of Firebase.
46
+ */
15
47
  logout(): Promise<void>;
16
- getRefreshToken(): string | null;
48
+ /**
49
+ * Returns the current Firebase ID token, or `null` if no user is signed in.
50
+ */
17
51
  getIdToken(): Promise<string | null>;
18
52
  }
@@ -1,58 +1,70 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.FirebaseAuthProvider = void 0;
4
- class FirebaseAuthProvider {
5
- constructor(auth, storageKeyPrefix = 'ph_cms_fb_') {
4
+ const base_provider_1 = require("./base-provider");
5
+ /**
6
+ * Authentication provider that integrates Firebase Authentication with PH-CMS.
7
+ *
8
+ * Uses Firebase as the identity provider while managing PH-CMS-specific
9
+ * access/refresh tokens. When no valid PH-CMS token is available, falls back
10
+ * to the Firebase ID token (useful for the initial token exchange call).
11
+ *
12
+ * Extends {@link BaseAuthProvider} which handles all common token management
13
+ * logic (storage, refresh de-duplication, expiry checks, etc.).
14
+ */
15
+ class FirebaseAuthProvider extends base_provider_1.BaseAuthProvider {
16
+ /**
17
+ * @param auth The Firebase `Auth` instance.
18
+ * @param storageKeyPrefix Prefix for `localStorage` keys.
19
+ * Defaults to `'ph_cms_fb_'`.
20
+ * @param options Optional configuration.
21
+ * @param options.expiryBufferMs How many ms before actual expiration the
22
+ * token is considered expired. Defaults to
23
+ * 60 000 ms (1 minute).
24
+ */
25
+ constructor(auth, storageKeyPrefix = 'ph_cms_fb_', options) {
26
+ super(storageKeyPrefix, options?.expiryBufferMs);
6
27
  this.auth = auth;
7
- this.storageKeyPrefix = storageKeyPrefix;
8
28
  this.type = 'FIREBASE';
9
- this.accessToken = null;
10
- this.refreshToken = null;
11
- this.onExpiredCallback = null;
12
- if (typeof window !== 'undefined') {
13
- this.accessToken = localStorage.getItem(`${this.storageKeyPrefix}access_token`);
14
- this.refreshToken = localStorage.getItem(`${this.storageKeyPrefix}refresh_token`);
15
- }
16
- }
17
- hasToken() {
18
- return this.accessToken !== null || this.refreshToken !== null;
19
- }
20
- setTokens(accessToken, refreshToken) {
21
- this.accessToken = accessToken;
22
- this.refreshToken = refreshToken;
23
- if (typeof window !== 'undefined') {
24
- localStorage.setItem(`${this.storageKeyPrefix}access_token`, accessToken);
25
- localStorage.setItem(`${this.storageKeyPrefix}refresh_token`, refreshToken);
26
- }
27
29
  }
30
+ /**
31
+ * Returns a valid access token for PH-CMS API calls.
32
+ *
33
+ * 1. If a PH-CMS access token exists and is still valid, return it.
34
+ * 2. If the PH-CMS access token is expired (or will expire within
35
+ * `expiryBufferMs`), attempt an automatic refresh using the stored
36
+ * refresh token.
37
+ * 3. If no PH-CMS token exists at all (or refresh failed), fall back to
38
+ * the Firebase ID token (useful for the initial exchange call or if the
39
+ * server supports Firebase tokens directly).
40
+ */
28
41
  async getToken() {
29
- // If we have a local access token, use it for PH-CMS API calls.
30
42
  if (this.accessToken) {
31
- return this.accessToken;
43
+ const expired = this.isCurrentTokenExpired();
44
+ if (expired === true) {
45
+ const refreshed = await this.tryRefresh();
46
+ if (refreshed)
47
+ return refreshed;
48
+ // Refresh failed — fall through to Firebase ID token fallback.
49
+ }
50
+ else {
51
+ // Token is valid (or unparseable — let server decide).
52
+ return this.accessToken;
53
+ }
32
54
  }
33
- // Fallback to Firebase ID token if no local token is set yet.
34
- // This might be useful if the server starts supporting Firebase tokens directly
35
- // or for the initial exchange call if we want to automate it (though currently it's manual).
36
- const user = this.auth.currentUser;
37
- if (!user)
38
- return null;
39
- return user.getIdToken();
40
- }
41
- onTokenExpired(callback) {
42
- this.onExpiredCallback = callback;
55
+ // Fallback to Firebase ID token if no valid PH-CMS token is available.
56
+ return this.getIdToken();
43
57
  }
58
+ /**
59
+ * Clears PH-CMS tokens **and** signs the user out of Firebase.
60
+ */
44
61
  async logout() {
45
- this.accessToken = null;
46
- this.refreshToken = null;
47
- if (typeof window !== 'undefined') {
48
- localStorage.removeItem(`${this.storageKeyPrefix}access_token`);
49
- localStorage.removeItem(`${this.storageKeyPrefix}refresh_token`);
50
- }
62
+ await super.logout();
51
63
  await this.auth.signOut();
52
64
  }
53
- getRefreshToken() {
54
- return this.refreshToken;
55
- }
65
+ /**
66
+ * Returns the current Firebase ID token, or `null` if no user is signed in.
67
+ */
56
68
  async getIdToken() {
57
69
  const user = this.auth.currentUser;
58
70
  if (!user)
@@ -2,7 +2,9 @@ export interface AuthProvider {
2
2
  type: 'FIREBASE' | 'LOCAL';
3
3
  /**
4
4
  * Returns the current valid access token.
5
- * Should handle refreshing if necessary (or return null/throw if expired and can't refresh).
5
+ * Implementations should check token expiration and attempt a refresh
6
+ * automatically before returning the token. If the token is expired
7
+ * and cannot be refreshed, return `null`.
6
8
  */
7
9
  getToken(): Promise<string | null>;
8
10
  /**
@@ -12,7 +14,28 @@ export interface AuthProvider {
12
14
  */
13
15
  hasToken(): boolean;
14
16
  /**
15
- * Sets a callback to be called when the token is known to be expired by the external world (e.g. 401 response).
17
+ * Returns the current refresh token, or `null` if none is stored.
18
+ */
19
+ getRefreshToken(): string | null;
20
+ /**
21
+ * Registers the function that performs the actual token refresh against the server.
22
+ * This is called by `PHCMSClient` after construction so that the provider can
23
+ * refresh tokens autonomously inside `getToken()` without a circular dependency
24
+ * on the client/module layer.
25
+ *
26
+ * The function receives the current refresh token and returns a fresh pair.
27
+ */
28
+ setRefreshFn(fn: (refreshToken: string) => Promise<{
29
+ accessToken: string;
30
+ refreshToken: string;
31
+ }>): void;
32
+ /**
33
+ * Stores a new pair of access/refresh tokens.
34
+ */
35
+ setTokens(accessToken: string, refreshToken: string): void;
36
+ /**
37
+ * Sets a callback to be called when the token is known to be expired by the
38
+ * external world (e.g. 401 response) and automatic refresh has failed.
16
39
  */
17
40
  onTokenExpired(callback: () => Promise<void>): void;
18
41
  /**
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Lightweight JWT utilities for client-side token inspection.
3
+ *
4
+ * These helpers parse the **payload** of a JWT (the middle segment) using
5
+ * plain base64 decoding. They do NOT verify the signature — that is the
6
+ * server's responsibility. The client only needs to know *when* a token
7
+ * expires so it can proactively refresh before sending an API request.
8
+ */
9
+ export interface JwtPayload {
10
+ /** Subject (user id / uid) */
11
+ sub?: string;
12
+ /** Internal user id (PH-CMS specific) */
13
+ id?: number;
14
+ /** Issued-at timestamp (seconds since epoch) */
15
+ iat?: number;
16
+ /** Expiration timestamp (seconds since epoch) */
17
+ exp?: number;
18
+ /** Any other claims */
19
+ [key: string]: unknown;
20
+ }
21
+ /**
22
+ * Decode the payload (second segment) of a JWT string.
23
+ *
24
+ * Returns `null` if the token is malformed or cannot be decoded.
25
+ * This function works in both browser and Node.js environments.
26
+ */
27
+ export declare function decodeJwtPayload(token: string): JwtPayload | null;
28
+ /**
29
+ * Returns the expiration time of a JWT as a Unix timestamp **in milliseconds**.
30
+ *
31
+ * Returns `null` if the token is malformed or does not contain an `exp` claim.
32
+ */
33
+ export declare function getTokenExpirationMs(token: string): number | null;
34
+ /**
35
+ * Check whether a token has expired (or will expire within `bufferMs`).
36
+ *
37
+ * @param token Raw JWT string
38
+ * @param bufferMs Grace period in milliseconds. If the token expires within
39
+ * this window it is considered "expired" so the caller can
40
+ * refresh proactively. Defaults to **60 000 ms (1 minute)**.
41
+ * @returns
42
+ * - `true` — the token is expired or will expire within `bufferMs`
43
+ * - `false` — the token is still valid beyond the buffer window
44
+ * - `null` — the token could not be parsed (treat as expired for safety)
45
+ */
46
+ export declare function isTokenExpired(token: string, bufferMs?: number): boolean | null;
47
+ /**
48
+ * Returns the number of milliseconds until the token expires.
49
+ *
50
+ * Returns `null` if the token is malformed.
51
+ * Returns a negative value if the token is already expired.
52
+ */
53
+ export declare function getTokenTTL(token: string): number | null;