@scaleway/auth-scw 1.0.4 → 1.1.0
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.
- package/README.md +40 -0
- package/dist/constants.js +2 -1
- package/dist/index.d.ts +5 -3
- package/dist/index.js +3 -2
- package/dist/types/dist/index.d.ts +13 -0
- package/dist/types/index.d.ts +19 -1
- package/dist/useAuthScw/AuthScwProvider.d.ts +20 -5
- package/dist/useAuthScw/AuthScwProvider.js +48 -31
- package/dist/useAuthScw/authStoreManager/authStoreManager.d.ts +59 -0
- package/dist/useAuthScw/authStoreManager/authStoreManager.js +143 -0
- package/dist/useAuthScw/authStoreManager/constants.d.ts +6 -0
- package/dist/useAuthScw/authStoreManager/constants.js +7 -0
- package/dist/useAuthScw/authStoreManager/cookieBackend.js +64 -0
- package/dist/useAuthScw/authStoreManager/index.d.ts +4 -0
- package/dist/useAuthScw/authStoreManager/localStorageBackend.js +41 -0
- package/dist/useAuthScw/authStoreManager/types.d.ts +9 -0
- package/dist/useAuthScw/helpers.js +4 -5
- package/dist/useAuthScw/index.d.ts +2 -0
- package/dist/zodSchemas.d.ts +2 -1
- package/dist/zodSchemas.js +2 -1
- package/package.json +6 -5
- package/dist/useAuthScw/authStoreManager.d.ts +0 -25
- package/dist/useAuthScw/authStoreManager.js +0 -105
package/README.md
CHANGED
|
@@ -12,6 +12,46 @@ pnpm add @scaleway/auth-scw
|
|
|
12
12
|
|
|
13
13
|
...
|
|
14
14
|
|
|
15
|
+
### Choosing a storage backend
|
|
16
|
+
|
|
17
|
+
By default, the JWT and audienceId are persisted in **cookies**. You can opt-in
|
|
18
|
+
to **localStorage** with the `storageType` option:
|
|
19
|
+
|
|
20
|
+
```tsx
|
|
21
|
+
<AuthScwProvider
|
|
22
|
+
// ...other props
|
|
23
|
+
storageType="localStorage"
|
|
24
|
+
>
|
|
25
|
+
{children}
|
|
26
|
+
</AuthScwProvider>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
| `storageType` | Pros | Cons |
|
|
30
|
+
| ---------------- | ----------------------------------------------------------- | ----------------------------------------------------------------- |
|
|
31
|
+
| `'cookie'` | Cross-tab synchronisation, sent to the server automatically | Size limits (~4KB), sent on every request |
|
|
32
|
+
| `'localStorage'` | Larger storage limit (~5MB), not sent on every HTTP request | Origin-scoped, no cross-tab sync (use `storage` events if needed) |
|
|
33
|
+
|
|
34
|
+
### Live migration from cookie to localStorage
|
|
35
|
+
|
|
36
|
+
When switching from `'cookie'` to `'localStorage'` on an already-deployed app,
|
|
37
|
+
set `migrateFromCookie` to migrate any existing cookie-based session to
|
|
38
|
+
localStorage on provider initialization (then clean up the cookies):
|
|
39
|
+
|
|
40
|
+
```tsx
|
|
41
|
+
<AuthScwProvider
|
|
42
|
+
// ...other props
|
|
43
|
+
storageType="localStorage"
|
|
44
|
+
migrateFromCookie
|
|
45
|
+
>
|
|
46
|
+
{children}
|
|
47
|
+
</AuthScwProvider>
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
The migration runs **once**, before the provider reads the active storage. It is
|
|
51
|
+
idempotent and safe to keep enabled even after all users have migrated — it is a
|
|
52
|
+
no-op when nothing is found in cookies. You can also invoke it manually via the
|
|
53
|
+
exported `migrateCookieToLocalStorage()` helper.
|
|
54
|
+
|
|
15
55
|
## How to dev
|
|
16
56
|
|
|
17
57
|
### Prerequisites
|
package/dist/constants.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { COOKIE_AGE, DEFAULT_AUTH_SUFFIX_KEY, DOMAIN, getUniqueHostnameString } from "./useAuthScw/authStoreManager/constants.js";
|
|
2
|
+
import { AuthStoreManager, createAuthStoreManager } from "./useAuthScw/authStoreManager/authStoreManager.js";
|
|
3
|
+
import "./useAuthScw/authStoreManager/index.js";
|
|
4
|
+
import { AudienceIdType, AuthScwContextType, EncodedJWT, StorageType } from "./types/index.js";
|
|
2
5
|
import { EmptySession } from "./constants.js";
|
|
3
6
|
import { AuthScwProvider, useAuthScw } from "./useAuthScw/AuthScwProvider.js";
|
|
4
7
|
import "./useAuthScw/index.js";
|
|
5
|
-
|
|
6
|
-
export { type AudienceIdType, type AuthScwContextType, AuthScwProvider, AuthStoreManager, EmptySession, type EncodedJWT, useAuthScw };
|
|
8
|
+
export { type AudienceIdType, type AuthScwContextType, AuthScwProvider, AuthStoreManager, COOKIE_AGE, DEFAULT_AUTH_SUFFIX_KEY, DOMAIN, EmptySession, type EncodedJWT, type StorageType, createAuthStoreManager, getUniqueHostnameString, useAuthScw };
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { EmptySession } from "./constants.js";
|
|
2
|
-
import {
|
|
2
|
+
import { COOKIE_AGE, DEFAULT_AUTH_SUFFIX_KEY, DOMAIN, getUniqueHostnameString } from "./useAuthScw/authStoreManager/constants.js";
|
|
3
|
+
import { AuthStoreManager, createAuthStoreManager } from "./useAuthScw/authStoreManager/authStoreManager.js";
|
|
3
4
|
import { AuthScwProvider, useAuthScw } from "./useAuthScw/AuthScwProvider.js";
|
|
4
|
-
export { AuthScwProvider, AuthStoreManager, EmptySession, useAuthScw };
|
|
5
|
+
export { AuthScwProvider, AuthStoreManager, COOKIE_AGE, DEFAULT_AUTH_SUFFIX_KEY, DOMAIN, EmptySession, createAuthStoreManager, getUniqueHostnameString, useAuthScw };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
//#region ../types/dist/index.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* A utility used for XOR: marks every key that is only in T (and not in U)
|
|
4
|
+
* as optional-never, effectively requiring those keys to be absent.
|
|
5
|
+
*/
|
|
6
|
+
export type Without<T, U> = Partial<Record<Exclude<keyof T, keyof U>, never>>;
|
|
7
|
+
/**
|
|
8
|
+
* Exclusive-or of two object types: produces a type that is either
|
|
9
|
+
* (T without any U-specific keys) OR (U without any T-specific keys),
|
|
10
|
+
* so only one of them may be present. For non-object unions it falls back to T | U.
|
|
11
|
+
*/
|
|
12
|
+
export type SingleXOR<T, U> = T | U extends object ? (Without<T, U> & U) | (Without<U, T> & T) : T | U;
|
|
13
|
+
//#endregion
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { AuthStoreManagerInstance } from "../useAuthScw/authStoreManager/authStoreManager.js";
|
|
2
|
+
import "../useAuthScw/authStoreManager/index.js";
|
|
1
3
|
import { Iamv1alpha1 } from "./iam/index.js";
|
|
2
4
|
import { audienceIdSchema, jwtSchema } from "../zodSchemas.js";
|
|
3
5
|
import { z } from "zod";
|
|
@@ -11,7 +13,7 @@ export type OnError = (onError: {
|
|
|
11
13
|
extra?: Record<string, unknown>;
|
|
12
14
|
}) => void;
|
|
13
15
|
type GetJWT = (audienceId?: string) => Promise<(EncodedJWT & {
|
|
14
|
-
source: 'refreshSession' | '
|
|
16
|
+
source: 'refreshSession' | 'storage';
|
|
15
17
|
}) | undefined>;
|
|
16
18
|
type GetJwtTokenResponse = NonNullable<Awaited<ReturnType<GetJWT>>>['token'] | undefined;
|
|
17
19
|
export type AuthScwContextType = {
|
|
@@ -25,6 +27,7 @@ export type AuthScwContextType = {
|
|
|
25
27
|
getJwtToken: (audienceId?: string) => Promise<GetJwtTokenResponse>;
|
|
26
28
|
logout: () => void;
|
|
27
29
|
authenticated: boolean;
|
|
30
|
+
storeManager: AuthStoreManagerInstance;
|
|
28
31
|
};
|
|
29
32
|
export declare class IamV1alpha1API extends API {
|
|
30
33
|
deleteJWT: Iamv1alpha1.API['deleteJWT'];
|
|
@@ -45,4 +48,19 @@ export type CookieConfigType = {
|
|
|
45
48
|
sameSite: 'lax' | 'none' | 'strict' | boolean | undefined;
|
|
46
49
|
secure: boolean;
|
|
47
50
|
};
|
|
51
|
+
export type CookieOptions = {
|
|
52
|
+
/** Suffix appended to storage keys to namespace sessions. */
|
|
53
|
+
suffix: string;
|
|
54
|
+
/** Cookie attributes (httpOnly, path, sameSite, secure). Defaults to `COOKIE_CONFIG`. */
|
|
55
|
+
config?: CookieConfigType;
|
|
56
|
+
/** Cookie max-age in seconds. Defaults to `COOKIE_AGE` (31 days). */
|
|
57
|
+
age?: number;
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* Where the JWT and audienceId are persisted.
|
|
61
|
+
*
|
|
62
|
+
* - `'cookie'` (default): backward-compatible, allows synchronisation across tabs.
|
|
63
|
+
* - `'localStorage'`: scoped to a single origin, survives reloads, avoids cookie size limits.
|
|
64
|
+
*/
|
|
65
|
+
export type StorageType = 'cookie' | 'localStorage';
|
|
48
66
|
//#endregion
|
|
@@ -1,13 +1,28 @@
|
|
|
1
|
-
import { AuthScwContextType, ConfigAuthProvider,
|
|
1
|
+
import { AuthScwContextType, ConfigAuthProvider, CookieOptions, OnError } from "../types/index.js";
|
|
2
|
+
import { SingleXOR } from "../types/dist/index.js";
|
|
2
3
|
import { ReactNode } from "react";
|
|
3
4
|
//#region src/useAuthScw/AuthScwProvider.d.ts
|
|
5
|
+
type CookieStorageOptions = {
|
|
6
|
+
storageType?: 'cookie';
|
|
7
|
+
cookie: CookieOptions;
|
|
8
|
+
};
|
|
9
|
+
type LocalStorageOptions = {
|
|
10
|
+
storageType: 'localStorage';
|
|
11
|
+
cookie?: CookieOptions;
|
|
12
|
+
};
|
|
4
13
|
export type AuthProviderParamType = ConfigAuthProvider & {
|
|
5
14
|
children: ReactNode;
|
|
6
|
-
cookieSuffix: string;
|
|
7
15
|
urlParamTokenName: string;
|
|
8
16
|
onError?: OnError;
|
|
9
|
-
|
|
10
|
-
|
|
17
|
+
/**
|
|
18
|
+
* When `true` and `storageType` is `'localStorage'`, any JWT/audienceId
|
|
19
|
+
* previously stored in cookies will be migrated to localStorage on
|
|
20
|
+
* initialization, then removed from cookies. Useful for live migrations.
|
|
21
|
+
*
|
|
22
|
+
* Defaults to `false`.
|
|
23
|
+
*/
|
|
24
|
+
migrateFromCookie?: boolean;
|
|
25
|
+
} & SingleXOR<CookieStorageOptions, LocalStorageOptions>;
|
|
11
26
|
export declare const useAuthScw: () => AuthScwContextType;
|
|
12
|
-
export declare const AuthScwProvider: ({ clientSettings, IamV1Alpha1, IamUnauthenticatedV1Alpha1, children,
|
|
27
|
+
export declare const AuthScwProvider: ({ clientSettings, IamV1Alpha1, IamUnauthenticatedV1Alpha1, children, cookie, urlParamTokenName, onError, storageType, migrateFromCookie }: AuthProviderParamType) => import("react").JSX.Element;
|
|
13
28
|
//#endregion
|
|
@@ -1,56 +1,66 @@
|
|
|
1
1
|
import { isExpired } from "../isExpired.js";
|
|
2
|
-
import {
|
|
2
|
+
import { createAuthStoreManager } from "./authStoreManager/authStoreManager.js";
|
|
3
3
|
import { clientSingleton } from "./createClient.js";
|
|
4
|
-
import { decodeToken, encodeToken,
|
|
4
|
+
import { decodeToken, encodeToken, getStoredJWT, refreshSession } from "./helpers.js";
|
|
5
5
|
import { proxyJwt } from "./proxyJwt.js";
|
|
6
6
|
import { createContext, useCallback, useContext, useMemo, useState } from "react";
|
|
7
7
|
import { jsx } from "react/jsx-runtime";
|
|
8
8
|
//#region src/useAuthScw/AuthScwProvider.tsx
|
|
9
9
|
const AuthScwContext = createContext({});
|
|
10
10
|
const useAuthScw = () => useContext(AuthScwContext);
|
|
11
|
-
const AuthScwProvider = ({ clientSettings, IamV1Alpha1, IamUnauthenticatedV1Alpha1, children,
|
|
12
|
-
|
|
11
|
+
const AuthScwProvider = ({ clientSettings, IamV1Alpha1, IamUnauthenticatedV1Alpha1, children, cookie, urlParamTokenName, onError, storageType = "cookie", migrateFromCookie = false }) => {
|
|
12
|
+
const storeManager = useMemo(() => createAuthStoreManager({
|
|
13
|
+
storageType,
|
|
14
|
+
cookieConfig: cookie?.config,
|
|
15
|
+
cookieAge: cookie?.age,
|
|
16
|
+
suffixKey: cookie?.suffix
|
|
17
|
+
}), [
|
|
18
|
+
storageType,
|
|
19
|
+
cookie?.config,
|
|
20
|
+
cookie?.age,
|
|
21
|
+
cookie?.suffix
|
|
22
|
+
]);
|
|
13
23
|
clientSingleton.setAPIsAndSettings({
|
|
14
24
|
clientSettings,
|
|
15
25
|
IamV1Alpha1,
|
|
16
26
|
IamUnauthenticatedV1Alpha1
|
|
17
27
|
});
|
|
18
|
-
const initAudienceId =
|
|
19
|
-
|
|
28
|
+
const initAudienceId = () => {
|
|
29
|
+
if (storageType === "localStorage" && migrateFromCookie) storeManager.migrateCookieToLocalStorage();
|
|
20
30
|
const currentUrl = new URL(globalThis.location.href);
|
|
21
31
|
const tokenRaw = currentUrl.searchParams.get(urlParamTokenName);
|
|
22
32
|
if (tokenRaw) {
|
|
23
33
|
currentUrl.searchParams.delete(urlParamTokenName);
|
|
24
|
-
const token = decodeToken(tokenRaw);
|
|
34
|
+
const token = decodeToken(tokenRaw, storeManager);
|
|
25
35
|
if (token) {
|
|
26
36
|
const jwtProxy = proxyJwt(token);
|
|
27
|
-
if (jwtProxy.jwt)
|
|
37
|
+
if (jwtProxy.jwt) storeManager.setJwt({ jwtInfo: jwtProxy });
|
|
28
38
|
globalThis.history.replaceState({}, "", currentUrl);
|
|
29
39
|
return jwtProxy.jwt?.audienceId;
|
|
30
40
|
}
|
|
31
41
|
}
|
|
32
|
-
const audienceId =
|
|
42
|
+
const audienceId = storeManager.getAudienceId();
|
|
33
43
|
if (audienceId) {
|
|
34
|
-
if (
|
|
44
|
+
if (storeManager.getJwt(audienceId)) return audienceId;
|
|
35
45
|
}
|
|
36
|
-
}
|
|
46
|
+
};
|
|
37
47
|
const [currentAudienceId, setCurrentAudienceId] = useState(() => initAudienceId());
|
|
38
48
|
const setJWT = useCallback((jwtInfoParam) => {
|
|
39
49
|
const currentJWT = proxyJwt(jwtInfoParam);
|
|
40
50
|
if (currentJWT.jwt?.audienceId) {
|
|
41
|
-
|
|
51
|
+
storeManager.setJwt({ jwtInfo: currentJWT });
|
|
42
52
|
setCurrentAudienceId(currentJWT.jwt.audienceId);
|
|
43
53
|
}
|
|
44
|
-
}, [setCurrentAudienceId]);
|
|
54
|
+
}, [setCurrentAudienceId, storeManager]);
|
|
45
55
|
const getJWT = useCallback(async (audienceId = currentAudienceId) => {
|
|
46
56
|
if (audienceId) {
|
|
47
|
-
const
|
|
48
|
-
if (
|
|
49
|
-
const { renewToken, token, jwt: currentJWT } =
|
|
57
|
+
const storedJWT = getStoredJWT(audienceId, storeManager);
|
|
58
|
+
if (storedJWT?.jwt) {
|
|
59
|
+
const { renewToken, token, jwt: currentJWT } = storedJWT;
|
|
50
60
|
if (currentJWT.expiresAt && !isExpired(new Date(currentJWT.expiresAt))) return {
|
|
51
61
|
jwt: currentJWT,
|
|
52
62
|
renewToken,
|
|
53
|
-
source: "
|
|
63
|
+
source: "storage",
|
|
54
64
|
token
|
|
55
65
|
};
|
|
56
66
|
const jwtInfoData = await refreshSession({
|
|
@@ -72,7 +82,8 @@ const AuthScwProvider = ({ clientSettings, IamV1Alpha1, IamUnauthenticatedV1Alph
|
|
|
72
82
|
}, [
|
|
73
83
|
currentAudienceId,
|
|
74
84
|
setJWT,
|
|
75
|
-
onError
|
|
85
|
+
onError,
|
|
86
|
+
storeManager
|
|
76
87
|
]);
|
|
77
88
|
const getJwtToken = useCallback(async (audienceId) => {
|
|
78
89
|
const currentJWT = await getJWT(audienceId);
|
|
@@ -80,37 +91,42 @@ const AuthScwProvider = ({ clientSettings, IamV1Alpha1, IamUnauthenticatedV1Alph
|
|
|
80
91
|
}, [getJWT]);
|
|
81
92
|
const logout = useCallback(() => {
|
|
82
93
|
if (currentAudienceId) {
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
if (
|
|
94
|
+
const storedJWT = getStoredJWT(currentAudienceId, storeManager);
|
|
95
|
+
storeManager.deleteJwt(currentAudienceId);
|
|
96
|
+
if (storedJWT?.jwt) {
|
|
86
97
|
const { deleteJWT } = clientSingleton.createClient({ getAsyncToken: getJwtToken });
|
|
87
|
-
deleteJWT?.({ jti:
|
|
98
|
+
deleteJWT?.({ jti: storedJWT.jwt.jti }).catch(() => null);
|
|
88
99
|
}
|
|
89
100
|
}
|
|
90
|
-
|
|
101
|
+
storeManager.deleteAudienceId();
|
|
91
102
|
setCurrentAudienceId(void 0);
|
|
92
|
-
}, [
|
|
103
|
+
}, [
|
|
104
|
+
getJwtToken,
|
|
105
|
+
currentAudienceId,
|
|
106
|
+
storeManager
|
|
107
|
+
]);
|
|
93
108
|
const jti = useMemo(() => {
|
|
94
109
|
if (currentAudienceId) {
|
|
95
|
-
const
|
|
96
|
-
if (
|
|
110
|
+
const storedJWT = getStoredJWT(currentAudienceId, storeManager);
|
|
111
|
+
if (storedJWT?.jwt) return storedJWT.jwt.jti;
|
|
97
112
|
}
|
|
98
|
-
}, [currentAudienceId]);
|
|
99
|
-
const isAuthenticated = useMemo(() => Boolean(currentAudienceId &&
|
|
113
|
+
}, [currentAudienceId, storeManager]);
|
|
114
|
+
const isAuthenticated = useMemo(() => Boolean(currentAudienceId && getStoredJWT(currentAudienceId, storeManager)), [currentAudienceId, storeManager]);
|
|
100
115
|
const setAudienceId = useCallback((audienceId) => {
|
|
101
116
|
setCurrentAudienceId(audienceId);
|
|
102
117
|
}, []);
|
|
103
118
|
const value = useMemo(() => ({
|
|
104
119
|
audienceId: currentAudienceId ?? void 0,
|
|
105
120
|
authenticated: isAuthenticated,
|
|
106
|
-
decodeToken,
|
|
121
|
+
decodeToken: (encodedToken) => decodeToken(encodedToken, storeManager),
|
|
107
122
|
encodeToken,
|
|
108
123
|
getJWT,
|
|
109
124
|
getJwtToken,
|
|
110
125
|
jti,
|
|
111
126
|
logout,
|
|
112
127
|
setAudienceId,
|
|
113
|
-
setJWT
|
|
128
|
+
setJWT,
|
|
129
|
+
storeManager
|
|
114
130
|
}), [
|
|
115
131
|
getJWT,
|
|
116
132
|
getJwtToken,
|
|
@@ -119,7 +135,8 @@ const AuthScwProvider = ({ clientSettings, IamV1Alpha1, IamUnauthenticatedV1Alph
|
|
|
119
135
|
setAudienceId,
|
|
120
136
|
currentAudienceId,
|
|
121
137
|
isAuthenticated,
|
|
122
|
-
jti
|
|
138
|
+
jti,
|
|
139
|
+
storeManager
|
|
123
140
|
]);
|
|
124
141
|
return /* @__PURE__ */ jsx(AuthScwContext, {
|
|
125
142
|
value,
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { CreateAuthStoreManagerOptions } from "./types.js";
|
|
2
|
+
import { COOKIE_AGE, DEFAULT_AUTH_SUFFIX_KEY, DOMAIN, getUniqueHostnameString } from "./constants.js";
|
|
3
|
+
import { AudienceIdType, EncodedJWT, StorageType } from "../../types/index.js";
|
|
4
|
+
//#region src/useAuthScw/authStoreManager/authStoreManager.d.ts
|
|
5
|
+
export type AuthStoreManagerInstance = ReturnType<typeof createAuthStoreManager>;
|
|
6
|
+
/**
|
|
7
|
+
* Factory that creates an isolated AuthStoreManager instance with its own
|
|
8
|
+
* immutable `storageType`, `suffixKey`, and cookie config (`cookieConfig`/`cookieAge`).
|
|
9
|
+
*
|
|
10
|
+
* Each instance owns its own backends, so multiple providers can coexist
|
|
11
|
+
* without clobbering each other's config. The `storageType` is set at creation
|
|
12
|
+
* time and cannot be changed afterwards — use {@link createAuthStoreManager}
|
|
13
|
+
* to create a new instance with a different storage type.
|
|
14
|
+
*
|
|
15
|
+
* Accepts either an options object `{ storageType, cookieConfig, cookieAge }`
|
|
16
|
+
* or, for backward compatibility, a bare `StorageType` string.
|
|
17
|
+
*
|
|
18
|
+
* For backward compatibility, a default singleton `AuthStoreManager` is also
|
|
19
|
+
* exported below.
|
|
20
|
+
*/
|
|
21
|
+
export declare const createAuthStoreManager: (param?: StorageType | CreateAuthStoreManagerOptions) => {
|
|
22
|
+
COOKIE_AGE: number;
|
|
23
|
+
DOMAIN: string;
|
|
24
|
+
deleteAllJwts(): void;
|
|
25
|
+
deleteAudienceId(): void;
|
|
26
|
+
deleteJwt(audienceId: string): void;
|
|
27
|
+
getAudienceId(): string | null;
|
|
28
|
+
getJwt(audienceId: string): EncodedJWT | null;
|
|
29
|
+
getKeyAudienceId(): string;
|
|
30
|
+
getKeySession(audienceId: string): string;
|
|
31
|
+
getStorageType(): StorageType;
|
|
32
|
+
getUniqueHostnameString: typeof getUniqueHostnameString;
|
|
33
|
+
/**
|
|
34
|
+
* Migrates any existing JWT and audienceId stored in cookies to localStorage,
|
|
35
|
+
* then removes the cookie entries. Safe to call repeatedly — it is a no-op when
|
|
36
|
+
* nothing is stored in cookies.
|
|
37
|
+
*
|
|
38
|
+
* Accesses the cookie and localStorage backends directly without changing
|
|
39
|
+
* the instance's active `storageType`.
|
|
40
|
+
*/
|
|
41
|
+
migrateCookieToLocalStorage(): {
|
|
42
|
+
migrated: boolean;
|
|
43
|
+
audienceId?: string;
|
|
44
|
+
};
|
|
45
|
+
setAudienceId(audienceId: string): void;
|
|
46
|
+
setJwt({ jwtInfo, setAudienceId }: {
|
|
47
|
+
jwtInfo: EncodedJWT;
|
|
48
|
+
setAudienceId?: boolean;
|
|
49
|
+
}): void;
|
|
50
|
+
readonly SUFFIX_KEY: string;
|
|
51
|
+
typeGuardAudienceId(untypedObject: unknown): untypedObject is AudienceIdType;
|
|
52
|
+
typeGuardJWT(untypedObject: unknown): untypedObject is EncodedJWT;
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* @deprecated Use `useAuthScw().storeManager` or `createAuthStoreManager()` instead.
|
|
56
|
+
* The singleton bypasses the provider's configuration and shares global state across all providers.
|
|
57
|
+
*/
|
|
58
|
+
export declare const AuthStoreManager: AuthStoreManagerInstance;
|
|
59
|
+
//#endregion
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { COOKIE_AGE, DOMAIN, getUniqueHostnameString } from "./constants.js";
|
|
2
|
+
import { audienceIdSchema, jwtSchema } from "../../zodSchemas.js";
|
|
3
|
+
import { createCookieBackend } from "./cookieBackend.js";
|
|
4
|
+
import { createLocalStorageBackend } from "./localStorageBackend.js";
|
|
5
|
+
//#region src/useAuthScw/authStoreManager/authStoreManager.ts
|
|
6
|
+
const KEY_SESSION = "_scw_session";
|
|
7
|
+
const KEY_AUDIENCE_ID = "_scw_audience_id";
|
|
8
|
+
const DEFAULT_SUFFIX_KEY = "";
|
|
9
|
+
const buildBackends = (options) => ({
|
|
10
|
+
cookie: createCookieBackend({
|
|
11
|
+
cookieConfig: options.cookieConfig,
|
|
12
|
+
cookieAge: options.cookieAge
|
|
13
|
+
}),
|
|
14
|
+
localStorage: createLocalStorageBackend()
|
|
15
|
+
});
|
|
16
|
+
const normalizeOptions = (param) => typeof param === "string" ? { storageType: param } : param ?? {};
|
|
17
|
+
/**
|
|
18
|
+
* Factory that creates an isolated AuthStoreManager instance with its own
|
|
19
|
+
* immutable `storageType`, `suffixKey`, and cookie config (`cookieConfig`/`cookieAge`).
|
|
20
|
+
*
|
|
21
|
+
* Each instance owns its own backends, so multiple providers can coexist
|
|
22
|
+
* without clobbering each other's config. The `storageType` is set at creation
|
|
23
|
+
* time and cannot be changed afterwards — use {@link createAuthStoreManager}
|
|
24
|
+
* to create a new instance with a different storage type.
|
|
25
|
+
*
|
|
26
|
+
* Accepts either an options object `{ storageType, cookieConfig, cookieAge }`
|
|
27
|
+
* or, for backward compatibility, a bare `StorageType` string.
|
|
28
|
+
*
|
|
29
|
+
* For backward compatibility, a default singleton `AuthStoreManager` is also
|
|
30
|
+
* exported below.
|
|
31
|
+
*/
|
|
32
|
+
const createAuthStoreManager = (param) => {
|
|
33
|
+
const options = normalizeOptions(param);
|
|
34
|
+
const storageType = options.storageType ?? "cookie";
|
|
35
|
+
const suffixKey = options.suffixKey ? `_${options.suffixKey}` : DEFAULT_SUFFIX_KEY;
|
|
36
|
+
const backends = buildBackends(options);
|
|
37
|
+
const getBackend = () => backends[storageType];
|
|
38
|
+
const instance = {
|
|
39
|
+
COOKIE_AGE,
|
|
40
|
+
DOMAIN,
|
|
41
|
+
deleteAllJwts() {
|
|
42
|
+
const prefix = instance.getKeySession("");
|
|
43
|
+
getBackend().deleteAllWithPrefix(prefix);
|
|
44
|
+
},
|
|
45
|
+
deleteAudienceId() {
|
|
46
|
+
getBackend().delete(instance.getKeyAudienceId());
|
|
47
|
+
},
|
|
48
|
+
deleteJwt(audienceId) {
|
|
49
|
+
getBackend().delete(instance.getKeySession(audienceId));
|
|
50
|
+
},
|
|
51
|
+
getAudienceId() {
|
|
52
|
+
const parsed = getBackend().get(instance.getKeyAudienceId());
|
|
53
|
+
const resultParsed = audienceIdSchema.safeParse(parsed);
|
|
54
|
+
if (resultParsed.success) return resultParsed.data.audienceId;
|
|
55
|
+
return null;
|
|
56
|
+
},
|
|
57
|
+
getJwt(audienceId) {
|
|
58
|
+
const parsed = getBackend().get(instance.getKeySession(audienceId));
|
|
59
|
+
const resultParsed = jwtSchema.safeParse(parsed);
|
|
60
|
+
if (resultParsed.success) return resultParsed.data;
|
|
61
|
+
if (resultParsed.error) {}
|
|
62
|
+
return null;
|
|
63
|
+
},
|
|
64
|
+
getKeyAudienceId() {
|
|
65
|
+
return `${KEY_AUDIENCE_ID}${suffixKey}`;
|
|
66
|
+
},
|
|
67
|
+
getKeySession(audienceId) {
|
|
68
|
+
return `${KEY_SESSION}${suffixKey}_${audienceId}`;
|
|
69
|
+
},
|
|
70
|
+
getStorageType() {
|
|
71
|
+
return storageType;
|
|
72
|
+
},
|
|
73
|
+
getUniqueHostnameString,
|
|
74
|
+
/**
|
|
75
|
+
* Migrates any existing JWT and audienceId stored in cookies to localStorage,
|
|
76
|
+
* then removes the cookie entries. Safe to call repeatedly — it is a no-op when
|
|
77
|
+
* nothing is stored in cookies.
|
|
78
|
+
*
|
|
79
|
+
* Accesses the cookie and localStorage backends directly without changing
|
|
80
|
+
* the instance's active `storageType`.
|
|
81
|
+
*/
|
|
82
|
+
migrateCookieToLocalStorage() {
|
|
83
|
+
const cookieBackend = backends.cookie;
|
|
84
|
+
const localStorageBackend = backends.localStorage;
|
|
85
|
+
const audienceIdRaw = cookieBackend.get(instance.getKeyAudienceId());
|
|
86
|
+
const audienceIdParsed = audienceIdSchema.safeParse(audienceIdRaw);
|
|
87
|
+
if (!audienceIdParsed.success) return { migrated: false };
|
|
88
|
+
const { audienceId } = audienceIdParsed.data;
|
|
89
|
+
const jwtRaw = cookieBackend.get(instance.getKeySession(audienceId));
|
|
90
|
+
const jwtParsed = jwtSchema.safeParse(jwtRaw);
|
|
91
|
+
if (!jwtParsed.success) {
|
|
92
|
+
localStorageBackend.set(instance.getKeyAudienceId(), { audienceId });
|
|
93
|
+
cookieBackend.delete(instance.getKeyAudienceId());
|
|
94
|
+
return {
|
|
95
|
+
migrated: true,
|
|
96
|
+
audienceId
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
localStorageBackend.set(instance.getKeySession(audienceId), jwtParsed.data);
|
|
100
|
+
localStorageBackend.set(instance.getKeyAudienceId(), { audienceId });
|
|
101
|
+
cookieBackend.delete(instance.getKeySession(audienceId));
|
|
102
|
+
cookieBackend.delete(instance.getKeyAudienceId());
|
|
103
|
+
return {
|
|
104
|
+
migrated: true,
|
|
105
|
+
audienceId
|
|
106
|
+
};
|
|
107
|
+
},
|
|
108
|
+
setAudienceId(audienceId) {
|
|
109
|
+
getBackend().set(instance.getKeyAudienceId(), { audienceId });
|
|
110
|
+
},
|
|
111
|
+
setJwt({ jwtInfo, setAudienceId = true }) {
|
|
112
|
+
const audienceId = jwtInfo.jwt?.audienceId;
|
|
113
|
+
getBackend().set(instance.getKeySession(audienceId), jwtInfo);
|
|
114
|
+
if (setAudienceId) instance.setAudienceId(audienceId);
|
|
115
|
+
},
|
|
116
|
+
get SUFFIX_KEY() {
|
|
117
|
+
return suffixKey;
|
|
118
|
+
},
|
|
119
|
+
typeGuardAudienceId(untypedObject) {
|
|
120
|
+
return audienceIdSchema.safeParse(untypedObject).success;
|
|
121
|
+
},
|
|
122
|
+
typeGuardJWT(untypedObject) {
|
|
123
|
+
return jwtSchema.safeParse(untypedObject).success;
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
return instance;
|
|
127
|
+
};
|
|
128
|
+
let authStoreManagerSingletonWarned = false;
|
|
129
|
+
const warnSingletonUsage = () => {
|
|
130
|
+
if (authStoreManagerSingletonWarned) return;
|
|
131
|
+
authStoreManagerSingletonWarned = true;
|
|
132
|
+
console.warn("`AuthStoreManager` singleton is deprecated. It uses shared global state that bypasses the provider. Use `useAuthScw().storeManager` to access the instance managed by `<AuthScwProvider>` instead. If you need a standalone instance, create one via `createAuthStoreManager()`.");
|
|
133
|
+
};
|
|
134
|
+
/**
|
|
135
|
+
* @deprecated Use `useAuthScw().storeManager` or `createAuthStoreManager()` instead.
|
|
136
|
+
* The singleton bypasses the provider's configuration and shares global state across all providers.
|
|
137
|
+
*/
|
|
138
|
+
const AuthStoreManager = new Proxy(createAuthStoreManager(), { get(target, prop, receiver) {
|
|
139
|
+
warnSingletonUsage();
|
|
140
|
+
return Reflect.get(target, prop, receiver);
|
|
141
|
+
} });
|
|
142
|
+
//#endregion
|
|
143
|
+
export { AuthStoreManager, createAuthStoreManager };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
//#region src/useAuthScw/authStoreManager/constants.d.ts
|
|
2
|
+
export declare const COOKIE_AGE: number;
|
|
3
|
+
export declare const DOMAIN: string;
|
|
4
|
+
export declare const getUniqueHostnameString: (inputString: string) => string;
|
|
5
|
+
export declare const DEFAULT_AUTH_SUFFIX_KEY: string;
|
|
6
|
+
//#endregion
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
//#region src/useAuthScw/authStoreManager/constants.ts
|
|
2
|
+
const COOKIE_AGE = 2678400;
|
|
3
|
+
const DOMAIN = typeof globalThis !== "undefined" && "location" in globalThis ? globalThis.location.hostname : "";
|
|
4
|
+
const getUniqueHostnameString = (inputString) => inputString.replaceAll(".", "_").replaceAll("-", "_");
|
|
5
|
+
const DEFAULT_AUTH_SUFFIX_KEY = getUniqueHostnameString(DOMAIN);
|
|
6
|
+
//#endregion
|
|
7
|
+
export { COOKIE_AGE, DEFAULT_AUTH_SUFFIX_KEY, DOMAIN, getUniqueHostnameString };
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { COOKIE_CONFIG } from "../../constants.js";
|
|
2
|
+
import { DOMAIN } from "./constants.js";
|
|
3
|
+
import { parseCookie, stringifySetCookie } from "cookie";
|
|
4
|
+
//#region src/useAuthScw/authStoreManager/cookieBackend.ts
|
|
5
|
+
const getCookie = (key) => {
|
|
6
|
+
const cookies = parseCookie(document.cookie);
|
|
7
|
+
if (key in cookies && cookies[key]) {
|
|
8
|
+
const cookieSessionString = cookies[key];
|
|
9
|
+
if (cookieSessionString) return JSON.parse(cookieSessionString);
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
return null;
|
|
13
|
+
};
|
|
14
|
+
const createCookieBackend = (options = {}) => {
|
|
15
|
+
const config = options.cookieConfig ?? COOKIE_CONFIG;
|
|
16
|
+
const maxAgeDefault = options.cookieAge ?? 2678400;
|
|
17
|
+
const setCookie = (value, key, maxAge = maxAgeDefault) => {
|
|
18
|
+
document.cookie = stringifySetCookie({
|
|
19
|
+
name: key,
|
|
20
|
+
value: JSON.stringify(value),
|
|
21
|
+
domain: DOMAIN,
|
|
22
|
+
httpOnly: config.httpOnly,
|
|
23
|
+
maxAge,
|
|
24
|
+
path: config.path,
|
|
25
|
+
sameSite: config.sameSite,
|
|
26
|
+
secure: config.secure
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
const deleteCookie = (key) => {
|
|
30
|
+
document.cookie = stringifySetCookie({
|
|
31
|
+
name: key,
|
|
32
|
+
value: "",
|
|
33
|
+
domain: DOMAIN,
|
|
34
|
+
httpOnly: config.httpOnly,
|
|
35
|
+
maxAge: -1,
|
|
36
|
+
path: config.path,
|
|
37
|
+
sameSite: config.sameSite,
|
|
38
|
+
secure: config.secure
|
|
39
|
+
});
|
|
40
|
+
};
|
|
41
|
+
return {
|
|
42
|
+
delete(key) {
|
|
43
|
+
deleteCookie(key);
|
|
44
|
+
},
|
|
45
|
+
deleteAllWithPrefix(prefix) {
|
|
46
|
+
document.cookie.split(";").forEach((cookieParam) => {
|
|
47
|
+
let [audienceId] = cookieParam.split("=");
|
|
48
|
+
if (audienceId) {
|
|
49
|
+
audienceId = audienceId.trim();
|
|
50
|
+
if (audienceId.startsWith(prefix)) deleteCookie(audienceId);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
},
|
|
54
|
+
get(key) {
|
|
55
|
+
return getCookie(key);
|
|
56
|
+
},
|
|
57
|
+
set(key, value, maxAge = maxAgeDefault) {
|
|
58
|
+
setCookie(value, key, maxAge);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
};
|
|
62
|
+
createCookieBackend();
|
|
63
|
+
//#endregion
|
|
64
|
+
export { createCookieBackend };
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { CreateAuthStoreManagerOptions } from "./types.js";
|
|
2
|
+
import { COOKIE_AGE, DEFAULT_AUTH_SUFFIX_KEY, DOMAIN, getUniqueHostnameString } from "./constants.js";
|
|
3
|
+
import { AuthStoreManager, AuthStoreManagerInstance, createAuthStoreManager } from "./authStoreManager.js";
|
|
4
|
+
export { AuthStoreManager, type AuthStoreManagerInstance, COOKIE_AGE, DEFAULT_AUTH_SUFFIX_KEY, DOMAIN, createAuthStoreManager, getUniqueHostnameString };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
//#region src/useAuthScw/authStoreManager/localStorageBackend.ts
|
|
2
|
+
/**
|
|
3
|
+
* localStorage-based storage backend.
|
|
4
|
+
*
|
|
5
|
+
* Reads/writes are wrapped in try/catch because `localStorage` can throw in
|
|
6
|
+
* restricted environments (e.g. sandboxed iframes, private mode in some
|
|
7
|
+
* browsers, or when the quota is exceeded).
|
|
8
|
+
*/
|
|
9
|
+
const createLocalStorageBackend = () => ({
|
|
10
|
+
delete(key) {
|
|
11
|
+
try {
|
|
12
|
+
globalThis.localStorage.removeItem(key);
|
|
13
|
+
} catch {}
|
|
14
|
+
},
|
|
15
|
+
deleteAllWithPrefix(prefix) {
|
|
16
|
+
try {
|
|
17
|
+
const keysToRemove = [];
|
|
18
|
+
for (let i = 0; i < globalThis.localStorage.length; i++) {
|
|
19
|
+
const key = globalThis.localStorage.key(i);
|
|
20
|
+
if (key?.startsWith(prefix)) keysToRemove.push(key);
|
|
21
|
+
}
|
|
22
|
+
keysToRemove.forEach((key) => globalThis.localStorage.removeItem(key));
|
|
23
|
+
} catch {}
|
|
24
|
+
},
|
|
25
|
+
get(key) {
|
|
26
|
+
try {
|
|
27
|
+
const raw = globalThis.localStorage.getItem(key);
|
|
28
|
+
if (!raw) return null;
|
|
29
|
+
return JSON.parse(raw);
|
|
30
|
+
} catch {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
set(key, value) {
|
|
35
|
+
try {
|
|
36
|
+
globalThis.localStorage.setItem(key, JSON.stringify(value));
|
|
37
|
+
} catch {}
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
//#endregion
|
|
41
|
+
export { createLocalStorageBackend };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { CookieConfigType, StorageType } from "../../types/index.js";
|
|
2
|
+
//#region src/useAuthScw/authStoreManager/types.d.ts
|
|
3
|
+
export type CreateAuthStoreManagerOptions = {
|
|
4
|
+
storageType?: StorageType;
|
|
5
|
+
cookieConfig?: CookieConfigType;
|
|
6
|
+
cookieAge?: number;
|
|
7
|
+
suffixKey?: string;
|
|
8
|
+
};
|
|
9
|
+
//#endregion
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { AuthStoreManager } from "./authStoreManager.js";
|
|
2
1
|
import { clientSingleton } from "./createClient.js";
|
|
3
2
|
import { Errors } from "@scaleway/sdk-client";
|
|
4
3
|
//#region src/useAuthScw/helpers.ts
|
|
@@ -25,16 +24,16 @@ const refreshSession = ({ paramsRenewRequest, setJWT, onError }) => {
|
|
|
25
24
|
});
|
|
26
25
|
return refreshSessionPromise;
|
|
27
26
|
};
|
|
28
|
-
const decodeToken = (encodedToken) => {
|
|
27
|
+
const decodeToken = (encodedToken, storeManager) => {
|
|
29
28
|
try {
|
|
30
29
|
const decodedTokenRaw = JSON.parse(atob(encodedToken));
|
|
31
|
-
if (
|
|
30
|
+
if (storeManager.typeGuardJWT(decodedTokenRaw)) return decodedTokenRaw;
|
|
32
31
|
return null;
|
|
33
32
|
} catch {
|
|
34
33
|
return null;
|
|
35
34
|
}
|
|
36
35
|
};
|
|
37
36
|
const encodeToken = (jwt) => btoa(JSON.stringify(jwt));
|
|
38
|
-
const
|
|
37
|
+
const getStoredJWT = (audienceId, storeManager) => storeManager.getJwt(audienceId);
|
|
39
38
|
//#endregion
|
|
40
|
-
export { decodeToken, encodeToken,
|
|
39
|
+
export { decodeToken, encodeToken, getStoredJWT, refreshSession };
|
package/dist/zodSchemas.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { Iamv1alpha1 } from "./types/iam/index.js";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
//#region src/zodSchemas.d.ts
|
|
4
|
-
type TransformJWT = Omit<Iamv1alpha1.JWT, 'createdAt' | 'updatedAt' | 'expiresAt'> & {
|
|
4
|
+
type TransformJWT = Omit<Iamv1alpha1.JWT, 'createdAt' | 'updatedAt' | 'expiresAt' | 'srn'> & {
|
|
5
5
|
createdAt?: string | Date;
|
|
6
6
|
updatedAt?: string | Date;
|
|
7
7
|
expiresAt?: string | Date;
|
|
8
|
+
srn?: string;
|
|
8
9
|
};
|
|
9
10
|
type ValidateJWTSchema = {
|
|
10
11
|
jwt?: TransformJWT;
|
package/dist/zodSchemas.js
CHANGED
|
@@ -9,7 +9,8 @@ const rawJwtSchema = z.object({
|
|
|
9
9
|
issuerId: z.string().min(1),
|
|
10
10
|
jti: z.string().min(1),
|
|
11
11
|
updatedAt: z.union([z.string().min(1), z.date()]).optional(),
|
|
12
|
-
userAgent: z.string().min(1)
|
|
12
|
+
userAgent: z.string().min(1),
|
|
13
|
+
srn: z.string().optional()
|
|
13
14
|
}),
|
|
14
15
|
renewToken: z.string().min(1),
|
|
15
16
|
token: z.string().min(1)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scaleway/auth-scw",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Auth Scaleway",
|
|
5
5
|
"homepage": "https://github.com/scaleway/scaleway-lib/tree/main/packages/auth-scw",
|
|
6
6
|
"bugs": {
|
|
@@ -31,14 +31,15 @@
|
|
|
31
31
|
"zod": "4.6.2"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
|
-
"@scaleway/sdk-client": "2.
|
|
35
|
-
"@scaleway/sdk-iam": "2.
|
|
34
|
+
"@scaleway/sdk-client": "2.9.0",
|
|
35
|
+
"@scaleway/sdk-iam": "2.16.0",
|
|
36
|
+
"@scaleway/types": "1.2.0",
|
|
36
37
|
"@types/react": "19.3.0",
|
|
37
38
|
"react": "19.3.0"
|
|
38
39
|
},
|
|
39
40
|
"peerDependencies": {
|
|
40
|
-
"@scaleway/sdk-client": "2.
|
|
41
|
-
"@scaleway/sdk-iam": "2.
|
|
41
|
+
"@scaleway/sdk-client": "2.9.0",
|
|
42
|
+
"@scaleway/sdk-iam": "2.16.0",
|
|
42
43
|
"react": ">=18.0"
|
|
43
44
|
},
|
|
44
45
|
"scripts": {
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { AudienceIdType, EncodedJWT } from "../types/index.js";
|
|
2
|
-
//#region src/useAuthScw/authStoreManager.d.ts
|
|
3
|
-
declare const getUniqueHostnameString: (inputString: string) => string;
|
|
4
|
-
export declare const AuthStoreManager: {
|
|
5
|
-
COOKIE_AGE: number;
|
|
6
|
-
DOMAIN: string;
|
|
7
|
-
deleteAllJwts(): void;
|
|
8
|
-
deleteAudienceId(): void;
|
|
9
|
-
deleteJwt(audienceId: string): void;
|
|
10
|
-
getAudienceId(): string | null;
|
|
11
|
-
getJwt(audienceId: string): EncodedJWT | null;
|
|
12
|
-
getKeyAudienceId(): string;
|
|
13
|
-
getKeySession(audienceId: string): string;
|
|
14
|
-
getUniqueHostnameString: typeof getUniqueHostnameString;
|
|
15
|
-
SUFFIX_KEY: string;
|
|
16
|
-
setAudienceId(audienceId: string): void;
|
|
17
|
-
setJwt({ jwtInfo, setAudienceId }: {
|
|
18
|
-
jwtInfo: EncodedJWT;
|
|
19
|
-
setAudienceId?: boolean;
|
|
20
|
-
}): void;
|
|
21
|
-
setSuffixKey(key: string): void;
|
|
22
|
-
typeGuardAudienceId(untypedObject: unknown): untypedObject is AudienceIdType;
|
|
23
|
-
typeGuardJWT(untypedObject: unknown): untypedObject is EncodedJWT;
|
|
24
|
-
};
|
|
25
|
-
//#endregion
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
import { COOKIE_CONFIG } from "../constants.js";
|
|
2
|
-
import { audienceIdSchema, jwtSchema } from "../zodSchemas.js";
|
|
3
|
-
import { parseCookie, stringifySetCookie } from "cookie";
|
|
4
|
-
//#region src/useAuthScw/authStoreManager.ts
|
|
5
|
-
const KEY_SESSION = "_scw_session";
|
|
6
|
-
const KEY_AUDIENCE_ID = "_scw_audience_id";
|
|
7
|
-
const DEFAULT_SUFFIX_KEY = "";
|
|
8
|
-
const COOKIE_AGE = 2678400;
|
|
9
|
-
let cookieConfig = COOKIE_CONFIG;
|
|
10
|
-
const DOMAIN = typeof window !== "undefined" ? globalThis.location.hostname : "";
|
|
11
|
-
const getUniqueHostnameString = (inputString) => inputString.replaceAll(".", "_").replaceAll("-", "_");
|
|
12
|
-
const setCookieConfig = (COOKIE_CONFIG_PARAM) => cookieConfig = COOKIE_CONFIG_PARAM;
|
|
13
|
-
const setCookie = (value, key, maxAge = COOKIE_AGE) => {
|
|
14
|
-
document.cookie = stringifySetCookie({
|
|
15
|
-
name: key,
|
|
16
|
-
value: JSON.stringify(value),
|
|
17
|
-
domain: DOMAIN,
|
|
18
|
-
httpOnly: cookieConfig.httpOnly,
|
|
19
|
-
maxAge,
|
|
20
|
-
path: cookieConfig.path,
|
|
21
|
-
sameSite: cookieConfig.sameSite,
|
|
22
|
-
secure: cookieConfig.secure
|
|
23
|
-
});
|
|
24
|
-
};
|
|
25
|
-
const getCookie = (key) => {
|
|
26
|
-
const cookies = parseCookie(document.cookie);
|
|
27
|
-
if (key in cookies && cookies[key]) {
|
|
28
|
-
const cookieSessionString = cookies[key];
|
|
29
|
-
if (cookieSessionString) return JSON.parse(cookieSessionString);
|
|
30
|
-
return null;
|
|
31
|
-
}
|
|
32
|
-
return null;
|
|
33
|
-
};
|
|
34
|
-
const deleteCookie = (key) => {
|
|
35
|
-
document.cookie = stringifySetCookie({
|
|
36
|
-
name: key,
|
|
37
|
-
value: "",
|
|
38
|
-
domain: DOMAIN,
|
|
39
|
-
httpOnly: cookieConfig.httpOnly,
|
|
40
|
-
maxAge: -1,
|
|
41
|
-
path: cookieConfig.path,
|
|
42
|
-
sameSite: cookieConfig.sameSite,
|
|
43
|
-
secure: cookieConfig.secure
|
|
44
|
-
});
|
|
45
|
-
};
|
|
46
|
-
const AuthStoreManager = {
|
|
47
|
-
COOKIE_AGE,
|
|
48
|
-
DOMAIN,
|
|
49
|
-
deleteAllJwts() {
|
|
50
|
-
const prefix = AuthStoreManager.getKeySession("");
|
|
51
|
-
document.cookie.split(";").forEach((cookieParam) => {
|
|
52
|
-
let [audienceId] = cookieParam.split("=");
|
|
53
|
-
if (audienceId) {
|
|
54
|
-
audienceId = audienceId.trim();
|
|
55
|
-
if (audienceId.startsWith(prefix)) this.deleteJwt(audienceId);
|
|
56
|
-
}
|
|
57
|
-
});
|
|
58
|
-
},
|
|
59
|
-
deleteAudienceId() {
|
|
60
|
-
deleteCookie(AuthStoreManager.getKeyAudienceId());
|
|
61
|
-
},
|
|
62
|
-
deleteJwt(audienceId) {
|
|
63
|
-
deleteCookie(AuthStoreManager.getKeySession(audienceId));
|
|
64
|
-
},
|
|
65
|
-
getAudienceId() {
|
|
66
|
-
const cookieParsed = getCookie(AuthStoreManager.getKeyAudienceId());
|
|
67
|
-
const resultCookieParsed = audienceIdSchema.safeParse(cookieParsed);
|
|
68
|
-
if (resultCookieParsed.success) return resultCookieParsed.data.audienceId;
|
|
69
|
-
return null;
|
|
70
|
-
},
|
|
71
|
-
getJwt(audienceId) {
|
|
72
|
-
const cookieParsed = getCookie(AuthStoreManager.getKeySession(audienceId));
|
|
73
|
-
const resultCookieParsed = jwtSchema.safeParse(cookieParsed);
|
|
74
|
-
if (resultCookieParsed.success) return resultCookieParsed.data;
|
|
75
|
-
if (resultCookieParsed.error) {}
|
|
76
|
-
return null;
|
|
77
|
-
},
|
|
78
|
-
getKeyAudienceId() {
|
|
79
|
-
return `${KEY_AUDIENCE_ID}${AuthStoreManager.SUFFIX_KEY}`;
|
|
80
|
-
},
|
|
81
|
-
getKeySession(audienceId) {
|
|
82
|
-
return `${KEY_SESSION}${AuthStoreManager.SUFFIX_KEY}_${audienceId}`;
|
|
83
|
-
},
|
|
84
|
-
getUniqueHostnameString,
|
|
85
|
-
SUFFIX_KEY: DEFAULT_SUFFIX_KEY,
|
|
86
|
-
setAudienceId(audienceId) {
|
|
87
|
-
setCookie({ audienceId }, AuthStoreManager.getKeyAudienceId());
|
|
88
|
-
},
|
|
89
|
-
setJwt({ jwtInfo, setAudienceId = true }) {
|
|
90
|
-
const audienceId = jwtInfo.jwt?.audienceId;
|
|
91
|
-
setCookie(jwtInfo, AuthStoreManager.getKeySession(audienceId));
|
|
92
|
-
if (setAudienceId) this.setAudienceId(audienceId);
|
|
93
|
-
},
|
|
94
|
-
setSuffixKey(key) {
|
|
95
|
-
AuthStoreManager.SUFFIX_KEY = key ? `_${key}` : DEFAULT_SUFFIX_KEY;
|
|
96
|
-
},
|
|
97
|
-
typeGuardAudienceId(untypedObject) {
|
|
98
|
-
return audienceIdSchema.safeParse(untypedObject).success;
|
|
99
|
-
},
|
|
100
|
-
typeGuardJWT(untypedObject) {
|
|
101
|
-
return jwtSchema.safeParse(untypedObject).success;
|
|
102
|
-
}
|
|
103
|
-
};
|
|
104
|
-
//#endregion
|
|
105
|
-
export { AuthStoreManager, setCookieConfig };
|