@jezweb/oauth-token-manager 0.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 +184 -0
- package/SECURITY.md +162 -0
- package/dist/crypto.d.ts +43 -0
- package/dist/crypto.d.ts.map +1 -0
- package/dist/crypto.js +107 -0
- package/dist/crypto.js.map +1 -0
- package/dist/errors.d.ts +75 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +117 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +54 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +58 -0
- package/dist/index.js.map +1 -0
- package/dist/providers/github.d.ts +45 -0
- package/dist/providers/github.d.ts.map +1 -0
- package/dist/providers/github.js +70 -0
- package/dist/providers/github.js.map +1 -0
- package/dist/providers/google.d.ts +24 -0
- package/dist/providers/google.d.ts.map +1 -0
- package/dist/providers/google.js +63 -0
- package/dist/providers/google.js.map +1 -0
- package/dist/providers/microsoft.d.ts +29 -0
- package/dist/providers/microsoft.d.ts.map +1 -0
- package/dist/providers/microsoft.js +72 -0
- package/dist/providers/microsoft.js.map +1 -0
- package/dist/providers/types.d.ts +7 -0
- package/dist/providers/types.d.ts.map +1 -0
- package/dist/providers/types.js +7 -0
- package/dist/providers/types.js.map +1 -0
- package/dist/storage/d1.d.ts +22 -0
- package/dist/storage/d1.d.ts.map +1 -0
- package/dist/storage/d1.js +31 -0
- package/dist/storage/d1.js.map +1 -0
- package/dist/storage/kv.d.ts +38 -0
- package/dist/storage/kv.d.ts.map +1 -0
- package/dist/storage/kv.js +143 -0
- package/dist/storage/kv.js.map +1 -0
- package/dist/storage/types.d.ts +7 -0
- package/dist/storage/types.d.ts.map +1 -0
- package/dist/storage/types.js +7 -0
- package/dist/storage/types.js.map +1 -0
- package/dist/token-manager.d.ts +88 -0
- package/dist/token-manager.d.ts.map +1 -0
- package/dist/token-manager.js +199 -0
- package/dist/token-manager.js.map +1 -0
- package/dist/types.d.ts +158 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/package.json +88 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cloudflare D1 Storage Adapter (Placeholder)
|
|
3
|
+
*
|
|
4
|
+
* D1 provides stronger consistency guarantees than KV and allows
|
|
5
|
+
* for more complex queries (e.g., find all tokens expiring soon).
|
|
6
|
+
*
|
|
7
|
+
* TODO: Implement full D1 adapter with migrations
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* D1 Storage adapter for Cloudflare D1
|
|
11
|
+
*
|
|
12
|
+
* NOT YET IMPLEMENTED - Use KVStorage for now
|
|
13
|
+
*/
|
|
14
|
+
export class D1Storage {
|
|
15
|
+
constructor(_db, _encryptionKey) {
|
|
16
|
+
throw new Error('D1Storage is not yet implemented. Use KVStorage instead, or contribute an implementation!');
|
|
17
|
+
}
|
|
18
|
+
get(_userId, _provider) {
|
|
19
|
+
throw new Error('Not implemented');
|
|
20
|
+
}
|
|
21
|
+
set(_token) {
|
|
22
|
+
throw new Error('Not implemented');
|
|
23
|
+
}
|
|
24
|
+
delete(_userId, _provider) {
|
|
25
|
+
throw new Error('Not implemented');
|
|
26
|
+
}
|
|
27
|
+
list(_userId) {
|
|
28
|
+
throw new Error('Not implemented');
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=d1.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"d1.js","sourceRoot":"","sources":["../../src/storage/d1.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH;;;;GAIG;AACH,MAAM,OAAO,SAAS;IACpB,YAAY,GAAe,EAAE,cAAsB;QACjD,MAAM,IAAI,KAAK,CACb,2FAA2F,CAC5F,CAAC;IACJ,CAAC;IAED,GAAG,CAAC,OAAe,EAAE,SAAiB;QACpC,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;IACrC,CAAC;IAED,GAAG,CAAC,MAAmB;QACrB,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;IACrC,CAAC;IAED,MAAM,CAAC,OAAe,EAAE,SAAiB;QACvC,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;IACrC,CAAC;IAED,IAAI,CAAC,OAAe;QAClB,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;IACrC,CAAC;CACF"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cloudflare KV Storage Adapter
|
|
3
|
+
*
|
|
4
|
+
* Key structure:
|
|
5
|
+
* - tokens:{userId}:{provider} → encrypted token data
|
|
6
|
+
* - token-index:{userId} → JSON array of provider IDs (for listing)
|
|
7
|
+
*
|
|
8
|
+
* Note: KV is eventually consistent. For strict consistency needs, use D1 adapter.
|
|
9
|
+
*/
|
|
10
|
+
import type { TokenStorage, StoredToken, ConnectedProvider } from '../types';
|
|
11
|
+
export interface KVStorageOptions {
|
|
12
|
+
/** Cloudflare KV Namespace binding */
|
|
13
|
+
namespace: KVNamespace;
|
|
14
|
+
/** Encryption key for token data */
|
|
15
|
+
encryptionKey: string;
|
|
16
|
+
/** Optional key prefix (default: 'tokens') */
|
|
17
|
+
keyPrefix?: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* KV Storage adapter for Cloudflare Workers KV
|
|
21
|
+
*/
|
|
22
|
+
export declare class KVStorage implements TokenStorage {
|
|
23
|
+
private readonly kv;
|
|
24
|
+
private readonly encryptionKey;
|
|
25
|
+
private readonly keyPrefix;
|
|
26
|
+
constructor(options: KVStorageOptions);
|
|
27
|
+
private tokenKey;
|
|
28
|
+
private indexKey;
|
|
29
|
+
get(userId: string, provider: string): Promise<StoredToken | null>;
|
|
30
|
+
set(token: StoredToken): Promise<void>;
|
|
31
|
+
delete(userId: string, provider: string): Promise<void>;
|
|
32
|
+
list(userId: string): Promise<ConnectedProvider[]>;
|
|
33
|
+
/**
|
|
34
|
+
* Update the provider index for a user
|
|
35
|
+
*/
|
|
36
|
+
private updateIndex;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=kv.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kv.d.ts","sourceRoot":"","sources":["../../src/storage/kv.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EACV,YAAY,EACZ,WAAW,EACX,iBAAiB,EAClB,MAAM,UAAU,CAAC;AAyBlB,MAAM,WAAW,gBAAgB;IAC/B,sCAAsC;IACtC,SAAS,EAAE,WAAW,CAAC;IACvB,oCAAoC;IACpC,aAAa,EAAE,MAAM,CAAC;IACtB,8CAA8C;IAC9C,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,qBAAa,SAAU,YAAW,YAAY;IAC5C,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAc;IACjC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;gBAEvB,OAAO,EAAE,gBAAgB;IAMrC,OAAO,CAAC,QAAQ;IAIhB,OAAO,CAAC,QAAQ;IAIV,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAyBlE,GAAG,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAkCtC,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAUvD,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC;IA8BxD;;OAEG;YACW,WAAW;CAyB1B"}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cloudflare KV Storage Adapter
|
|
3
|
+
*
|
|
4
|
+
* Key structure:
|
|
5
|
+
* - tokens:{userId}:{provider} → encrypted token data
|
|
6
|
+
* - token-index:{userId} → JSON array of provider IDs (for listing)
|
|
7
|
+
*
|
|
8
|
+
* Note: KV is eventually consistent. For strict consistency needs, use D1 adapter.
|
|
9
|
+
*/
|
|
10
|
+
import { StorageError } from '../errors';
|
|
11
|
+
import { encrypt, decrypt } from '../crypto';
|
|
12
|
+
const KEY_PREFIX = 'tokens';
|
|
13
|
+
const INDEX_PREFIX = 'token-index';
|
|
14
|
+
/**
|
|
15
|
+
* KV Storage adapter for Cloudflare Workers KV
|
|
16
|
+
*/
|
|
17
|
+
export class KVStorage {
|
|
18
|
+
kv;
|
|
19
|
+
encryptionKey;
|
|
20
|
+
keyPrefix;
|
|
21
|
+
constructor(options) {
|
|
22
|
+
this.kv = options.namespace;
|
|
23
|
+
this.encryptionKey = options.encryptionKey;
|
|
24
|
+
this.keyPrefix = options.keyPrefix ?? KEY_PREFIX;
|
|
25
|
+
}
|
|
26
|
+
tokenKey(userId, provider) {
|
|
27
|
+
return `${this.keyPrefix}:${userId}:${provider}`;
|
|
28
|
+
}
|
|
29
|
+
indexKey(userId) {
|
|
30
|
+
return `${INDEX_PREFIX}:${userId}`;
|
|
31
|
+
}
|
|
32
|
+
async get(userId, provider) {
|
|
33
|
+
try {
|
|
34
|
+
const key = this.tokenKey(userId, provider);
|
|
35
|
+
const data = await this.kv.get(key, 'json');
|
|
36
|
+
if (!data) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
// Decrypt sensitive fields
|
|
40
|
+
const accessToken = await decrypt(data.accessToken, this.encryptionKey);
|
|
41
|
+
const refreshToken = data.refreshToken
|
|
42
|
+
? await decrypt(data.refreshToken, this.encryptionKey)
|
|
43
|
+
: undefined;
|
|
44
|
+
return {
|
|
45
|
+
...data,
|
|
46
|
+
accessToken,
|
|
47
|
+
refreshToken,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
throw new StorageError('get', error instanceof Error ? error : undefined);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
async set(token) {
|
|
55
|
+
try {
|
|
56
|
+
const key = this.tokenKey(token.userId, token.provider);
|
|
57
|
+
// Encrypt sensitive fields
|
|
58
|
+
const encryptedAccessToken = await encrypt(token.accessToken, this.encryptionKey);
|
|
59
|
+
const encryptedRefreshToken = token.refreshToken
|
|
60
|
+
? await encrypt(token.refreshToken, this.encryptionKey)
|
|
61
|
+
: undefined;
|
|
62
|
+
const data = {
|
|
63
|
+
userId: token.userId,
|
|
64
|
+
provider: token.provider,
|
|
65
|
+
accessToken: encryptedAccessToken,
|
|
66
|
+
refreshToken: encryptedRefreshToken,
|
|
67
|
+
expiresAt: token.expiresAt,
|
|
68
|
+
scopes: token.scopes,
|
|
69
|
+
createdAt: token.createdAt,
|
|
70
|
+
updatedAt: token.updatedAt,
|
|
71
|
+
};
|
|
72
|
+
// Store the token
|
|
73
|
+
await this.kv.put(key, JSON.stringify(data));
|
|
74
|
+
// Update the index
|
|
75
|
+
await this.updateIndex(token.userId, token.provider, 'add');
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
throw new StorageError('set', error instanceof Error ? error : undefined);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
async delete(userId, provider) {
|
|
82
|
+
try {
|
|
83
|
+
const key = this.tokenKey(userId, provider);
|
|
84
|
+
await this.kv.delete(key);
|
|
85
|
+
await this.updateIndex(userId, provider, 'remove');
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
throw new StorageError('delete', error instanceof Error ? error : undefined);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
async list(userId) {
|
|
92
|
+
try {
|
|
93
|
+
const indexKey = this.indexKey(userId);
|
|
94
|
+
const providers = await this.kv.get(indexKey, 'json');
|
|
95
|
+
if (!providers || providers.length === 0) {
|
|
96
|
+
return [];
|
|
97
|
+
}
|
|
98
|
+
// Fetch each provider's token data
|
|
99
|
+
const results = [];
|
|
100
|
+
for (const provider of providers) {
|
|
101
|
+
const token = await this.get(userId, provider);
|
|
102
|
+
if (token) {
|
|
103
|
+
results.push({
|
|
104
|
+
provider: token.provider,
|
|
105
|
+
scopes: token.scopes,
|
|
106
|
+
connectedAt: token.createdAt,
|
|
107
|
+
expiresAt: token.expiresAt,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return results;
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
throw new StorageError('list', error instanceof Error ? error : undefined);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Update the provider index for a user
|
|
119
|
+
*/
|
|
120
|
+
async updateIndex(userId, provider, action) {
|
|
121
|
+
const indexKey = this.indexKey(userId);
|
|
122
|
+
const current = (await this.kv.get(indexKey, 'json')) ?? [];
|
|
123
|
+
let updated;
|
|
124
|
+
if (action === 'add') {
|
|
125
|
+
if (!current.includes(provider)) {
|
|
126
|
+
updated = [...current, provider];
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
return; // Already in index
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
updated = current.filter((p) => p !== provider);
|
|
134
|
+
}
|
|
135
|
+
if (updated.length === 0) {
|
|
136
|
+
await this.kv.delete(indexKey);
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
await this.kv.put(indexKey, JSON.stringify(updated));
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=kv.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kv.js","sourceRoot":"","sources":["../../src/storage/kv.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/storage/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/storage/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth Token Manager
|
|
3
|
+
*
|
|
4
|
+
* Main entry point for storing, retrieving, and refreshing OAuth tokens
|
|
5
|
+
* for downstream API access in Cloudflare Workers.
|
|
6
|
+
*/
|
|
7
|
+
import type { TokenManagerConfig, TokenProvider, TokenData, StoreTokenOptions, GetTokenOptions, ListTokensOptions, ConnectedProvider, RevokeTokenOptions } from './types';
|
|
8
|
+
/**
|
|
9
|
+
* OAuth Token Manager
|
|
10
|
+
*
|
|
11
|
+
* Manages OAuth tokens for downstream API access:
|
|
12
|
+
* - Stores tokens encrypted at rest
|
|
13
|
+
* - Automatically refreshes expired tokens
|
|
14
|
+
* - Validates required scopes
|
|
15
|
+
* - Supports Google, Microsoft, and GitHub out of the box
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* const tokens = new TokenManager({
|
|
20
|
+
* storage: new KVStorage({ namespace: env.TOKEN_KV, encryptionKey: env.TOKEN_KEY }),
|
|
21
|
+
* encryptionKey: env.TOKEN_KEY,
|
|
22
|
+
* providers: {
|
|
23
|
+
* google: {
|
|
24
|
+
* clientId: env.GOOGLE_CLIENT_ID,
|
|
25
|
+
* clientSecret: env.GOOGLE_CLIENT_SECRET,
|
|
26
|
+
* },
|
|
27
|
+
* },
|
|
28
|
+
* });
|
|
29
|
+
*
|
|
30
|
+
* // Store token after OAuth callback
|
|
31
|
+
* await tokens.store({ userId, provider: 'google', accessToken, refreshToken, expiresAt, scopes });
|
|
32
|
+
*
|
|
33
|
+
* // Get valid token (auto-refreshes if needed)
|
|
34
|
+
* const { accessToken } = await tokens.get({ userId, provider: 'google' });
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export declare class TokenManager {
|
|
38
|
+
private readonly storage;
|
|
39
|
+
private readonly providers;
|
|
40
|
+
private readonly defaultRefreshBuffer;
|
|
41
|
+
constructor(config: TokenManagerConfig);
|
|
42
|
+
/**
|
|
43
|
+
* Store a new token or update an existing one
|
|
44
|
+
*
|
|
45
|
+
* Call this after a successful OAuth callback to store the user's tokens.
|
|
46
|
+
*/
|
|
47
|
+
store(options: StoreTokenOptions): Promise<void>;
|
|
48
|
+
/**
|
|
49
|
+
* Get a valid access token for API calls
|
|
50
|
+
*
|
|
51
|
+
* - Returns the current token if still valid
|
|
52
|
+
* - Automatically refreshes if expired or expiring soon
|
|
53
|
+
* - Validates required scopes if specified
|
|
54
|
+
*
|
|
55
|
+
* @throws TokenNotFoundError - User hasn't connected this provider
|
|
56
|
+
* @throws TokenExpiredError - Token expired and refresh failed
|
|
57
|
+
* @throws InsufficientScopesError - Token missing required scopes
|
|
58
|
+
* @throws ProviderNotConfiguredError - Provider not in config (for refresh)
|
|
59
|
+
*/
|
|
60
|
+
get(options: GetTokenOptions): Promise<TokenData>;
|
|
61
|
+
/**
|
|
62
|
+
* List all connected providers for a user
|
|
63
|
+
*/
|
|
64
|
+
list(options: ListTokensOptions): Promise<ConnectedProvider[]>;
|
|
65
|
+
/**
|
|
66
|
+
* Revoke/delete a token
|
|
67
|
+
*
|
|
68
|
+
* Note: This only removes the token from storage. For providers that
|
|
69
|
+
* support token revocation (e.g., GitHub), you may want to also call
|
|
70
|
+
* the provider's revocation endpoint.
|
|
71
|
+
*/
|
|
72
|
+
revoke(options: RevokeTokenOptions): Promise<void>;
|
|
73
|
+
/**
|
|
74
|
+
* Check if a user has a token for a provider (without retrieving it)
|
|
75
|
+
*/
|
|
76
|
+
has(userId: string, provider: string): Promise<boolean>;
|
|
77
|
+
/**
|
|
78
|
+
* Refresh an expired token
|
|
79
|
+
*/
|
|
80
|
+
private refreshToken;
|
|
81
|
+
/**
|
|
82
|
+
* Register a custom provider implementation
|
|
83
|
+
*
|
|
84
|
+
* Use this to add support for providers beyond Google/Microsoft/GitHub
|
|
85
|
+
*/
|
|
86
|
+
static registerProvider(provider: TokenProvider): void;
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=token-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-manager.d.ts","sourceRoot":"","sources":["../src/token-manager.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EACV,kBAAkB,EAElB,aAAa,EAGb,SAAS,EACT,iBAAiB,EACjB,eAAe,EACf,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,EACnB,MAAM,SAAS,CAAC;AAyBjB;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAe;IACvC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA8B;IACxD,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAS;gBAElC,MAAM,EAAE,kBAAkB;IAMtC;;;;OAIG;IACG,KAAK,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAoBtD;;;;;;;;;;;OAWG;IACG,GAAG,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,SAAS,CAAC;IA0CvD;;OAEG;IACG,IAAI,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC;IAIpE;;;;;;OAMG;IACG,MAAM,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;IAIxD;;OAEG;IACG,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAK7D;;OAEG;YACW,YAAY;IAuD1B;;;;OAIG;IACH,MAAM,CAAC,gBAAgB,CAAC,QAAQ,EAAE,aAAa,GAAG,IAAI;CAGvD"}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth Token Manager
|
|
3
|
+
*
|
|
4
|
+
* Main entry point for storing, retrieving, and refreshing OAuth tokens
|
|
5
|
+
* for downstream API access in Cloudflare Workers.
|
|
6
|
+
*/
|
|
7
|
+
import { TokenNotFoundError, TokenExpiredError, InsufficientScopesError, ProviderNotConfiguredError, } from './errors';
|
|
8
|
+
import { GoogleProvider } from './providers/google';
|
|
9
|
+
import { MicrosoftProvider } from './providers/microsoft';
|
|
10
|
+
import { GitHubProvider } from './providers/github';
|
|
11
|
+
// Default refresh buffer: 5 minutes before expiry
|
|
12
|
+
const DEFAULT_REFRESH_BUFFER_MS = 5 * 60 * 1000;
|
|
13
|
+
/**
|
|
14
|
+
* Built-in provider instances
|
|
15
|
+
*/
|
|
16
|
+
const builtInProviders = {
|
|
17
|
+
google: new GoogleProvider(),
|
|
18
|
+
microsoft: new MicrosoftProvider(),
|
|
19
|
+
github: new GitHubProvider(),
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* OAuth Token Manager
|
|
23
|
+
*
|
|
24
|
+
* Manages OAuth tokens for downstream API access:
|
|
25
|
+
* - Stores tokens encrypted at rest
|
|
26
|
+
* - Automatically refreshes expired tokens
|
|
27
|
+
* - Validates required scopes
|
|
28
|
+
* - Supports Google, Microsoft, and GitHub out of the box
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```typescript
|
|
32
|
+
* const tokens = new TokenManager({
|
|
33
|
+
* storage: new KVStorage({ namespace: env.TOKEN_KV, encryptionKey: env.TOKEN_KEY }),
|
|
34
|
+
* encryptionKey: env.TOKEN_KEY,
|
|
35
|
+
* providers: {
|
|
36
|
+
* google: {
|
|
37
|
+
* clientId: env.GOOGLE_CLIENT_ID,
|
|
38
|
+
* clientSecret: env.GOOGLE_CLIENT_SECRET,
|
|
39
|
+
* },
|
|
40
|
+
* },
|
|
41
|
+
* });
|
|
42
|
+
*
|
|
43
|
+
* // Store token after OAuth callback
|
|
44
|
+
* await tokens.store({ userId, provider: 'google', accessToken, refreshToken, expiresAt, scopes });
|
|
45
|
+
*
|
|
46
|
+
* // Get valid token (auto-refreshes if needed)
|
|
47
|
+
* const { accessToken } = await tokens.get({ userId, provider: 'google' });
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
export class TokenManager {
|
|
51
|
+
storage;
|
|
52
|
+
providers;
|
|
53
|
+
defaultRefreshBuffer;
|
|
54
|
+
constructor(config) {
|
|
55
|
+
this.storage = config.storage;
|
|
56
|
+
this.providers = new Map(Object.entries(config.providers).filter(([, v]) => v !== undefined));
|
|
57
|
+
this.defaultRefreshBuffer = config.defaultRefreshBuffer ?? DEFAULT_REFRESH_BUFFER_MS;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Store a new token or update an existing one
|
|
61
|
+
*
|
|
62
|
+
* Call this after a successful OAuth callback to store the user's tokens.
|
|
63
|
+
*/
|
|
64
|
+
async store(options) {
|
|
65
|
+
const now = Date.now();
|
|
66
|
+
// Check if token already exists (update vs create)
|
|
67
|
+
const existing = await this.storage.get(options.userId, options.provider);
|
|
68
|
+
const token = {
|
|
69
|
+
userId: options.userId,
|
|
70
|
+
provider: options.provider,
|
|
71
|
+
accessToken: options.accessToken,
|
|
72
|
+
refreshToken: options.refreshToken,
|
|
73
|
+
expiresAt: options.expiresAt,
|
|
74
|
+
scopes: options.scopes,
|
|
75
|
+
createdAt: existing?.createdAt ?? now,
|
|
76
|
+
updatedAt: now,
|
|
77
|
+
};
|
|
78
|
+
await this.storage.set(token);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Get a valid access token for API calls
|
|
82
|
+
*
|
|
83
|
+
* - Returns the current token if still valid
|
|
84
|
+
* - Automatically refreshes if expired or expiring soon
|
|
85
|
+
* - Validates required scopes if specified
|
|
86
|
+
*
|
|
87
|
+
* @throws TokenNotFoundError - User hasn't connected this provider
|
|
88
|
+
* @throws TokenExpiredError - Token expired and refresh failed
|
|
89
|
+
* @throws InsufficientScopesError - Token missing required scopes
|
|
90
|
+
* @throws ProviderNotConfiguredError - Provider not in config (for refresh)
|
|
91
|
+
*/
|
|
92
|
+
async get(options) {
|
|
93
|
+
const { userId, provider, requiredScopes, refreshBuffer } = options;
|
|
94
|
+
// Fetch stored token
|
|
95
|
+
const stored = await this.storage.get(userId, provider);
|
|
96
|
+
if (!stored) {
|
|
97
|
+
throw new TokenNotFoundError(userId, provider);
|
|
98
|
+
}
|
|
99
|
+
// Check required scopes
|
|
100
|
+
if (requiredScopes && requiredScopes.length > 0) {
|
|
101
|
+
const hasAllScopes = requiredScopes.every((scope) => stored.scopes.includes(scope));
|
|
102
|
+
if (!hasAllScopes) {
|
|
103
|
+
throw new InsufficientScopesError(userId, provider, requiredScopes, stored.scopes);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
// Check if token needs refresh
|
|
107
|
+
const bufferMs = refreshBuffer ?? this.defaultRefreshBuffer;
|
|
108
|
+
const needsRefresh = stored.expiresAt && Date.now() + bufferMs >= stored.expiresAt;
|
|
109
|
+
if (needsRefresh) {
|
|
110
|
+
return await this.refreshToken(stored);
|
|
111
|
+
}
|
|
112
|
+
return {
|
|
113
|
+
accessToken: stored.accessToken,
|
|
114
|
+
refreshToken: stored.refreshToken,
|
|
115
|
+
expiresAt: stored.expiresAt,
|
|
116
|
+
scopes: stored.scopes,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* List all connected providers for a user
|
|
121
|
+
*/
|
|
122
|
+
async list(options) {
|
|
123
|
+
return this.storage.list(options.userId);
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Revoke/delete a token
|
|
127
|
+
*
|
|
128
|
+
* Note: This only removes the token from storage. For providers that
|
|
129
|
+
* support token revocation (e.g., GitHub), you may want to also call
|
|
130
|
+
* the provider's revocation endpoint.
|
|
131
|
+
*/
|
|
132
|
+
async revoke(options) {
|
|
133
|
+
await this.storage.delete(options.userId, options.provider);
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Check if a user has a token for a provider (without retrieving it)
|
|
137
|
+
*/
|
|
138
|
+
async has(userId, provider) {
|
|
139
|
+
const token = await this.storage.get(userId, provider);
|
|
140
|
+
return token !== null;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Refresh an expired token
|
|
144
|
+
*/
|
|
145
|
+
async refreshToken(stored) {
|
|
146
|
+
const { userId, provider, refreshToken } = stored;
|
|
147
|
+
// Check for refresh token
|
|
148
|
+
if (!refreshToken) {
|
|
149
|
+
throw new TokenExpiredError(userId, provider, 'no_refresh_token');
|
|
150
|
+
}
|
|
151
|
+
// Get provider config
|
|
152
|
+
const providerConfig = this.providers.get(provider);
|
|
153
|
+
if (!providerConfig) {
|
|
154
|
+
throw new ProviderNotConfiguredError(provider);
|
|
155
|
+
}
|
|
156
|
+
// Get provider implementation
|
|
157
|
+
const providerImpl = builtInProviders[provider];
|
|
158
|
+
if (!providerImpl) {
|
|
159
|
+
// For custom providers without built-in implementation,
|
|
160
|
+
// we can't refresh - user needs to re-authenticate
|
|
161
|
+
throw new TokenExpiredError(userId, provider, 'refresh_failed');
|
|
162
|
+
}
|
|
163
|
+
// Check if provider supports refresh
|
|
164
|
+
if (!providerImpl.supportsRefresh) {
|
|
165
|
+
// Provider tokens don't expire (e.g., GitHub)
|
|
166
|
+
// If we got here, the token must be invalid
|
|
167
|
+
throw new TokenExpiredError(userId, provider, 'refresh_failed');
|
|
168
|
+
}
|
|
169
|
+
// Attempt refresh
|
|
170
|
+
const refreshed = await providerImpl.refresh(refreshToken, providerConfig);
|
|
171
|
+
if (!refreshed) {
|
|
172
|
+
throw new TokenExpiredError(userId, provider, 'refresh_failed');
|
|
173
|
+
}
|
|
174
|
+
// Update stored token with new values
|
|
175
|
+
const updatedToken = {
|
|
176
|
+
...stored,
|
|
177
|
+
accessToken: refreshed.accessToken,
|
|
178
|
+
refreshToken: refreshed.refreshToken ?? stored.refreshToken,
|
|
179
|
+
expiresAt: refreshed.expiresAt,
|
|
180
|
+
updatedAt: Date.now(),
|
|
181
|
+
};
|
|
182
|
+
await this.storage.set(updatedToken);
|
|
183
|
+
return {
|
|
184
|
+
accessToken: updatedToken.accessToken,
|
|
185
|
+
refreshToken: updatedToken.refreshToken,
|
|
186
|
+
expiresAt: updatedToken.expiresAt,
|
|
187
|
+
scopes: updatedToken.scopes,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Register a custom provider implementation
|
|
192
|
+
*
|
|
193
|
+
* Use this to add support for providers beyond Google/Microsoft/GitHub
|
|
194
|
+
*/
|
|
195
|
+
static registerProvider(provider) {
|
|
196
|
+
builtInProviders[provider.id] = provider;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
//# sourceMappingURL=token-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-manager.js","sourceRoot":"","sources":["../src/token-manager.ts"],"names":[],"mappings":"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"}
|