@crimson-education/sdk 0.2.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 +377 -0
- package/dist/core/account.d.ts +14 -0
- package/dist/core/account.js +30 -0
- package/dist/core/auth/index.d.ts +11 -0
- package/dist/core/auth/index.js +25 -0
- package/dist/core/auth/oauth-adapter.d.ts +78 -0
- package/dist/core/auth/oauth-adapter.js +341 -0
- package/dist/core/auth/pkce.d.ts +20 -0
- package/dist/core/auth/pkce.js +112 -0
- package/dist/core/auth/token-manager.d.ts +68 -0
- package/dist/core/auth/token-manager.js +294 -0
- package/dist/core/auth/token-storage.d.ts +46 -0
- package/dist/core/auth/token-storage.js +155 -0
- package/dist/core/auth/types.d.ts +148 -0
- package/dist/core/auth/types.js +15 -0
- package/dist/core/client.d.ts +84 -0
- package/dist/core/client.js +229 -0
- package/dist/core/index.d.ts +11 -0
- package/dist/core/index.js +47 -0
- package/dist/core/missionLibrary.d.ts +68 -0
- package/dist/core/missionLibrary.js +143 -0
- package/dist/core/missions.d.ts +45 -0
- package/dist/core/missions.js +140 -0
- package/dist/core/roadmap.d.ts +8 -0
- package/dist/core/roadmap.js +18 -0
- package/dist/core/studentProfile.d.ts +21 -0
- package/dist/core/studentProfile.js +41 -0
- package/dist/core/tasks.d.ts +117 -0
- package/dist/core/tasks.js +288 -0
- package/dist/core/types.d.ts +402 -0
- package/dist/core/types.js +2 -0
- package/dist/core/users.d.ts +21 -0
- package/dist/core/users.js +46 -0
- package/dist/iframe/auth-state.d.ts +7 -0
- package/dist/iframe/auth-state.js +125 -0
- package/dist/iframe/constants.d.ts +8 -0
- package/dist/iframe/constants.js +29 -0
- package/dist/iframe/index.d.ts +5 -0
- package/dist/iframe/index.js +17 -0
- package/dist/iframe/listener.d.ts +2 -0
- package/dist/iframe/listener.js +57 -0
- package/dist/iframe/types.d.ts +18 -0
- package/dist/iframe/types.js +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +22 -0
- package/dist/react/hooks/index.d.ts +10 -0
- package/dist/react/hooks/index.js +48 -0
- package/dist/react/hooks/useAccount.d.ts +13 -0
- package/dist/react/hooks/useAccount.js +39 -0
- package/dist/react/hooks/useAuthState.d.ts +2 -0
- package/dist/react/hooks/useAuthState.js +18 -0
- package/dist/react/hooks/useMissionLibrary.d.ts +31 -0
- package/dist/react/hooks/useMissionLibrary.js +183 -0
- package/dist/react/hooks/useMissions.d.ts +24 -0
- package/dist/react/hooks/useMissions.js +104 -0
- package/dist/react/hooks/useOAuth.d.ts +94 -0
- package/dist/react/hooks/useOAuth.js +211 -0
- package/dist/react/hooks/useRoadmapContext.d.ts +2 -0
- package/dist/react/hooks/useRoadmapContext.js +29 -0
- package/dist/react/hooks/useStudentProfile.d.ts +24 -0
- package/dist/react/hooks/useStudentProfile.js +65 -0
- package/dist/react/hooks/useTasks.d.ts +26 -0
- package/dist/react/hooks/useTasks.js +137 -0
- package/dist/react/hooks/useUsers.d.ts +9 -0
- package/dist/react/hooks/useUsers.js +50 -0
- package/dist/react/index.d.ts +3 -0
- package/dist/react/index.js +21 -0
- package/dist/react/provider.d.ts +16 -0
- package/dist/react/provider.js +41 -0
- package/package.json +61 -0
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Token Manager
|
|
4
|
+
* Handles token lifecycle, automatic refresh, and state management
|
|
5
|
+
*/
|
|
6
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
7
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
8
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
9
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
10
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
11
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
12
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
13
|
+
});
|
|
14
|
+
};
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.TokenManager = void 0;
|
|
17
|
+
/** Buffer time before expiration to trigger refresh (5 minutes) */
|
|
18
|
+
const REFRESH_BUFFER_MS = 5 * 60 * 1000;
|
|
19
|
+
/** Minimum time between refresh attempts (30 seconds) */
|
|
20
|
+
const MIN_REFRESH_INTERVAL_MS = 30 * 1000;
|
|
21
|
+
class TokenManager {
|
|
22
|
+
constructor(config) {
|
|
23
|
+
var _a, _b;
|
|
24
|
+
this.listeners = new Set();
|
|
25
|
+
this.refreshPromise = null;
|
|
26
|
+
this.refreshTimer = null;
|
|
27
|
+
this.lastRefreshAttempt = 0;
|
|
28
|
+
this.currentState = {
|
|
29
|
+
isAuthenticated: false,
|
|
30
|
+
isLoading: true,
|
|
31
|
+
tokens: null,
|
|
32
|
+
error: null,
|
|
33
|
+
};
|
|
34
|
+
this.storage = config.storage;
|
|
35
|
+
this.onRefreshNeeded = config.onRefreshNeeded;
|
|
36
|
+
this.refreshBufferMs = (_a = config.refreshBufferMs) !== null && _a !== void 0 ? _a : REFRESH_BUFFER_MS;
|
|
37
|
+
this.autoRefresh = (_b = config.autoRefresh) !== null && _b !== void 0 ? _b : true;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Initialize the token manager
|
|
41
|
+
* Loads tokens from storage and sets up auto-refresh if enabled
|
|
42
|
+
*/
|
|
43
|
+
initialize() {
|
|
44
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
45
|
+
try {
|
|
46
|
+
const tokens = yield this.storage.getTokens();
|
|
47
|
+
if (tokens) {
|
|
48
|
+
if (this.isTokenExpired(tokens)) {
|
|
49
|
+
// Token expired, try to refresh
|
|
50
|
+
if (tokens.refreshToken) {
|
|
51
|
+
yield this.refreshToken();
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
// No refresh token, clear and set unauthenticated
|
|
55
|
+
yield this.storage.clearTokens();
|
|
56
|
+
this.updateState({
|
|
57
|
+
isAuthenticated: false,
|
|
58
|
+
isLoading: false,
|
|
59
|
+
tokens: null,
|
|
60
|
+
error: null,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
// Token still valid
|
|
66
|
+
this.updateState({
|
|
67
|
+
isAuthenticated: true,
|
|
68
|
+
isLoading: false,
|
|
69
|
+
tokens,
|
|
70
|
+
error: null,
|
|
71
|
+
});
|
|
72
|
+
this.scheduleRefresh(tokens);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
this.updateState({
|
|
77
|
+
isAuthenticated: false,
|
|
78
|
+
isLoading: false,
|
|
79
|
+
tokens: null,
|
|
80
|
+
error: null,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
this.updateState({
|
|
86
|
+
isAuthenticated: false,
|
|
87
|
+
isLoading: false,
|
|
88
|
+
tokens: null,
|
|
89
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Get current authentication state
|
|
96
|
+
*/
|
|
97
|
+
getState() {
|
|
98
|
+
return this.currentState;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Get current access token if valid
|
|
102
|
+
* Automatically refreshes if needed and possible
|
|
103
|
+
*/
|
|
104
|
+
getAccessToken() {
|
|
105
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
106
|
+
var _a;
|
|
107
|
+
const tokens = yield this.storage.getTokens();
|
|
108
|
+
if (!tokens) {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
// Check if token needs refresh
|
|
112
|
+
if (this.shouldRefresh(tokens)) {
|
|
113
|
+
const refreshed = yield this.refreshToken();
|
|
114
|
+
return (_a = refreshed === null || refreshed === void 0 ? void 0 : refreshed.accessToken) !== null && _a !== void 0 ? _a : null;
|
|
115
|
+
}
|
|
116
|
+
return tokens.accessToken;
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Set new tokens (after successful authorization)
|
|
121
|
+
*/
|
|
122
|
+
setTokens(tokens) {
|
|
123
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
124
|
+
yield this.storage.setTokens(tokens);
|
|
125
|
+
this.updateState({
|
|
126
|
+
isAuthenticated: true,
|
|
127
|
+
isLoading: false,
|
|
128
|
+
tokens,
|
|
129
|
+
error: null,
|
|
130
|
+
});
|
|
131
|
+
this.scheduleRefresh(tokens);
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Refresh the access token
|
|
136
|
+
* Uses a lock to prevent concurrent refresh attempts
|
|
137
|
+
*/
|
|
138
|
+
refreshToken() {
|
|
139
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
140
|
+
// Check minimum refresh interval
|
|
141
|
+
const now = Date.now();
|
|
142
|
+
if (now - this.lastRefreshAttempt < MIN_REFRESH_INTERVAL_MS) {
|
|
143
|
+
// Wait for existing refresh or return current tokens
|
|
144
|
+
if (this.refreshPromise) {
|
|
145
|
+
return this.refreshPromise;
|
|
146
|
+
}
|
|
147
|
+
return this.currentState.tokens;
|
|
148
|
+
}
|
|
149
|
+
// If refresh already in progress, wait for it
|
|
150
|
+
if (this.refreshPromise) {
|
|
151
|
+
return this.refreshPromise;
|
|
152
|
+
}
|
|
153
|
+
this.lastRefreshAttempt = now;
|
|
154
|
+
// Start refresh with lock
|
|
155
|
+
this.refreshPromise = this.doRefresh();
|
|
156
|
+
try {
|
|
157
|
+
return yield this.refreshPromise;
|
|
158
|
+
}
|
|
159
|
+
finally {
|
|
160
|
+
this.refreshPromise = null;
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
doRefresh() {
|
|
165
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
166
|
+
const currentTokens = yield this.storage.getTokens();
|
|
167
|
+
if (!(currentTokens === null || currentTokens === void 0 ? void 0 : currentTokens.refreshToken)) {
|
|
168
|
+
this.updateState({
|
|
169
|
+
isAuthenticated: false,
|
|
170
|
+
isLoading: false,
|
|
171
|
+
tokens: null,
|
|
172
|
+
error: new Error("No refresh token available"),
|
|
173
|
+
});
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
try {
|
|
177
|
+
const newTokens = yield this.onRefreshNeeded(currentTokens.refreshToken);
|
|
178
|
+
if (newTokens) {
|
|
179
|
+
yield this.storage.setTokens(newTokens);
|
|
180
|
+
this.updateState({
|
|
181
|
+
isAuthenticated: true,
|
|
182
|
+
isLoading: false,
|
|
183
|
+
tokens: newTokens,
|
|
184
|
+
error: null,
|
|
185
|
+
});
|
|
186
|
+
this.scheduleRefresh(newTokens);
|
|
187
|
+
return newTokens;
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
// Refresh failed, clear tokens
|
|
191
|
+
yield this.storage.clearTokens();
|
|
192
|
+
this.updateState({
|
|
193
|
+
isAuthenticated: false,
|
|
194
|
+
isLoading: false,
|
|
195
|
+
tokens: null,
|
|
196
|
+
error: new Error("Token refresh failed"),
|
|
197
|
+
});
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
catch (error) {
|
|
202
|
+
// Check if this is a revocation/invalid token error
|
|
203
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
204
|
+
const isRevoked = errorMessage.includes("invalid_grant") ||
|
|
205
|
+
errorMessage.includes("revoked") ||
|
|
206
|
+
errorMessage.includes("reuse");
|
|
207
|
+
if (isRevoked) {
|
|
208
|
+
// Token was revoked or reused, clear everything
|
|
209
|
+
yield this.storage.clearTokens();
|
|
210
|
+
}
|
|
211
|
+
this.updateState({
|
|
212
|
+
isAuthenticated: false,
|
|
213
|
+
isLoading: false,
|
|
214
|
+
tokens: isRevoked ? null : this.currentState.tokens,
|
|
215
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
216
|
+
});
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Clear tokens (logout)
|
|
223
|
+
*/
|
|
224
|
+
clearTokens() {
|
|
225
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
226
|
+
this.cancelScheduledRefresh();
|
|
227
|
+
yield this.storage.clearTokens();
|
|
228
|
+
this.updateState({
|
|
229
|
+
isAuthenticated: false,
|
|
230
|
+
isLoading: false,
|
|
231
|
+
tokens: null,
|
|
232
|
+
error: null,
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Subscribe to auth state changes
|
|
238
|
+
*/
|
|
239
|
+
subscribe(listener) {
|
|
240
|
+
this.listeners.add(listener);
|
|
241
|
+
// Immediately call with current state
|
|
242
|
+
listener(this.currentState);
|
|
243
|
+
// Return unsubscribe function
|
|
244
|
+
return () => {
|
|
245
|
+
this.listeners.delete(listener);
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Clean up resources
|
|
250
|
+
*/
|
|
251
|
+
destroy() {
|
|
252
|
+
this.cancelScheduledRefresh();
|
|
253
|
+
this.listeners.clear();
|
|
254
|
+
}
|
|
255
|
+
updateState(newState) {
|
|
256
|
+
this.currentState = newState;
|
|
257
|
+
this.listeners.forEach((listener) => {
|
|
258
|
+
try {
|
|
259
|
+
listener(newState);
|
|
260
|
+
}
|
|
261
|
+
catch (_a) {
|
|
262
|
+
// Ignore listener errors
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
isTokenExpired(tokens) {
|
|
267
|
+
return Date.now() >= tokens.expiresAt;
|
|
268
|
+
}
|
|
269
|
+
shouldRefresh(tokens) {
|
|
270
|
+
// Refresh if within buffer time of expiration
|
|
271
|
+
return Date.now() >= tokens.expiresAt - this.refreshBufferMs;
|
|
272
|
+
}
|
|
273
|
+
scheduleRefresh(tokens) {
|
|
274
|
+
if (!this.autoRefresh || !tokens.refreshToken) {
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
this.cancelScheduledRefresh();
|
|
278
|
+
// Calculate time until refresh is needed
|
|
279
|
+
const refreshAt = tokens.expiresAt - this.refreshBufferMs;
|
|
280
|
+
const delay = Math.max(refreshAt - Date.now(), 0);
|
|
281
|
+
this.refreshTimer = setTimeout(() => {
|
|
282
|
+
this.refreshToken().catch(() => {
|
|
283
|
+
// Refresh error handled in doRefresh
|
|
284
|
+
});
|
|
285
|
+
}, delay);
|
|
286
|
+
}
|
|
287
|
+
cancelScheduledRefresh() {
|
|
288
|
+
if (this.refreshTimer) {
|
|
289
|
+
clearTimeout(this.refreshTimer);
|
|
290
|
+
this.refreshTimer = null;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
exports.TokenManager = TokenManager;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Token Storage Implementations
|
|
3
|
+
* Provides different storage strategies for OAuth tokens
|
|
4
|
+
*/
|
|
5
|
+
import { OAuthTokens, TokenStorage } from "./types";
|
|
6
|
+
/**
|
|
7
|
+
* LocalStorage-based token storage
|
|
8
|
+
* Persists tokens across browser sessions
|
|
9
|
+
* Suitable for web applications where persistence is desired
|
|
10
|
+
*/
|
|
11
|
+
export declare class LocalStorageTokenStorage implements TokenStorage {
|
|
12
|
+
private readonly storageKey;
|
|
13
|
+
constructor(storageKey?: string);
|
|
14
|
+
getTokens(): Promise<OAuthTokens | null>;
|
|
15
|
+
setTokens(tokens: OAuthTokens): Promise<void>;
|
|
16
|
+
clearTokens(): Promise<void>;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* SessionStorage-based token storage
|
|
20
|
+
* Tokens are cleared when the browser tab is closed
|
|
21
|
+
* More secure than localStorage for sensitive applications
|
|
22
|
+
*/
|
|
23
|
+
export declare class SessionStorageTokenStorage implements TokenStorage {
|
|
24
|
+
private readonly storageKey;
|
|
25
|
+
constructor(storageKey?: string);
|
|
26
|
+
getTokens(): Promise<OAuthTokens | null>;
|
|
27
|
+
setTokens(tokens: OAuthTokens): Promise<void>;
|
|
28
|
+
clearTokens(): Promise<void>;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* In-memory token storage
|
|
32
|
+
* Tokens are lost on page refresh - most secure option
|
|
33
|
+
* Suitable for high-security applications or server-side usage
|
|
34
|
+
*/
|
|
35
|
+
export declare class MemoryTokenStorage implements TokenStorage {
|
|
36
|
+
private tokens;
|
|
37
|
+
getTokens(): Promise<OAuthTokens | null>;
|
|
38
|
+
setTokens(tokens: OAuthTokens): Promise<void>;
|
|
39
|
+
clearTokens(): Promise<void>;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Create a default token storage based on the environment
|
|
43
|
+
* - Browser: LocalStorageTokenStorage
|
|
44
|
+
* - Server/Node.js: MemoryTokenStorage
|
|
45
|
+
*/
|
|
46
|
+
export declare function createDefaultTokenStorage(): TokenStorage;
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Token Storage Implementations
|
|
4
|
+
* Provides different storage strategies for OAuth tokens
|
|
5
|
+
*/
|
|
6
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
7
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
8
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
9
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
10
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
11
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
12
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
13
|
+
});
|
|
14
|
+
};
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.MemoryTokenStorage = exports.SessionStorageTokenStorage = exports.LocalStorageTokenStorage = void 0;
|
|
17
|
+
exports.createDefaultTokenStorage = createDefaultTokenStorage;
|
|
18
|
+
const STORAGE_KEY = "crimson_oauth_tokens";
|
|
19
|
+
/**
|
|
20
|
+
* LocalStorage-based token storage
|
|
21
|
+
* Persists tokens across browser sessions
|
|
22
|
+
* Suitable for web applications where persistence is desired
|
|
23
|
+
*/
|
|
24
|
+
class LocalStorageTokenStorage {
|
|
25
|
+
constructor(storageKey = STORAGE_KEY) {
|
|
26
|
+
this.storageKey = storageKey;
|
|
27
|
+
}
|
|
28
|
+
getTokens() {
|
|
29
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
30
|
+
if (typeof window === "undefined" || !window.localStorage) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
try {
|
|
34
|
+
const stored = localStorage.getItem(this.storageKey);
|
|
35
|
+
if (!stored) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
const tokens = JSON.parse(stored);
|
|
39
|
+
// Validate token structure
|
|
40
|
+
if (!tokens.accessToken || !tokens.expiresAt) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
return tokens;
|
|
44
|
+
}
|
|
45
|
+
catch (_a) {
|
|
46
|
+
// Invalid JSON or other error
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
setTokens(tokens) {
|
|
52
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
53
|
+
if (typeof window === "undefined" || !window.localStorage) {
|
|
54
|
+
throw new Error("localStorage is not available");
|
|
55
|
+
}
|
|
56
|
+
localStorage.setItem(this.storageKey, JSON.stringify(tokens));
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
clearTokens() {
|
|
60
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
61
|
+
if (typeof window === "undefined" || !window.localStorage) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
localStorage.removeItem(this.storageKey);
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
exports.LocalStorageTokenStorage = LocalStorageTokenStorage;
|
|
69
|
+
/**
|
|
70
|
+
* SessionStorage-based token storage
|
|
71
|
+
* Tokens are cleared when the browser tab is closed
|
|
72
|
+
* More secure than localStorage for sensitive applications
|
|
73
|
+
*/
|
|
74
|
+
class SessionStorageTokenStorage {
|
|
75
|
+
constructor(storageKey = STORAGE_KEY) {
|
|
76
|
+
this.storageKey = storageKey;
|
|
77
|
+
}
|
|
78
|
+
getTokens() {
|
|
79
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
80
|
+
if (typeof window === "undefined" || !window.sessionStorage) {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
try {
|
|
84
|
+
const stored = sessionStorage.getItem(this.storageKey);
|
|
85
|
+
if (!stored) {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
const tokens = JSON.parse(stored);
|
|
89
|
+
// Validate token structure
|
|
90
|
+
if (!tokens.accessToken || !tokens.expiresAt) {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
return tokens;
|
|
94
|
+
}
|
|
95
|
+
catch (_a) {
|
|
96
|
+
// Invalid JSON or other error
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
setTokens(tokens) {
|
|
102
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
103
|
+
if (typeof window === "undefined" || !window.sessionStorage) {
|
|
104
|
+
throw new Error("sessionStorage is not available");
|
|
105
|
+
}
|
|
106
|
+
sessionStorage.setItem(this.storageKey, JSON.stringify(tokens));
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
clearTokens() {
|
|
110
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
111
|
+
if (typeof window === "undefined" || !window.sessionStorage) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
sessionStorage.removeItem(this.storageKey);
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
exports.SessionStorageTokenStorage = SessionStorageTokenStorage;
|
|
119
|
+
/**
|
|
120
|
+
* In-memory token storage
|
|
121
|
+
* Tokens are lost on page refresh - most secure option
|
|
122
|
+
* Suitable for high-security applications or server-side usage
|
|
123
|
+
*/
|
|
124
|
+
class MemoryTokenStorage {
|
|
125
|
+
constructor() {
|
|
126
|
+
this.tokens = null;
|
|
127
|
+
}
|
|
128
|
+
getTokens() {
|
|
129
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
130
|
+
return this.tokens;
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
setTokens(tokens) {
|
|
134
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
135
|
+
this.tokens = tokens;
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
clearTokens() {
|
|
139
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
140
|
+
this.tokens = null;
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
exports.MemoryTokenStorage = MemoryTokenStorage;
|
|
145
|
+
/**
|
|
146
|
+
* Create a default token storage based on the environment
|
|
147
|
+
* - Browser: LocalStorageTokenStorage
|
|
148
|
+
* - Server/Node.js: MemoryTokenStorage
|
|
149
|
+
*/
|
|
150
|
+
function createDefaultTokenStorage() {
|
|
151
|
+
if (typeof window !== "undefined" && window.localStorage) {
|
|
152
|
+
return new LocalStorageTokenStorage();
|
|
153
|
+
}
|
|
154
|
+
return new MemoryTokenStorage();
|
|
155
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth Authentication Types
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* OAuth configuration for the adapter
|
|
6
|
+
*/
|
|
7
|
+
export interface OAuthConfig {
|
|
8
|
+
/**
|
|
9
|
+
* OAuth client ID
|
|
10
|
+
*/
|
|
11
|
+
clientId: string;
|
|
12
|
+
/**
|
|
13
|
+
* OAuth client secret (for confidential clients only)
|
|
14
|
+
*/
|
|
15
|
+
clientSecret?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Redirect URI for OAuth callback
|
|
18
|
+
*/
|
|
19
|
+
redirectUri: string;
|
|
20
|
+
/**
|
|
21
|
+
* Authorization endpoint URL
|
|
22
|
+
*/
|
|
23
|
+
authorizationEndpoint: string;
|
|
24
|
+
/**
|
|
25
|
+
* Token endpoint URL
|
|
26
|
+
*/
|
|
27
|
+
tokenEndpoint: string;
|
|
28
|
+
/**
|
|
29
|
+
* Token revocation endpoint URL (optional)
|
|
30
|
+
*/
|
|
31
|
+
revocationEndpoint?: string;
|
|
32
|
+
/**
|
|
33
|
+
* Requested scopes
|
|
34
|
+
*/
|
|
35
|
+
scope?: string[];
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* OAuth token response
|
|
39
|
+
*/
|
|
40
|
+
export interface OAuthTokens {
|
|
41
|
+
/** Access token */
|
|
42
|
+
accessToken: string;
|
|
43
|
+
/** Refresh token (if granted) */
|
|
44
|
+
refreshToken?: string;
|
|
45
|
+
/** Token type (always Bearer) */
|
|
46
|
+
tokenType: string;
|
|
47
|
+
/** Expiration timestamp in milliseconds */
|
|
48
|
+
expiresAt: number;
|
|
49
|
+
/** Granted scopes */
|
|
50
|
+
scope?: string[];
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* OAuth authorization options
|
|
54
|
+
*/
|
|
55
|
+
export interface AuthorizeOptions {
|
|
56
|
+
/**
|
|
57
|
+
* Override redirect URI
|
|
58
|
+
*/
|
|
59
|
+
redirectUri?: string;
|
|
60
|
+
/**
|
|
61
|
+
* Override requested scopes
|
|
62
|
+
*/
|
|
63
|
+
scope?: string[];
|
|
64
|
+
/**
|
|
65
|
+
* Force user to re-authenticate
|
|
66
|
+
*/
|
|
67
|
+
prompt?: "login" | "consent" | "none";
|
|
68
|
+
/**
|
|
69
|
+
* Pre-fill login with this email
|
|
70
|
+
*/
|
|
71
|
+
loginHint?: string;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* OAuth callback parameters (from URL query string)
|
|
75
|
+
*/
|
|
76
|
+
export interface CallbackParams {
|
|
77
|
+
/** Authorization code */
|
|
78
|
+
code?: string;
|
|
79
|
+
/** State parameter for CSRF protection */
|
|
80
|
+
state?: string;
|
|
81
|
+
/** OAuth error code (if authorization failed) */
|
|
82
|
+
error?: string;
|
|
83
|
+
/** OAuth error description */
|
|
84
|
+
errorDescription?: string;
|
|
85
|
+
/** Override redirect URI for code exchange */
|
|
86
|
+
redirectUri?: string;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* OAuth error
|
|
90
|
+
*/
|
|
91
|
+
export interface OAuthError {
|
|
92
|
+
error: string;
|
|
93
|
+
errorDescription?: string;
|
|
94
|
+
errorUri?: string;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* OAuth Authentication state
|
|
98
|
+
*/
|
|
99
|
+
export interface OAuthAuthState {
|
|
100
|
+
/** Whether the user is authenticated */
|
|
101
|
+
isAuthenticated: boolean;
|
|
102
|
+
/** Whether authentication is in progress */
|
|
103
|
+
isLoading: boolean;
|
|
104
|
+
/** Current tokens (if authenticated) */
|
|
105
|
+
tokens: OAuthTokens | null;
|
|
106
|
+
/** Last error (if any) */
|
|
107
|
+
error: Error | OAuthError | null;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* OAuth auth state change listener
|
|
111
|
+
*/
|
|
112
|
+
export type OAuthAuthStateListener = (state: OAuthAuthState) => void;
|
|
113
|
+
/**
|
|
114
|
+
* Token refresh function signature
|
|
115
|
+
*/
|
|
116
|
+
export type TokenRefreshFunction = (refreshToken: string) => Promise<OAuthTokens | null>;
|
|
117
|
+
/**
|
|
118
|
+
* Token storage interface
|
|
119
|
+
* All methods are async to support various storage backends
|
|
120
|
+
*/
|
|
121
|
+
export interface TokenStorage {
|
|
122
|
+
/** Get stored tokens */
|
|
123
|
+
getTokens(): Promise<OAuthTokens | null>;
|
|
124
|
+
/** Store tokens */
|
|
125
|
+
setTokens(tokens: OAuthTokens): Promise<void>;
|
|
126
|
+
/** Clear stored tokens */
|
|
127
|
+
clearTokens(): Promise<void>;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* PKCE state stored during authorization
|
|
131
|
+
*/
|
|
132
|
+
export interface PkceState {
|
|
133
|
+
/** Code verifier (kept secret) */
|
|
134
|
+
codeVerifier: string;
|
|
135
|
+
/** State parameter for CSRF protection */
|
|
136
|
+
state: string;
|
|
137
|
+
/** Timestamp when PKCE state was created */
|
|
138
|
+
createdAt: number;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* OAuth endpoints paths
|
|
142
|
+
*/
|
|
143
|
+
export declare const OAUTH_ENDPOINTS: {
|
|
144
|
+
readonly AUTHORIZE: "/oauth/authorize";
|
|
145
|
+
readonly TOKEN: "/oauth/token";
|
|
146
|
+
readonly REVOKE: "/oauth/revoke";
|
|
147
|
+
readonly USERINFO: "/oauth/userinfo";
|
|
148
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* OAuth Authentication Types
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.OAUTH_ENDPOINTS = void 0;
|
|
7
|
+
/**
|
|
8
|
+
* OAuth endpoints paths
|
|
9
|
+
*/
|
|
10
|
+
exports.OAUTH_ENDPOINTS = {
|
|
11
|
+
AUTHORIZE: "/oauth/authorize",
|
|
12
|
+
TOKEN: "/oauth/token",
|
|
13
|
+
REVOKE: "/oauth/revoke",
|
|
14
|
+
USERINFO: "/oauth/userinfo",
|
|
15
|
+
};
|