react-token-manager 1.0.2 → 1.0.4
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 +80 -39
- package/dist/index.d.mts +23 -17
- package/dist/index.d.ts +23 -17
- package/dist/index.js +48 -42
- package/dist/index.mjs +49 -43
- package/package.json +1 -1
- package/src/core.ts +76 -30
- package/src/hook.ts +16 -31
- package/src/useTokenManager.ts +0 -38
package/README.md
CHANGED
|
@@ -19,7 +19,6 @@ import { configureTokenManager } from 'react-token-manager'
|
|
|
19
19
|
|
|
20
20
|
configureTokenManager({
|
|
21
21
|
storage: 'localStorage', // 'localStorage' | 'sessionStorage' | 'cookie'
|
|
22
|
-
tokenKey: 'access_token', // default: 'access_token'
|
|
23
22
|
})
|
|
24
23
|
```
|
|
25
24
|
## Using the Hook useTokenManager
|
|
@@ -28,18 +27,43 @@ configureTokenManager({
|
|
|
28
27
|
import { useTokenManager } from 'react-token-manager'
|
|
29
28
|
|
|
30
29
|
export default function Dashboard() {
|
|
31
|
-
const {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
30
|
+
const {
|
|
31
|
+
setTokens,
|
|
32
|
+
getTokens,
|
|
33
|
+
removeTokens,
|
|
34
|
+
clearAllTokens,
|
|
35
|
+
decodeToken,
|
|
36
|
+
isExpired
|
|
37
|
+
} = useTokenManager()
|
|
38
|
+
|
|
39
|
+
// Set multiple tokens at once
|
|
40
|
+
setTokens({
|
|
41
|
+
access_token: 'abc123',
|
|
42
|
+
refresh_token: 'xyz789',
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
// Get multiple tokens
|
|
46
|
+
const tokens = getTokens(['access_token', 'refresh_token'])
|
|
47
|
+
console.log(tokens.access_token) // 'abc123'
|
|
48
|
+
console.log(tokens.refresh_token) // 'xyz789'
|
|
49
|
+
|
|
50
|
+
// Decode a token
|
|
51
|
+
const payload = decodeToken(tokens.access_token!)
|
|
52
|
+
|
|
53
|
+
// Check if a token is expired
|
|
54
|
+
console.log(isExpired(tokens.access_token!))
|
|
55
|
+
|
|
56
|
+
// Remove multiple tokens
|
|
57
|
+
removeTokens(['access_token', 'refresh_token'])
|
|
58
|
+
|
|
59
|
+
// Clear all tracked tokens
|
|
60
|
+
clearAllTokens()
|
|
35
61
|
|
|
36
62
|
return (
|
|
37
63
|
<div>
|
|
38
|
-
<p>Token: {
|
|
39
|
-
<p>
|
|
40
|
-
<p>Decoded Payload: {JSON.stringify(
|
|
41
|
-
<button onClick={handleLogin}>Login</button>
|
|
42
|
-
<button onClick={handleLogout}>Logout</button>
|
|
64
|
+
<p>Access Token: {tokens.access_token}</p>
|
|
65
|
+
<p>Refresh Token: {tokens.refresh_token}</p>
|
|
66
|
+
<p>Decoded Payload: {JSON.stringify(payload)}</p>
|
|
43
67
|
</div>
|
|
44
68
|
)
|
|
45
69
|
}
|
|
@@ -52,54 +76,65 @@ You can override storage or tokenKey per hook if needed.
|
|
|
52
76
|
```bash
|
|
53
77
|
import { TokenManager } from 'react-token-manager'
|
|
54
78
|
|
|
55
|
-
const manager = new TokenManager({ storage: 'cookie'
|
|
79
|
+
const manager = new TokenManager({ storage: 'cookie' })
|
|
80
|
+
|
|
81
|
+
// Set multiple tokens
|
|
82
|
+
manager.set({
|
|
83
|
+
access_token: 'abc123',
|
|
84
|
+
refresh_token: 'xyz789',
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
// Get multiple tokens
|
|
88
|
+
const tokens = manager.get(['access_token', 'refresh_token'])
|
|
89
|
+
console.log(tokens.access_token) // 'abc123'
|
|
90
|
+
|
|
91
|
+
// Remove multiple tokens
|
|
92
|
+
manager.remove(['access_token', 'refresh_token'])
|
|
93
|
+
|
|
94
|
+
// Clear all tracked tokens
|
|
95
|
+
manager.clearAll()
|
|
96
|
+
|
|
97
|
+
// Decode and check expiration
|
|
98
|
+
console.log(manager.decode(tokens.access_token!))
|
|
99
|
+
console.log(manager.isExpired(tokens.access_token!))
|
|
56
100
|
|
|
57
|
-
manager.set('your.jwt.token')
|
|
58
|
-
console.log(manager.get())
|
|
59
|
-
console.log(manager.decode()) // decoded JWT payload
|
|
60
|
-
console.log(manager.isExpired())
|
|
61
|
-
manager.remove()
|
|
62
101
|
```
|
|
63
102
|
|
|
64
103
|
## API Reference
|
|
65
104
|
|
|
66
|
-
configureTokenManager(options: TokenManagerOptions)
|
|
67
|
-
|
|
68
105
|
Set global options.
|
|
69
106
|
|
|
70
107
|
```bash
|
|
71
108
|
{
|
|
72
109
|
storage?: 'localStorage' | 'sessionStorage' | 'cookie';
|
|
73
|
-
tokenKey?: string;
|
|
74
110
|
}
|
|
75
111
|
|
|
112
|
+
|
|
76
113
|
useTokenManager(options?: TokenManagerOptions)
|
|
77
114
|
|
|
78
115
|
Returns:
|
|
116
|
+
setTokens(tokens: Record<string, string>) // Set multiple tokens
|
|
79
117
|
|
|
80
|
-
|
|
118
|
+
getTokens(keys: string | string[]) // Get multiple tokens
|
|
81
119
|
|
|
82
|
-
|
|
120
|
+
removeTokens(keys: string | string[]) // Remove multiple tokens
|
|
83
121
|
|
|
84
|
-
|
|
122
|
+
decodeToken<T = unknown>(token: string) // Decode a single token
|
|
85
123
|
|
|
86
|
-
|
|
124
|
+
isExpired(token: string) // Check if a token is expired
|
|
87
125
|
|
|
88
|
-
decode<T>(): T | null — decode JWT payload
|
|
89
126
|
|
|
90
127
|
TokenManager Class Methods
|
|
128
|
+
set(tokens: Record<string, string>) // Store multiple tokens
|
|
91
129
|
|
|
92
|
-
|
|
130
|
+
get(keys: string | string[]) // Retrieve multiple tokens
|
|
93
131
|
|
|
94
|
-
|
|
132
|
+
remove(keys: string | string[]) // Delete multiple tokens
|
|
95
133
|
|
|
96
|
-
|
|
134
|
+
decode<T = unknown>(token: string) // Decode a single token
|
|
97
135
|
|
|
98
|
-
|
|
136
|
+
isExpired(token: string) // Check if a token is expired
|
|
99
137
|
|
|
100
|
-
isExpired(): boolean — check if token is expired
|
|
101
|
-
|
|
102
|
-
getValid(): string | null — get token only if valid
|
|
103
138
|
|
|
104
139
|
```
|
|
105
140
|
|
|
@@ -108,25 +143,31 @@ TokenManager Class Methods
|
|
|
108
143
|
Multiple Tokens
|
|
109
144
|
|
|
110
145
|
```bash
|
|
111
|
-
const {
|
|
112
|
-
|
|
146
|
+
const { setTokens, getTokens } = useTokenManager()
|
|
147
|
+
|
|
148
|
+
setTokens({
|
|
149
|
+
access_token: 'abc',
|
|
150
|
+
refresh_token: 'xyz',
|
|
151
|
+
})
|
|
113
152
|
|
|
114
|
-
|
|
115
|
-
|
|
153
|
+
const tokens = getTokens(['access_token', 'refresh_token'])
|
|
154
|
+
console.log(tokens.access_token) // 'abc'
|
|
155
|
+
console.log(tokens.refresh_token) // 'xyz'
|
|
116
156
|
```
|
|
117
157
|
|
|
118
158
|
##Using Cookies
|
|
119
159
|
```bash
|
|
120
|
-
configureTokenManager({ storage: 'cookie'
|
|
160
|
+
configureTokenManager({ storage: 'cookie' })
|
|
121
161
|
```
|
|
122
162
|
|
|
123
163
|
Checking Expiry Before API Call
|
|
124
164
|
|
|
125
165
|
```bash
|
|
126
|
-
const
|
|
166
|
+
const { getTokens, isExpired } = useTokenManager()
|
|
167
|
+
const { access_token } = getTokens(['access_token'])
|
|
127
168
|
|
|
128
|
-
if (!
|
|
129
|
-
callApi(
|
|
169
|
+
if (!isExpired(access_token!)) {
|
|
170
|
+
callApi(access_token)
|
|
130
171
|
}
|
|
131
172
|
```
|
|
132
173
|
|
package/dist/index.d.mts
CHANGED
|
@@ -1,28 +1,34 @@
|
|
|
1
1
|
type StorageType = 'localStorage' | 'sessionStorage' | 'cookie';
|
|
2
2
|
interface TokenManagerOptions {
|
|
3
3
|
storage?: StorageType;
|
|
4
|
-
tokenKey?: string;
|
|
5
4
|
}
|
|
6
5
|
declare const configureTokenManager: (options: TokenManagerOptions) => void;
|
|
7
6
|
declare class TokenManager {
|
|
8
7
|
private storage;
|
|
9
|
-
private tokenKey;
|
|
10
8
|
constructor(options?: TokenManagerOptions);
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
9
|
+
/** Set multiple tokens at once */
|
|
10
|
+
set(tokens: Record<string, string>): void;
|
|
11
|
+
/** Get single token by key or all tokens in an array of keys */
|
|
12
|
+
get(keys: string | string[]): Record<string, string | null>;
|
|
13
|
+
/** Remove multiple tokens by keys */
|
|
14
|
+
remove(keys: string | string[]): void;
|
|
15
|
+
/** Decode a single token by key */
|
|
16
|
+
decode<T = unknown>(token: string): T | null;
|
|
17
|
+
/** Check if a token is expired */
|
|
18
|
+
isExpired(token: string): boolean;
|
|
17
19
|
}
|
|
18
20
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
21
|
+
declare const useTokenManager: () => {
|
|
22
|
+
/** Set multiple tokens */
|
|
23
|
+
setTokens: (tokens: Record<string, string>) => void;
|
|
24
|
+
/** Get multiple tokens by keys */
|
|
25
|
+
getTokens: (keys: string | string[]) => Record<string, string | null>;
|
|
26
|
+
/** Remove multiple tokens by keys */
|
|
27
|
+
removeTokens: (keys: string | string[]) => void;
|
|
28
|
+
/** Decode a single token */
|
|
29
|
+
decodeToken: <T = unknown>(token: string) => T | null;
|
|
30
|
+
/** Check expiration of a single token */
|
|
31
|
+
isExpired: (token: string) => boolean;
|
|
32
|
+
};
|
|
27
33
|
|
|
28
|
-
export { type StorageType, TokenManager, type TokenManagerOptions,
|
|
34
|
+
export { type StorageType, TokenManager, type TokenManagerOptions, configureTokenManager, useTokenManager };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,28 +1,34 @@
|
|
|
1
1
|
type StorageType = 'localStorage' | 'sessionStorage' | 'cookie';
|
|
2
2
|
interface TokenManagerOptions {
|
|
3
3
|
storage?: StorageType;
|
|
4
|
-
tokenKey?: string;
|
|
5
4
|
}
|
|
6
5
|
declare const configureTokenManager: (options: TokenManagerOptions) => void;
|
|
7
6
|
declare class TokenManager {
|
|
8
7
|
private storage;
|
|
9
|
-
private tokenKey;
|
|
10
8
|
constructor(options?: TokenManagerOptions);
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
9
|
+
/** Set multiple tokens at once */
|
|
10
|
+
set(tokens: Record<string, string>): void;
|
|
11
|
+
/** Get single token by key or all tokens in an array of keys */
|
|
12
|
+
get(keys: string | string[]): Record<string, string | null>;
|
|
13
|
+
/** Remove multiple tokens by keys */
|
|
14
|
+
remove(keys: string | string[]): void;
|
|
15
|
+
/** Decode a single token by key */
|
|
16
|
+
decode<T = unknown>(token: string): T | null;
|
|
17
|
+
/** Check if a token is expired */
|
|
18
|
+
isExpired(token: string): boolean;
|
|
17
19
|
}
|
|
18
20
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
21
|
+
declare const useTokenManager: () => {
|
|
22
|
+
/** Set multiple tokens */
|
|
23
|
+
setTokens: (tokens: Record<string, string>) => void;
|
|
24
|
+
/** Get multiple tokens by keys */
|
|
25
|
+
getTokens: (keys: string | string[]) => Record<string, string | null>;
|
|
26
|
+
/** Remove multiple tokens by keys */
|
|
27
|
+
removeTokens: (keys: string | string[]) => void;
|
|
28
|
+
/** Decode a single token */
|
|
29
|
+
decodeToken: <T = unknown>(token: string) => T | null;
|
|
30
|
+
/** Check expiration of a single token */
|
|
31
|
+
isExpired: (token: string) => boolean;
|
|
32
|
+
};
|
|
27
33
|
|
|
28
|
-
export { type StorageType, TokenManager, type TokenManagerOptions,
|
|
34
|
+
export { type StorageType, TokenManager, type TokenManagerOptions, configureTokenManager, useTokenManager };
|
package/dist/index.js
CHANGED
|
@@ -40,8 +40,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
40
40
|
var jwtDecodeModule = __toESM(require("jwt-decode"));
|
|
41
41
|
var jwtDecode = jwtDecodeModule.default || jwtDecodeModule;
|
|
42
42
|
var globalOptions = {
|
|
43
|
-
storage: "localStorage"
|
|
44
|
-
tokenKey: "access_token"
|
|
43
|
+
storage: "localStorage"
|
|
45
44
|
};
|
|
46
45
|
var configureTokenManager = (options) => {
|
|
47
46
|
globalOptions = { ...globalOptions, ...options };
|
|
@@ -50,26 +49,42 @@ var TokenManager = class {
|
|
|
50
49
|
constructor(options) {
|
|
51
50
|
const opts = options || globalOptions;
|
|
52
51
|
this.storage = opts.storage || "localStorage";
|
|
53
|
-
this.tokenKey = opts.tokenKey || "access_token";
|
|
54
52
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
53
|
+
/** Set multiple tokens at once */
|
|
54
|
+
set(tokens) {
|
|
55
|
+
Object.entries(tokens).forEach(([key, value]) => {
|
|
56
|
+
if (this.storage === "localStorage") localStorage.setItem(key, value);
|
|
57
|
+
else if (this.storage === "sessionStorage") sessionStorage.setItem(key, value);
|
|
58
|
+
else document.cookie = `${key}=${value}; path=/`;
|
|
59
|
+
});
|
|
59
60
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
const
|
|
64
|
-
|
|
61
|
+
/** Get single token by key or all tokens in an array of keys */
|
|
62
|
+
get(keys) {
|
|
63
|
+
const keyArray = Array.isArray(keys) ? keys : [keys];
|
|
64
|
+
const result = {};
|
|
65
|
+
keyArray.forEach((key) => {
|
|
66
|
+
let value = null;
|
|
67
|
+
if (this.storage === "localStorage") value = localStorage.getItem(key);
|
|
68
|
+
else if (this.storage === "sessionStorage") value = sessionStorage.getItem(key);
|
|
69
|
+
else {
|
|
70
|
+
const match = document.cookie.match(new RegExp("(^| )" + key + "=([^;]+)"));
|
|
71
|
+
value = match ? match[2] : null;
|
|
72
|
+
}
|
|
73
|
+
result[key] = value;
|
|
74
|
+
});
|
|
75
|
+
return result;
|
|
65
76
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
77
|
+
/** Remove multiple tokens by keys */
|
|
78
|
+
remove(keys) {
|
|
79
|
+
const keyArray = Array.isArray(keys) ? keys : [keys];
|
|
80
|
+
keyArray.forEach((key) => {
|
|
81
|
+
if (this.storage === "localStorage") localStorage.removeItem(key);
|
|
82
|
+
else if (this.storage === "sessionStorage") sessionStorage.removeItem(key);
|
|
83
|
+
else document.cookie = `${key}=;expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/`;
|
|
84
|
+
});
|
|
70
85
|
}
|
|
71
|
-
|
|
72
|
-
|
|
86
|
+
/** Decode a single token by key */
|
|
87
|
+
decode(token) {
|
|
73
88
|
if (!token) return null;
|
|
74
89
|
try {
|
|
75
90
|
return jwtDecode(token);
|
|
@@ -77,38 +92,29 @@ var TokenManager = class {
|
|
|
77
92
|
return null;
|
|
78
93
|
}
|
|
79
94
|
}
|
|
80
|
-
|
|
81
|
-
|
|
95
|
+
/** Check if a token is expired */
|
|
96
|
+
isExpired(token) {
|
|
97
|
+
const decoded = this.decode(token);
|
|
82
98
|
if (!decoded || !decoded.exp) return true;
|
|
83
99
|
return Date.now() >= decoded.exp * 1e3;
|
|
84
100
|
}
|
|
85
|
-
getValid() {
|
|
86
|
-
return this.isExpired() ? null : this.get();
|
|
87
|
-
}
|
|
88
101
|
};
|
|
89
102
|
|
|
90
103
|
// src/hook.ts
|
|
91
104
|
var import_react = require("react");
|
|
92
|
-
var useTokenManager = (
|
|
93
|
-
const manager = new TokenManager(
|
|
94
|
-
const [token, setTokenState] = (0, import_react.useState)(null);
|
|
95
|
-
(0, import_react.useEffect)(() => {
|
|
96
|
-
setTokenState(manager.getValid());
|
|
97
|
-
}, []);
|
|
98
|
-
const setToken = (value) => {
|
|
99
|
-
manager.set(value);
|
|
100
|
-
setTokenState(value);
|
|
101
|
-
};
|
|
102
|
-
const removeToken = () => {
|
|
103
|
-
manager.remove();
|
|
104
|
-
setTokenState(null);
|
|
105
|
-
};
|
|
105
|
+
var useTokenManager = () => {
|
|
106
|
+
const manager = (0, import_react.useMemo)(() => new TokenManager(), []);
|
|
106
107
|
return {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
108
|
+
/** Set multiple tokens */
|
|
109
|
+
setTokens: (tokens) => manager.set(tokens),
|
|
110
|
+
/** Get multiple tokens by keys */
|
|
111
|
+
getTokens: (keys) => manager.get(keys),
|
|
112
|
+
/** Remove multiple tokens by keys */
|
|
113
|
+
removeTokens: (keys) => manager.remove(keys),
|
|
114
|
+
/** Decode a single token */
|
|
115
|
+
decodeToken: (token) => manager.decode(token),
|
|
116
|
+
/** Check expiration of a single token */
|
|
117
|
+
isExpired: (token) => manager.isExpired(token)
|
|
112
118
|
};
|
|
113
119
|
};
|
|
114
120
|
// Annotate the CommonJS export names for ESM import in node:
|
package/dist/index.mjs
CHANGED
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
import * as jwtDecodeModule from "jwt-decode";
|
|
3
3
|
var jwtDecode = jwtDecodeModule.default || jwtDecodeModule;
|
|
4
4
|
var globalOptions = {
|
|
5
|
-
storage: "localStorage"
|
|
6
|
-
tokenKey: "access_token"
|
|
5
|
+
storage: "localStorage"
|
|
7
6
|
};
|
|
8
7
|
var configureTokenManager = (options) => {
|
|
9
8
|
globalOptions = { ...globalOptions, ...options };
|
|
@@ -12,26 +11,42 @@ var TokenManager = class {
|
|
|
12
11
|
constructor(options) {
|
|
13
12
|
const opts = options || globalOptions;
|
|
14
13
|
this.storage = opts.storage || "localStorage";
|
|
15
|
-
this.tokenKey = opts.tokenKey || "access_token";
|
|
16
14
|
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
15
|
+
/** Set multiple tokens at once */
|
|
16
|
+
set(tokens) {
|
|
17
|
+
Object.entries(tokens).forEach(([key, value]) => {
|
|
18
|
+
if (this.storage === "localStorage") localStorage.setItem(key, value);
|
|
19
|
+
else if (this.storage === "sessionStorage") sessionStorage.setItem(key, value);
|
|
20
|
+
else document.cookie = `${key}=${value}; path=/`;
|
|
21
|
+
});
|
|
21
22
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const
|
|
26
|
-
|
|
23
|
+
/** Get single token by key or all tokens in an array of keys */
|
|
24
|
+
get(keys) {
|
|
25
|
+
const keyArray = Array.isArray(keys) ? keys : [keys];
|
|
26
|
+
const result = {};
|
|
27
|
+
keyArray.forEach((key) => {
|
|
28
|
+
let value = null;
|
|
29
|
+
if (this.storage === "localStorage") value = localStorage.getItem(key);
|
|
30
|
+
else if (this.storage === "sessionStorage") value = sessionStorage.getItem(key);
|
|
31
|
+
else {
|
|
32
|
+
const match = document.cookie.match(new RegExp("(^| )" + key + "=([^;]+)"));
|
|
33
|
+
value = match ? match[2] : null;
|
|
34
|
+
}
|
|
35
|
+
result[key] = value;
|
|
36
|
+
});
|
|
37
|
+
return result;
|
|
27
38
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
39
|
+
/** Remove multiple tokens by keys */
|
|
40
|
+
remove(keys) {
|
|
41
|
+
const keyArray = Array.isArray(keys) ? keys : [keys];
|
|
42
|
+
keyArray.forEach((key) => {
|
|
43
|
+
if (this.storage === "localStorage") localStorage.removeItem(key);
|
|
44
|
+
else if (this.storage === "sessionStorage") sessionStorage.removeItem(key);
|
|
45
|
+
else document.cookie = `${key}=;expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/`;
|
|
46
|
+
});
|
|
32
47
|
}
|
|
33
|
-
|
|
34
|
-
|
|
48
|
+
/** Decode a single token by key */
|
|
49
|
+
decode(token) {
|
|
35
50
|
if (!token) return null;
|
|
36
51
|
try {
|
|
37
52
|
return jwtDecode(token);
|
|
@@ -39,38 +54,29 @@ var TokenManager = class {
|
|
|
39
54
|
return null;
|
|
40
55
|
}
|
|
41
56
|
}
|
|
42
|
-
|
|
43
|
-
|
|
57
|
+
/** Check if a token is expired */
|
|
58
|
+
isExpired(token) {
|
|
59
|
+
const decoded = this.decode(token);
|
|
44
60
|
if (!decoded || !decoded.exp) return true;
|
|
45
61
|
return Date.now() >= decoded.exp * 1e3;
|
|
46
62
|
}
|
|
47
|
-
getValid() {
|
|
48
|
-
return this.isExpired() ? null : this.get();
|
|
49
|
-
}
|
|
50
63
|
};
|
|
51
64
|
|
|
52
65
|
// src/hook.ts
|
|
53
|
-
import {
|
|
54
|
-
var useTokenManager = (
|
|
55
|
-
const manager = new TokenManager(
|
|
56
|
-
const [token, setTokenState] = useState(null);
|
|
57
|
-
useEffect(() => {
|
|
58
|
-
setTokenState(manager.getValid());
|
|
59
|
-
}, []);
|
|
60
|
-
const setToken = (value) => {
|
|
61
|
-
manager.set(value);
|
|
62
|
-
setTokenState(value);
|
|
63
|
-
};
|
|
64
|
-
const removeToken = () => {
|
|
65
|
-
manager.remove();
|
|
66
|
-
setTokenState(null);
|
|
67
|
-
};
|
|
66
|
+
import { useMemo } from "react";
|
|
67
|
+
var useTokenManager = () => {
|
|
68
|
+
const manager = useMemo(() => new TokenManager(), []);
|
|
68
69
|
return {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
70
|
+
/** Set multiple tokens */
|
|
71
|
+
setTokens: (tokens) => manager.set(tokens),
|
|
72
|
+
/** Get multiple tokens by keys */
|
|
73
|
+
getTokens: (keys) => manager.get(keys),
|
|
74
|
+
/** Remove multiple tokens by keys */
|
|
75
|
+
removeTokens: (keys) => manager.remove(keys),
|
|
76
|
+
/** Decode a single token */
|
|
77
|
+
decodeToken: (token) => manager.decode(token),
|
|
78
|
+
/** Check expiration of a single token */
|
|
79
|
+
isExpired: (token) => manager.isExpired(token)
|
|
74
80
|
};
|
|
75
81
|
};
|
|
76
82
|
export {
|
package/package.json
CHANGED
package/src/core.ts
CHANGED
|
@@ -1,75 +1,121 @@
|
|
|
1
1
|
import type { JwtPayload } from 'jwt-decode'
|
|
2
2
|
import * as jwtDecodeModule from 'jwt-decode'
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
const jwtDecode: <T = unknown>(token: string) => T =
|
|
4
|
+
const jwtDecode: <T = unknown>(token: string) => T =
|
|
6
5
|
(jwtDecodeModule as any).default || jwtDecodeModule
|
|
7
6
|
|
|
8
|
-
|
|
9
7
|
export type StorageType = 'localStorage' | 'sessionStorage' | 'cookie'
|
|
10
8
|
|
|
11
9
|
export interface TokenManagerOptions {
|
|
12
10
|
storage?: StorageType
|
|
13
|
-
tokenKey?: string
|
|
14
11
|
}
|
|
15
12
|
|
|
16
|
-
// ---------------- Global configuration ----------------
|
|
17
13
|
let globalOptions: TokenManagerOptions = {
|
|
18
14
|
storage: 'localStorage',
|
|
19
|
-
tokenKey: 'access_token',
|
|
20
15
|
}
|
|
21
16
|
|
|
22
17
|
export const configureTokenManager = (options: TokenManagerOptions) => {
|
|
23
18
|
globalOptions = { ...globalOptions, ...options }
|
|
24
19
|
}
|
|
25
20
|
|
|
26
|
-
// ---------------- TokenManager Class ----------------
|
|
27
21
|
export class TokenManager {
|
|
28
22
|
private storage: StorageType
|
|
29
|
-
private
|
|
23
|
+
private trackedKeys: Set<string> = new Set()
|
|
30
24
|
|
|
31
25
|
constructor(options?: TokenManagerOptions) {
|
|
32
26
|
const opts = options || globalOptions
|
|
33
27
|
this.storage = opts.storage || 'localStorage'
|
|
34
|
-
this.tokenKey = opts.tokenKey || 'access_token'
|
|
35
28
|
}
|
|
36
29
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
30
|
+
/** Set multiple tokens at once */
|
|
31
|
+
set(tokens: Record<string, string>) {
|
|
32
|
+
Object.entries(tokens).forEach(([key, value]) => {
|
|
33
|
+
this.trackedKeys.add(key)
|
|
34
|
+
|
|
35
|
+
if (this.storage === 'localStorage') localStorage.setItem(key, value)
|
|
36
|
+
else if (this.storage === 'sessionStorage') sessionStorage.setItem(key, value)
|
|
37
|
+
else document.cookie = `${key}=${value}; path=/`
|
|
38
|
+
})
|
|
41
39
|
}
|
|
42
40
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
const
|
|
47
|
-
|
|
41
|
+
/** Get token(s) */
|
|
42
|
+
get(keys: string | string[]): Record<string, string | null> {
|
|
43
|
+
const keyArray = Array.isArray(keys) ? keys : [keys]
|
|
44
|
+
const result: Record<string, string | null> = {}
|
|
45
|
+
|
|
46
|
+
keyArray.forEach((key) => {
|
|
47
|
+
let value: string | null = null
|
|
48
|
+
|
|
49
|
+
if (this.storage === 'localStorage') value = localStorage.getItem(key)
|
|
50
|
+
else if (this.storage === 'sessionStorage') value = sessionStorage.getItem(key)
|
|
51
|
+
else {
|
|
52
|
+
const match = document.cookie.match(
|
|
53
|
+
new RegExp('(^| )' + key + '=([^;]+)')
|
|
54
|
+
)
|
|
55
|
+
value = match ? match[2] : null
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
result[key] = value
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
return result
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Remove specific tokens */
|
|
65
|
+
remove(keys: string | string[]) {
|
|
66
|
+
const keyArray = Array.isArray(keys) ? keys : [keys]
|
|
67
|
+
|
|
68
|
+
keyArray.forEach((key) => {
|
|
69
|
+
if (this.storage === 'localStorage') localStorage.removeItem(key)
|
|
70
|
+
else if (this.storage === 'sessionStorage') sessionStorage.removeItem(key)
|
|
71
|
+
else
|
|
72
|
+
document.cookie = `${key}=;expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/`
|
|
73
|
+
|
|
74
|
+
this.trackedKeys.delete(key)
|
|
75
|
+
})
|
|
48
76
|
}
|
|
49
77
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
78
|
+
/** Clear all tracked auth tokens */
|
|
79
|
+
clearAll() {
|
|
80
|
+
this.trackedKeys.forEach((key) => {
|
|
81
|
+
if (this.storage === 'localStorage') localStorage.removeItem(key)
|
|
82
|
+
else if (this.storage === 'sessionStorage') sessionStorage.removeItem(key)
|
|
83
|
+
else
|
|
84
|
+
document.cookie = `${key}=;expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/`
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
this.trackedKeys.clear()
|
|
54
88
|
}
|
|
55
89
|
|
|
56
|
-
|
|
57
|
-
|
|
90
|
+
/** Decode token */
|
|
91
|
+
decode<T = unknown>(token: string): T | null {
|
|
58
92
|
if (!token) return null
|
|
59
93
|
try {
|
|
60
|
-
return jwtDecode<T>(token)
|
|
94
|
+
return jwtDecode<T>(token)
|
|
61
95
|
} catch {
|
|
62
96
|
return null
|
|
63
97
|
}
|
|
64
98
|
}
|
|
65
99
|
|
|
66
|
-
|
|
67
|
-
|
|
100
|
+
/** Check if a token is expired
|
|
101
|
+
* If no token is provided, defaults to checking 'access_token'
|
|
102
|
+
*/
|
|
103
|
+
isExpired(token?: string): boolean {
|
|
104
|
+
let tokenToCheck = token
|
|
105
|
+
|
|
106
|
+
// If no token passed, try to get access_token
|
|
107
|
+
if (!tokenToCheck) {
|
|
108
|
+
const stored = this.get('access_token')
|
|
109
|
+
tokenToCheck = stored['access_token'] || undefined
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (!tokenToCheck) return true
|
|
113
|
+
|
|
114
|
+
const decoded = this.decode<JwtPayload>(tokenToCheck)
|
|
115
|
+
|
|
68
116
|
if (!decoded || !decoded.exp) return true
|
|
117
|
+
|
|
69
118
|
return Date.now() >= decoded.exp * 1000
|
|
70
119
|
}
|
|
71
120
|
|
|
72
|
-
getValid(): string | null {
|
|
73
|
-
return this.isExpired() ? null : this.get()
|
|
74
|
-
}
|
|
75
121
|
}
|
package/src/hook.ts
CHANGED
|
@@ -1,40 +1,25 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { TokenManager
|
|
1
|
+
import { useMemo } from 'react'
|
|
2
|
+
import { TokenManager } from './core'
|
|
3
3
|
|
|
4
|
-
export
|
|
5
|
-
|
|
6
|
-
setToken: (value: string) => void
|
|
7
|
-
removeToken: () => void
|
|
8
|
-
isExpired: () => boolean
|
|
9
|
-
decode: <T = unknown>() => T | null
|
|
10
|
-
}
|
|
4
|
+
export const useTokenManager = () => {
|
|
5
|
+
const manager = useMemo(() => new TokenManager(), [])
|
|
11
6
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const manager = new TokenManager(options)
|
|
7
|
+
return {
|
|
8
|
+
/** Set multiple tokens */
|
|
9
|
+
setTokens: (tokens: Record<string, string>) => manager.set(tokens),
|
|
16
10
|
|
|
17
|
-
|
|
11
|
+
/** Get multiple tokens by keys */
|
|
12
|
+
getTokens: (keys: string | string[]) => manager.get(keys),
|
|
18
13
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}, [])
|
|
14
|
+
/** Remove multiple tokens by keys */
|
|
15
|
+
removeTokens: (keys: string | string[]) => manager.remove(keys),
|
|
22
16
|
|
|
23
|
-
|
|
24
|
-
manager.set(value)
|
|
25
|
-
setTokenState(value)
|
|
26
|
-
}
|
|
17
|
+
clearTokens: () => manager.clearAll(),
|
|
27
18
|
|
|
28
|
-
|
|
29
|
-
manager.
|
|
30
|
-
setTokenState(null)
|
|
31
|
-
}
|
|
19
|
+
/** Decode a single token */
|
|
20
|
+
decodeToken: <T = unknown>(token: string) => manager.decode<T>(token),
|
|
32
21
|
|
|
33
|
-
|
|
34
|
-
token,
|
|
35
|
-
setToken,
|
|
36
|
-
removeToken,
|
|
37
|
-
isExpired: () => manager.isExpired(),
|
|
38
|
-
decode: <T = unknown>() => manager.decode<T>(),
|
|
22
|
+
/** Check expiration of a single token */
|
|
23
|
+
isExpired: (token: string) => manager.isExpired(token),
|
|
39
24
|
}
|
|
40
25
|
}
|
package/src/useTokenManager.ts
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { useEffect, useState, useMemo } from 'react'
|
|
2
|
-
import { TokenManager } from './core'
|
|
3
|
-
|
|
4
|
-
export interface UseTokenManagerReturn {
|
|
5
|
-
token: string | null
|
|
6
|
-
setToken: (value: string) => void
|
|
7
|
-
removeToken: () => void
|
|
8
|
-
isExpired: () => boolean
|
|
9
|
-
decode: <T = unknown>() => T | null
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export const useTokenManager = (): UseTokenManagerReturn => {
|
|
13
|
-
const manager = useMemo(() => new TokenManager(), [])
|
|
14
|
-
|
|
15
|
-
const [token, setTokenState] = useState<string | null>(null)
|
|
16
|
-
|
|
17
|
-
useEffect(() => {
|
|
18
|
-
setTokenState(manager.getValid())
|
|
19
|
-
}, [manager])
|
|
20
|
-
|
|
21
|
-
const setToken = (value: string) => {
|
|
22
|
-
manager.set(value)
|
|
23
|
-
setTokenState(value)
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const removeToken = () => {
|
|
27
|
-
manager.remove()
|
|
28
|
-
setTokenState(null)
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
return {
|
|
32
|
-
token,
|
|
33
|
-
setToken,
|
|
34
|
-
removeToken,
|
|
35
|
-
isExpired: () => manager.isExpired(),
|
|
36
|
-
decode: <T = unknown>() => manager.decode<T>(),
|
|
37
|
-
}
|
|
38
|
-
}
|