@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.
Files changed (70) hide show
  1. package/README.md +377 -0
  2. package/dist/core/account.d.ts +14 -0
  3. package/dist/core/account.js +30 -0
  4. package/dist/core/auth/index.d.ts +11 -0
  5. package/dist/core/auth/index.js +25 -0
  6. package/dist/core/auth/oauth-adapter.d.ts +78 -0
  7. package/dist/core/auth/oauth-adapter.js +341 -0
  8. package/dist/core/auth/pkce.d.ts +20 -0
  9. package/dist/core/auth/pkce.js +112 -0
  10. package/dist/core/auth/token-manager.d.ts +68 -0
  11. package/dist/core/auth/token-manager.js +294 -0
  12. package/dist/core/auth/token-storage.d.ts +46 -0
  13. package/dist/core/auth/token-storage.js +155 -0
  14. package/dist/core/auth/types.d.ts +148 -0
  15. package/dist/core/auth/types.js +15 -0
  16. package/dist/core/client.d.ts +84 -0
  17. package/dist/core/client.js +229 -0
  18. package/dist/core/index.d.ts +11 -0
  19. package/dist/core/index.js +47 -0
  20. package/dist/core/missionLibrary.d.ts +68 -0
  21. package/dist/core/missionLibrary.js +143 -0
  22. package/dist/core/missions.d.ts +45 -0
  23. package/dist/core/missions.js +140 -0
  24. package/dist/core/roadmap.d.ts +8 -0
  25. package/dist/core/roadmap.js +18 -0
  26. package/dist/core/studentProfile.d.ts +21 -0
  27. package/dist/core/studentProfile.js +41 -0
  28. package/dist/core/tasks.d.ts +117 -0
  29. package/dist/core/tasks.js +288 -0
  30. package/dist/core/types.d.ts +402 -0
  31. package/dist/core/types.js +2 -0
  32. package/dist/core/users.d.ts +21 -0
  33. package/dist/core/users.js +46 -0
  34. package/dist/iframe/auth-state.d.ts +7 -0
  35. package/dist/iframe/auth-state.js +125 -0
  36. package/dist/iframe/constants.d.ts +8 -0
  37. package/dist/iframe/constants.js +29 -0
  38. package/dist/iframe/index.d.ts +5 -0
  39. package/dist/iframe/index.js +17 -0
  40. package/dist/iframe/listener.d.ts +2 -0
  41. package/dist/iframe/listener.js +57 -0
  42. package/dist/iframe/types.d.ts +18 -0
  43. package/dist/iframe/types.js +2 -0
  44. package/dist/index.d.ts +2 -0
  45. package/dist/index.js +22 -0
  46. package/dist/react/hooks/index.d.ts +10 -0
  47. package/dist/react/hooks/index.js +48 -0
  48. package/dist/react/hooks/useAccount.d.ts +13 -0
  49. package/dist/react/hooks/useAccount.js +39 -0
  50. package/dist/react/hooks/useAuthState.d.ts +2 -0
  51. package/dist/react/hooks/useAuthState.js +18 -0
  52. package/dist/react/hooks/useMissionLibrary.d.ts +31 -0
  53. package/dist/react/hooks/useMissionLibrary.js +183 -0
  54. package/dist/react/hooks/useMissions.d.ts +24 -0
  55. package/dist/react/hooks/useMissions.js +104 -0
  56. package/dist/react/hooks/useOAuth.d.ts +94 -0
  57. package/dist/react/hooks/useOAuth.js +211 -0
  58. package/dist/react/hooks/useRoadmapContext.d.ts +2 -0
  59. package/dist/react/hooks/useRoadmapContext.js +29 -0
  60. package/dist/react/hooks/useStudentProfile.d.ts +24 -0
  61. package/dist/react/hooks/useStudentProfile.js +65 -0
  62. package/dist/react/hooks/useTasks.d.ts +26 -0
  63. package/dist/react/hooks/useTasks.js +137 -0
  64. package/dist/react/hooks/useUsers.d.ts +9 -0
  65. package/dist/react/hooks/useUsers.js +50 -0
  66. package/dist/react/index.d.ts +3 -0
  67. package/dist/react/index.js +21 -0
  68. package/dist/react/provider.d.ts +16 -0
  69. package/dist/react/provider.js +41 -0
  70. package/package.json +61 -0
@@ -0,0 +1,341 @@
1
+ "use strict";
2
+ /**
3
+ * OAuth Adapter
4
+ * Main entry point for OAuth 2.0 authentication with PKCE
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.OAuthAdapter = void 0;
17
+ exports.createCrimsonOAuthAdapter = createCrimsonOAuthAdapter;
18
+ const pkce_1 = require("./pkce");
19
+ const token_manager_1 = require("./token-manager");
20
+ const token_storage_1 = require("./token-storage");
21
+ const PKCE_STATE_KEY = "crimson_oauth_pkce_state";
22
+ class OAuthAdapter {
23
+ constructor(config) {
24
+ var _a, _b;
25
+ this.initialized = false;
26
+ this.config = config;
27
+ this.storage = (_a = config.storage) !== null && _a !== void 0 ? _a : (0, token_storage_1.createDefaultTokenStorage)();
28
+ this.tokenManager = new token_manager_1.TokenManager({
29
+ storage: this.storage,
30
+ onRefreshNeeded: this.refreshTokenWithApi.bind(this),
31
+ autoRefresh: (_b = config.autoRefresh) !== null && _b !== void 0 ? _b : true,
32
+ });
33
+ }
34
+ /**
35
+ * Initialize the OAuth adapter
36
+ * Loads existing tokens and sets up auto-refresh
37
+ */
38
+ initialize() {
39
+ return __awaiter(this, void 0, void 0, function* () {
40
+ if (this.initialized) {
41
+ return;
42
+ }
43
+ yield this.tokenManager.initialize();
44
+ this.initialized = true;
45
+ });
46
+ }
47
+ /**
48
+ * Get the authorization URL for OAuth flow
49
+ */
50
+ getAuthorizeUrl(options) {
51
+ return __awaiter(this, void 0, void 0, function* () {
52
+ var _a, _b, _c;
53
+ const codeVerifier = (0, pkce_1.generateCodeVerifier)();
54
+ const codeChallenge = yield (0, pkce_1.generateCodeChallenge)(codeVerifier);
55
+ const state = (0, pkce_1.generateState)();
56
+ // Store PKCE state for callback verification
57
+ const pkceState = {
58
+ codeVerifier,
59
+ state,
60
+ createdAt: Date.now(),
61
+ };
62
+ this.storePkceState(pkceState);
63
+ // Build authorization URL
64
+ const params = new URLSearchParams({
65
+ response_type: "code",
66
+ client_id: this.config.clientId,
67
+ redirect_uri: (_a = options === null || options === void 0 ? void 0 : options.redirectUri) !== null && _a !== void 0 ? _a : this.config.redirectUri,
68
+ scope: ((_c = (_b = options === null || options === void 0 ? void 0 : options.scope) !== null && _b !== void 0 ? _b : this.config.scope) !== null && _c !== void 0 ? _c : ["profile"]).join(" "),
69
+ state,
70
+ code_challenge: codeChallenge,
71
+ code_challenge_method: "S256",
72
+ });
73
+ return `${this.config.authorizationEndpoint}?${params.toString()}`;
74
+ });
75
+ }
76
+ /**
77
+ * Start the OAuth authorization flow
78
+ * Redirects the user to the authorization page
79
+ */
80
+ authorize(options) {
81
+ return __awaiter(this, void 0, void 0, function* () {
82
+ const url = yield this.getAuthorizeUrl(options);
83
+ if (typeof window !== "undefined") {
84
+ window.location.href = url;
85
+ }
86
+ else {
87
+ throw new Error("authorize() can only be called in a browser environment");
88
+ }
89
+ });
90
+ }
91
+ /**
92
+ * Handle the OAuth callback
93
+ * Exchanges the authorization code for tokens
94
+ */
95
+ handleCallback(params) {
96
+ return __awaiter(this, void 0, void 0, function* () {
97
+ // Check for error response
98
+ if (params.error) {
99
+ const error = {
100
+ error: params.error,
101
+ errorDescription: params.errorDescription,
102
+ };
103
+ throw error;
104
+ }
105
+ // Validate required parameters
106
+ if (!params.code) {
107
+ throw {
108
+ error: "invalid_request",
109
+ errorDescription: "Missing authorization code",
110
+ };
111
+ }
112
+ if (!params.state) {
113
+ throw {
114
+ error: "invalid_request",
115
+ errorDescription: "Missing state parameter",
116
+ };
117
+ }
118
+ // Retrieve and validate PKCE state
119
+ const pkceState = this.getPkceState();
120
+ if (!pkceState) {
121
+ throw {
122
+ error: "invalid_request",
123
+ errorDescription: "No PKCE state found",
124
+ };
125
+ }
126
+ // Validate state matches
127
+ if (pkceState.state !== params.state) {
128
+ throw {
129
+ error: "invalid_request",
130
+ errorDescription: "State mismatch - possible CSRF attack",
131
+ };
132
+ }
133
+ // Check if PKCE state is expired (10 minutes)
134
+ if (Date.now() - pkceState.createdAt > 10 * 60 * 1000) {
135
+ this.clearPkceState();
136
+ throw {
137
+ error: "invalid_request",
138
+ errorDescription: "Authorization request expired",
139
+ };
140
+ }
141
+ // Exchange code for tokens
142
+ try {
143
+ const tokens = yield this.exchangeCode(params.code, pkceState.codeVerifier, params.redirectUri);
144
+ // Clear PKCE state after successful exchange
145
+ this.clearPkceState();
146
+ // Store tokens
147
+ yield this.tokenManager.setTokens(tokens);
148
+ return tokens;
149
+ }
150
+ catch (error) {
151
+ this.clearPkceState();
152
+ throw error;
153
+ }
154
+ });
155
+ }
156
+ /**
157
+ * Manually refresh the access token
158
+ */
159
+ refreshToken() {
160
+ return __awaiter(this, void 0, void 0, function* () {
161
+ return this.tokenManager.refreshToken();
162
+ });
163
+ }
164
+ /**
165
+ * Logout - revoke tokens and clear storage
166
+ */
167
+ logout() {
168
+ return __awaiter(this, void 0, void 0, function* () {
169
+ const state = this.tokenManager.getState();
170
+ const tokens = state.tokens;
171
+ // Try to revoke tokens on the server
172
+ if (tokens === null || tokens === void 0 ? void 0 : tokens.accessToken) {
173
+ try {
174
+ yield this.revokeToken(tokens.accessToken, "access_token");
175
+ }
176
+ catch (_a) {
177
+ // Ignore revocation errors - we're logging out anyway
178
+ }
179
+ }
180
+ // Clear local tokens
181
+ yield this.tokenManager.clearTokens();
182
+ });
183
+ }
184
+ /**
185
+ * Get current access token if valid
186
+ */
187
+ getAccessToken() {
188
+ return __awaiter(this, void 0, void 0, function* () {
189
+ return this.tokenManager.getAccessToken();
190
+ });
191
+ }
192
+ /**
193
+ * Get current authentication state
194
+ */
195
+ getAuthState() {
196
+ return this.tokenManager.getState();
197
+ }
198
+ /**
199
+ * Subscribe to authentication state changes
200
+ */
201
+ subscribe(listener) {
202
+ return this.tokenManager.subscribe(listener);
203
+ }
204
+ /**
205
+ * Check if user is authenticated
206
+ */
207
+ isAuthenticated() {
208
+ return this.tokenManager.getState().isAuthenticated;
209
+ }
210
+ /**
211
+ * Clean up resources
212
+ */
213
+ destroy() {
214
+ this.tokenManager.destroy();
215
+ }
216
+ // Private methods
217
+ exchangeCode(code, codeVerifier, redirectUri) {
218
+ return __awaiter(this, void 0, void 0, function* () {
219
+ var _a, _b;
220
+ const body = new URLSearchParams({
221
+ grant_type: "authorization_code",
222
+ code,
223
+ redirect_uri: redirectUri !== null && redirectUri !== void 0 ? redirectUri : this.config.redirectUri,
224
+ client_id: this.config.clientId,
225
+ code_verifier: codeVerifier,
226
+ });
227
+ const response = yield fetch(this.config.tokenEndpoint, {
228
+ method: "POST",
229
+ headers: {
230
+ "Content-Type": "application/x-www-form-urlencoded",
231
+ },
232
+ body: body.toString(),
233
+ });
234
+ if (!response.ok) {
235
+ const error = yield response.json().catch(() => ({}));
236
+ throw {
237
+ error: (_a = error.error) !== null && _a !== void 0 ? _a : "token_error",
238
+ errorDescription: (_b = error.error_description) !== null && _b !== void 0 ? _b : "Failed to exchange authorization code",
239
+ };
240
+ }
241
+ const data = yield response.json();
242
+ return this.parseTokenResponse(data);
243
+ });
244
+ }
245
+ refreshTokenWithApi(refreshToken) {
246
+ return __awaiter(this, void 0, void 0, function* () {
247
+ var _a;
248
+ const body = new URLSearchParams({
249
+ grant_type: "refresh_token",
250
+ refresh_token: refreshToken,
251
+ client_id: this.config.clientId,
252
+ });
253
+ const response = yield fetch(this.config.tokenEndpoint, {
254
+ method: "POST",
255
+ headers: {
256
+ "Content-Type": "application/x-www-form-urlencoded",
257
+ },
258
+ body: body.toString(),
259
+ });
260
+ if (!response.ok) {
261
+ const error = yield response.json().catch(() => ({}));
262
+ throw new Error((_a = error.error) !== null && _a !== void 0 ? _a : "refresh_failed");
263
+ }
264
+ const data = yield response.json();
265
+ return this.parseTokenResponse(data);
266
+ });
267
+ }
268
+ revokeToken(token, tokenTypeHint) {
269
+ return __awaiter(this, void 0, void 0, function* () {
270
+ if (!this.config.revocationEndpoint) {
271
+ return;
272
+ }
273
+ const body = new URLSearchParams({
274
+ token,
275
+ client_id: this.config.clientId,
276
+ });
277
+ if (tokenTypeHint) {
278
+ body.set("token_type_hint", tokenTypeHint);
279
+ }
280
+ yield fetch(this.config.revocationEndpoint, {
281
+ method: "POST",
282
+ headers: {
283
+ "Content-Type": "application/x-www-form-urlencoded",
284
+ },
285
+ body: body.toString(),
286
+ });
287
+ // RFC 7009: revocation endpoint returns 200 even on error
288
+ // We don't need to check the response
289
+ });
290
+ }
291
+ parseTokenResponse(data) {
292
+ var _a, _b;
293
+ const accessToken = data.access_token;
294
+ const expiresIn = (_a = data.expires_in) !== null && _a !== void 0 ? _a : 3600;
295
+ const refreshToken = data.refresh_token;
296
+ const scope = data.scope;
297
+ const tokenType = (_b = data.token_type) !== null && _b !== void 0 ? _b : "Bearer";
298
+ if (!accessToken) {
299
+ throw {
300
+ error: "invalid_response",
301
+ errorDescription: "Missing access_token in response",
302
+ };
303
+ }
304
+ return {
305
+ accessToken,
306
+ refreshToken,
307
+ expiresAt: Date.now() + expiresIn * 1000,
308
+ scope: scope === null || scope === void 0 ? void 0 : scope.split(" "),
309
+ tokenType,
310
+ };
311
+ }
312
+ storePkceState(state) {
313
+ if (typeof window !== "undefined" && window.sessionStorage) {
314
+ sessionStorage.setItem(PKCE_STATE_KEY, JSON.stringify(state));
315
+ }
316
+ }
317
+ getPkceState() {
318
+ if (typeof window === "undefined" || !window.sessionStorage) {
319
+ return null;
320
+ }
321
+ try {
322
+ const stored = sessionStorage.getItem(PKCE_STATE_KEY);
323
+ return stored ? JSON.parse(stored) : null;
324
+ }
325
+ catch (_a) {
326
+ return null;
327
+ }
328
+ }
329
+ clearPkceState() {
330
+ if (typeof window !== "undefined" && window.sessionStorage) {
331
+ sessionStorage.removeItem(PKCE_STATE_KEY);
332
+ }
333
+ }
334
+ }
335
+ exports.OAuthAdapter = OAuthAdapter;
336
+ /**
337
+ * Create an OAuth adapter with default configuration for Crimson API
338
+ */
339
+ function createCrimsonOAuthAdapter(config) {
340
+ return new OAuthAdapter(Object.assign(Object.assign({}, config), { authorizationEndpoint: `${config.apiUrl}/oauth/authorize`, tokenEndpoint: `${config.apiUrl}/oauth/token`, revocationEndpoint: `${config.apiUrl}/oauth/revoke` }));
341
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * PKCE (Proof Key for Code Exchange) Utilities
3
+ * Client-side implementation for OAuth 2.0 with PKCE
4
+ */
5
+ /**
6
+ * Generate a code verifier for PKCE
7
+ * @returns A 64-character random string
8
+ */
9
+ export declare function generateCodeVerifier(): string;
10
+ /**
11
+ * Generate a state parameter for CSRF protection
12
+ * @returns A 32-character random string
13
+ */
14
+ export declare function generateState(): string;
15
+ /**
16
+ * Generate a code challenge from a code verifier using SHA256 (S256 method)
17
+ * @param codeVerifier The code verifier
18
+ * @returns Base64URL-encoded SHA256 hash
19
+ */
20
+ export declare function generateCodeChallenge(codeVerifier: string): Promise<string>;
@@ -0,0 +1,112 @@
1
+ "use strict";
2
+ /**
3
+ * PKCE (Proof Key for Code Exchange) Utilities
4
+ * Client-side implementation for OAuth 2.0 with PKCE
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.generateCodeVerifier = generateCodeVerifier;
17
+ exports.generateState = generateState;
18
+ exports.generateCodeChallenge = generateCodeChallenge;
19
+ /**
20
+ * Generate a cryptographically secure random string
21
+ */
22
+ function generateRandomString(length) {
23
+ const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~";
24
+ const randomValues = new Uint8Array(length);
25
+ if (typeof window !== "undefined" && window.crypto) {
26
+ window.crypto.getRandomValues(randomValues);
27
+ }
28
+ else if (typeof globalThis !== "undefined" && globalThis.crypto) {
29
+ globalThis.crypto.getRandomValues(randomValues);
30
+ }
31
+ else {
32
+ // Fallback for Node.js environments
33
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
34
+ const nodeCrypto = require("crypto");
35
+ const buffer = nodeCrypto.randomBytes(length);
36
+ for (let i = 0; i < length; i++) {
37
+ randomValues[i] = buffer[i];
38
+ }
39
+ }
40
+ let result = "";
41
+ for (let i = 0; i < length; i++) {
42
+ result += charset[randomValues[i] % charset.length];
43
+ }
44
+ return result;
45
+ }
46
+ /**
47
+ * Generate a code verifier for PKCE
48
+ * @returns A 64-character random string
49
+ */
50
+ function generateCodeVerifier() {
51
+ return generateRandomString(64);
52
+ }
53
+ /**
54
+ * Generate a state parameter for CSRF protection
55
+ * @returns A 32-character random string
56
+ */
57
+ function generateState() {
58
+ return generateRandomString(32);
59
+ }
60
+ /**
61
+ * Generate a code challenge from a code verifier using SHA256 (S256 method)
62
+ * @param codeVerifier The code verifier
63
+ * @returns Base64URL-encoded SHA256 hash
64
+ */
65
+ function generateCodeChallenge(codeVerifier) {
66
+ return __awaiter(this, void 0, void 0, function* () {
67
+ var _a, _b;
68
+ // Convert string to Uint8Array
69
+ const encoder = new TextEncoder();
70
+ const data = encoder.encode(codeVerifier);
71
+ // Hash with SHA-256
72
+ let hashBuffer;
73
+ if (typeof window !== "undefined" && ((_a = window.crypto) === null || _a === void 0 ? void 0 : _a.subtle)) {
74
+ hashBuffer = yield window.crypto.subtle.digest("SHA-256", data);
75
+ }
76
+ else if (typeof globalThis !== "undefined" && ((_b = globalThis.crypto) === null || _b === void 0 ? void 0 : _b.subtle)) {
77
+ hashBuffer = yield globalThis.crypto.subtle.digest("SHA-256", data);
78
+ }
79
+ else {
80
+ // Fallback for Node.js environments
81
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
82
+ const nodeCrypto = require("crypto");
83
+ const hash = nodeCrypto.createHash("sha256").update(codeVerifier).digest();
84
+ hashBuffer = hash.buffer.slice(hash.byteOffset, hash.byteOffset + hash.byteLength);
85
+ }
86
+ // Convert to base64url encoding
87
+ return base64UrlEncode(new Uint8Array(hashBuffer));
88
+ });
89
+ }
90
+ /**
91
+ * Base64URL encode a buffer
92
+ * @param buffer The buffer to encode
93
+ * @returns Base64URL-encoded string (no padding)
94
+ */
95
+ function base64UrlEncode(buffer) {
96
+ // Convert to base64
97
+ let base64 = "";
98
+ const bytes = new Uint8Array(buffer);
99
+ for (let i = 0; i < bytes.byteLength; i++) {
100
+ base64 += String.fromCharCode(bytes[i]);
101
+ }
102
+ // Use btoa if available (browser), otherwise Buffer (Node.js)
103
+ let encoded;
104
+ if (typeof btoa !== "undefined") {
105
+ encoded = btoa(base64);
106
+ }
107
+ else {
108
+ encoded = Buffer.from(base64, "binary").toString("base64");
109
+ }
110
+ // Convert to base64url (no padding)
111
+ return encoded.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
112
+ }
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Token Manager
3
+ * Handles token lifecycle, automatic refresh, and state management
4
+ */
5
+ import { OAuthTokens, TokenStorage, OAuthAuthState, OAuthAuthStateListener, TokenRefreshFunction } from "./types";
6
+ export interface TokenManagerConfig {
7
+ /** Token storage implementation */
8
+ storage: TokenStorage;
9
+ /** Function to call when tokens need refreshing */
10
+ onRefreshNeeded: TokenRefreshFunction;
11
+ /** Buffer time before expiration to trigger refresh (default: 5 minutes) */
12
+ refreshBufferMs?: number;
13
+ /** Enable automatic background refresh (default: true) */
14
+ autoRefresh?: boolean;
15
+ }
16
+ export declare class TokenManager {
17
+ private storage;
18
+ private onRefreshNeeded;
19
+ private refreshBufferMs;
20
+ private autoRefresh;
21
+ private listeners;
22
+ private refreshPromise;
23
+ private refreshTimer;
24
+ private lastRefreshAttempt;
25
+ private currentState;
26
+ constructor(config: TokenManagerConfig);
27
+ /**
28
+ * Initialize the token manager
29
+ * Loads tokens from storage and sets up auto-refresh if enabled
30
+ */
31
+ initialize(): Promise<void>;
32
+ /**
33
+ * Get current authentication state
34
+ */
35
+ getState(): OAuthAuthState;
36
+ /**
37
+ * Get current access token if valid
38
+ * Automatically refreshes if needed and possible
39
+ */
40
+ getAccessToken(): Promise<string | null>;
41
+ /**
42
+ * Set new tokens (after successful authorization)
43
+ */
44
+ setTokens(tokens: OAuthTokens): Promise<void>;
45
+ /**
46
+ * Refresh the access token
47
+ * Uses a lock to prevent concurrent refresh attempts
48
+ */
49
+ refreshToken(): Promise<OAuthTokens | null>;
50
+ private doRefresh;
51
+ /**
52
+ * Clear tokens (logout)
53
+ */
54
+ clearTokens(): Promise<void>;
55
+ /**
56
+ * Subscribe to auth state changes
57
+ */
58
+ subscribe(listener: OAuthAuthStateListener): () => void;
59
+ /**
60
+ * Clean up resources
61
+ */
62
+ destroy(): void;
63
+ private updateState;
64
+ private isTokenExpired;
65
+ private shouldRefresh;
66
+ private scheduleRefresh;
67
+ private cancelScheduledRefresh;
68
+ }