@dainprotocol/oauth2-token-manager 0.2.5 → 0.3.1
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 +68 -0
- package/dist/index.cjs +2 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +95 -1
- package/dist/index.d.ts +95 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -90,6 +90,77 @@ declare abstract class BaseProfileFetcher {
|
|
|
90
90
|
getEndpoint(): string;
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
+
/**
|
|
94
|
+
* Fields that will be encrypted in storage
|
|
95
|
+
*/
|
|
96
|
+
interface EncryptedTokenFields {
|
|
97
|
+
accessToken: string;
|
|
98
|
+
refreshToken?: string;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Configuration for token encryption
|
|
102
|
+
*/
|
|
103
|
+
interface TokenEncryptionConfig {
|
|
104
|
+
/**
|
|
105
|
+
* The encryption key (minimum 32 characters)
|
|
106
|
+
* This should be stored securely (e.g., environment variable, secrets manager)
|
|
107
|
+
*/
|
|
108
|
+
encryptionKey: string;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Token encryption utility for encrypting sensitive OAuth2 token data at rest.
|
|
112
|
+
* Uses AES-256-CBC encryption via iron-session with PBKDF2 key derivation.
|
|
113
|
+
*
|
|
114
|
+
* @example
|
|
115
|
+
* ```typescript
|
|
116
|
+
* const encryption = new TokenEncryption({
|
|
117
|
+
* encryptionKey: process.env.TOKEN_ENCRYPTION_KEY!
|
|
118
|
+
* });
|
|
119
|
+
*
|
|
120
|
+
* // Encrypt tokens before storage
|
|
121
|
+
* const encrypted = await encryption.encryptTokenFields({
|
|
122
|
+
* accessToken: 'ya29.xxx',
|
|
123
|
+
* refreshToken: '1//xxx'
|
|
124
|
+
* });
|
|
125
|
+
*
|
|
126
|
+
* // Decrypt tokens after retrieval
|
|
127
|
+
* const decrypted = await encryption.decryptTokenFields(encrypted);
|
|
128
|
+
* ```
|
|
129
|
+
*/
|
|
130
|
+
declare class TokenEncryption {
|
|
131
|
+
private readonly encryptionKey;
|
|
132
|
+
constructor(config: TokenEncryptionConfig);
|
|
133
|
+
/**
|
|
134
|
+
* Encrypt token fields (accessToken and refreshToken)
|
|
135
|
+
* Returns the encrypted values as strings that can be stored in the database
|
|
136
|
+
*/
|
|
137
|
+
encryptTokenFields(fields: EncryptedTokenFields): Promise<{
|
|
138
|
+
accessToken: string;
|
|
139
|
+
refreshToken?: string;
|
|
140
|
+
}>;
|
|
141
|
+
/**
|
|
142
|
+
* Decrypt token fields (accessToken and refreshToken)
|
|
143
|
+
* Returns the original plaintext values
|
|
144
|
+
*/
|
|
145
|
+
decryptTokenFields(fields: {
|
|
146
|
+
accessToken: string;
|
|
147
|
+
refreshToken?: string;
|
|
148
|
+
}): Promise<EncryptedTokenFields>;
|
|
149
|
+
/**
|
|
150
|
+
* Check if a string appears to be encrypted (starts with iron-session prefix)
|
|
151
|
+
*/
|
|
152
|
+
isEncrypted(value: string): boolean;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Options for storage adapters that support encryption
|
|
157
|
+
*/
|
|
158
|
+
interface EncryptionOptions {
|
|
159
|
+
/**
|
|
160
|
+
* Token encryption instance for encrypting/decrypting tokens at rest
|
|
161
|
+
*/
|
|
162
|
+
encryption?: TokenEncryption;
|
|
163
|
+
}
|
|
93
164
|
/**
|
|
94
165
|
* Simplified storage adapter interface
|
|
95
166
|
* Only stores tokens with provider, userId, and email as key fields
|
|
@@ -288,13 +359,36 @@ declare class OAuth2Client {
|
|
|
288
359
|
}>>;
|
|
289
360
|
}
|
|
290
361
|
|
|
362
|
+
type InMemoryStorageAdapterOptions = EncryptionOptions;
|
|
291
363
|
declare class InMemoryStorageAdapter implements StorageAdapter {
|
|
292
364
|
private tokens;
|
|
293
365
|
private states;
|
|
294
366
|
private profileFetchers;
|
|
367
|
+
private encryption?;
|
|
368
|
+
constructor(options?: InMemoryStorageAdapterOptions);
|
|
295
369
|
private generateId;
|
|
370
|
+
/**
|
|
371
|
+
* Encrypt token fields if encryption is enabled
|
|
372
|
+
*/
|
|
373
|
+
private encryptToken;
|
|
374
|
+
/**
|
|
375
|
+
* Decrypt token fields if encryption is enabled
|
|
376
|
+
*/
|
|
377
|
+
private decryptToken;
|
|
378
|
+
/**
|
|
379
|
+
* Decrypt a stored token before returning it
|
|
380
|
+
*/
|
|
381
|
+
private decryptStoredToken;
|
|
296
382
|
saveToken(input: SaveTokenInput): Promise<StoredToken>;
|
|
383
|
+
/**
|
|
384
|
+
* Internal query that doesn't decrypt tokens (for existence checks)
|
|
385
|
+
*/
|
|
386
|
+
private queryTokensInternal;
|
|
297
387
|
queryTokens(query: TokenQuery): Promise<StoredToken[]>;
|
|
388
|
+
/**
|
|
389
|
+
* Internal get that doesn't decrypt (for existence checks)
|
|
390
|
+
*/
|
|
391
|
+
private getTokenInternal;
|
|
298
392
|
getToken(provider: string, email: string, tenant?: string): Promise<StoredToken | null>;
|
|
299
393
|
getTokenById(id: string): Promise<StoredToken | null>;
|
|
300
394
|
getTokensByUserId(userId: string): Promise<StoredToken[]>;
|
|
@@ -433,4 +527,4 @@ declare const generateState: () => string;
|
|
|
433
527
|
declare const seal: <T>(d: T, key: string) => Promise<string>;
|
|
434
528
|
declare const unseal: <T>(s: string, key: string) => Promise<T>;
|
|
435
529
|
|
|
436
|
-
export { type AuthorizationOptions, type AuthorizationState, type AuthorizationUrlStrategy, type AutoRefreshOptions, BaseProfileFetcher, type CallbackResult, GenericOAuth2Provider, GenericProfileFetcher, GitHubProfileFetcher, GoogleProfileFetcher, InMemoryStorageAdapter, MicrosoftProfileFetcher, OAuth2Client, type OAuth2Config, type OAuth2Options, OAuth2Provider, type OAuth2Token, type ProfileBasedTokenOptions, type ProfileFetcher, ProfileFetcherFactory, type ProfileFetcherOptions, type ProfileMapping, type ProviderFactory, type ProviderType, type SaveTokenInput, StandardAuthorizationUrlStrategy, StandardTokenExchangeStrategy, type StorageAdapter, type StoredToken, type TokenExchangeStrategy, type TokenOptions, type TokenQuery, type UpdateTokenInput, type UserProfile, createCodeChallenge, createCodeVerifier, generateState, seal, unseal };
|
|
530
|
+
export { type AuthorizationOptions, type AuthorizationState, type AuthorizationUrlStrategy, type AutoRefreshOptions, BaseProfileFetcher, type CallbackResult, type EncryptedTokenFields, GenericOAuth2Provider, GenericProfileFetcher, GitHubProfileFetcher, GoogleProfileFetcher, InMemoryStorageAdapter, MicrosoftProfileFetcher, OAuth2Client, type OAuth2Config, type OAuth2Options, OAuth2Provider, type OAuth2Token, type ProfileBasedTokenOptions, type ProfileFetcher, ProfileFetcherFactory, type ProfileFetcherOptions, type ProfileMapping, type ProviderFactory, type ProviderType, type SaveTokenInput, StandardAuthorizationUrlStrategy, StandardTokenExchangeStrategy, type StorageAdapter, type StoredToken, TokenEncryption, type TokenEncryptionConfig, type TokenExchangeStrategy, type TokenOptions, type TokenQuery, type UpdateTokenInput, type UserProfile, createCodeChallenge, createCodeVerifier, generateState, seal, unseal };
|
package/dist/index.d.ts
CHANGED
|
@@ -90,6 +90,77 @@ declare abstract class BaseProfileFetcher {
|
|
|
90
90
|
getEndpoint(): string;
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
+
/**
|
|
94
|
+
* Fields that will be encrypted in storage
|
|
95
|
+
*/
|
|
96
|
+
interface EncryptedTokenFields {
|
|
97
|
+
accessToken: string;
|
|
98
|
+
refreshToken?: string;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Configuration for token encryption
|
|
102
|
+
*/
|
|
103
|
+
interface TokenEncryptionConfig {
|
|
104
|
+
/**
|
|
105
|
+
* The encryption key (minimum 32 characters)
|
|
106
|
+
* This should be stored securely (e.g., environment variable, secrets manager)
|
|
107
|
+
*/
|
|
108
|
+
encryptionKey: string;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Token encryption utility for encrypting sensitive OAuth2 token data at rest.
|
|
112
|
+
* Uses AES-256-CBC encryption via iron-session with PBKDF2 key derivation.
|
|
113
|
+
*
|
|
114
|
+
* @example
|
|
115
|
+
* ```typescript
|
|
116
|
+
* const encryption = new TokenEncryption({
|
|
117
|
+
* encryptionKey: process.env.TOKEN_ENCRYPTION_KEY!
|
|
118
|
+
* });
|
|
119
|
+
*
|
|
120
|
+
* // Encrypt tokens before storage
|
|
121
|
+
* const encrypted = await encryption.encryptTokenFields({
|
|
122
|
+
* accessToken: 'ya29.xxx',
|
|
123
|
+
* refreshToken: '1//xxx'
|
|
124
|
+
* });
|
|
125
|
+
*
|
|
126
|
+
* // Decrypt tokens after retrieval
|
|
127
|
+
* const decrypted = await encryption.decryptTokenFields(encrypted);
|
|
128
|
+
* ```
|
|
129
|
+
*/
|
|
130
|
+
declare class TokenEncryption {
|
|
131
|
+
private readonly encryptionKey;
|
|
132
|
+
constructor(config: TokenEncryptionConfig);
|
|
133
|
+
/**
|
|
134
|
+
* Encrypt token fields (accessToken and refreshToken)
|
|
135
|
+
* Returns the encrypted values as strings that can be stored in the database
|
|
136
|
+
*/
|
|
137
|
+
encryptTokenFields(fields: EncryptedTokenFields): Promise<{
|
|
138
|
+
accessToken: string;
|
|
139
|
+
refreshToken?: string;
|
|
140
|
+
}>;
|
|
141
|
+
/**
|
|
142
|
+
* Decrypt token fields (accessToken and refreshToken)
|
|
143
|
+
* Returns the original plaintext values
|
|
144
|
+
*/
|
|
145
|
+
decryptTokenFields(fields: {
|
|
146
|
+
accessToken: string;
|
|
147
|
+
refreshToken?: string;
|
|
148
|
+
}): Promise<EncryptedTokenFields>;
|
|
149
|
+
/**
|
|
150
|
+
* Check if a string appears to be encrypted (starts with iron-session prefix)
|
|
151
|
+
*/
|
|
152
|
+
isEncrypted(value: string): boolean;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Options for storage adapters that support encryption
|
|
157
|
+
*/
|
|
158
|
+
interface EncryptionOptions {
|
|
159
|
+
/**
|
|
160
|
+
* Token encryption instance for encrypting/decrypting tokens at rest
|
|
161
|
+
*/
|
|
162
|
+
encryption?: TokenEncryption;
|
|
163
|
+
}
|
|
93
164
|
/**
|
|
94
165
|
* Simplified storage adapter interface
|
|
95
166
|
* Only stores tokens with provider, userId, and email as key fields
|
|
@@ -288,13 +359,36 @@ declare class OAuth2Client {
|
|
|
288
359
|
}>>;
|
|
289
360
|
}
|
|
290
361
|
|
|
362
|
+
type InMemoryStorageAdapterOptions = EncryptionOptions;
|
|
291
363
|
declare class InMemoryStorageAdapter implements StorageAdapter {
|
|
292
364
|
private tokens;
|
|
293
365
|
private states;
|
|
294
366
|
private profileFetchers;
|
|
367
|
+
private encryption?;
|
|
368
|
+
constructor(options?: InMemoryStorageAdapterOptions);
|
|
295
369
|
private generateId;
|
|
370
|
+
/**
|
|
371
|
+
* Encrypt token fields if encryption is enabled
|
|
372
|
+
*/
|
|
373
|
+
private encryptToken;
|
|
374
|
+
/**
|
|
375
|
+
* Decrypt token fields if encryption is enabled
|
|
376
|
+
*/
|
|
377
|
+
private decryptToken;
|
|
378
|
+
/**
|
|
379
|
+
* Decrypt a stored token before returning it
|
|
380
|
+
*/
|
|
381
|
+
private decryptStoredToken;
|
|
296
382
|
saveToken(input: SaveTokenInput): Promise<StoredToken>;
|
|
383
|
+
/**
|
|
384
|
+
* Internal query that doesn't decrypt tokens (for existence checks)
|
|
385
|
+
*/
|
|
386
|
+
private queryTokensInternal;
|
|
297
387
|
queryTokens(query: TokenQuery): Promise<StoredToken[]>;
|
|
388
|
+
/**
|
|
389
|
+
* Internal get that doesn't decrypt (for existence checks)
|
|
390
|
+
*/
|
|
391
|
+
private getTokenInternal;
|
|
298
392
|
getToken(provider: string, email: string, tenant?: string): Promise<StoredToken | null>;
|
|
299
393
|
getTokenById(id: string): Promise<StoredToken | null>;
|
|
300
394
|
getTokensByUserId(userId: string): Promise<StoredToken[]>;
|
|
@@ -433,4 +527,4 @@ declare const generateState: () => string;
|
|
|
433
527
|
declare const seal: <T>(d: T, key: string) => Promise<string>;
|
|
434
528
|
declare const unseal: <T>(s: string, key: string) => Promise<T>;
|
|
435
529
|
|
|
436
|
-
export { type AuthorizationOptions, type AuthorizationState, type AuthorizationUrlStrategy, type AutoRefreshOptions, BaseProfileFetcher, type CallbackResult, GenericOAuth2Provider, GenericProfileFetcher, GitHubProfileFetcher, GoogleProfileFetcher, InMemoryStorageAdapter, MicrosoftProfileFetcher, OAuth2Client, type OAuth2Config, type OAuth2Options, OAuth2Provider, type OAuth2Token, type ProfileBasedTokenOptions, type ProfileFetcher, ProfileFetcherFactory, type ProfileFetcherOptions, type ProfileMapping, type ProviderFactory, type ProviderType, type SaveTokenInput, StandardAuthorizationUrlStrategy, StandardTokenExchangeStrategy, type StorageAdapter, type StoredToken, type TokenExchangeStrategy, type TokenOptions, type TokenQuery, type UpdateTokenInput, type UserProfile, createCodeChallenge, createCodeVerifier, generateState, seal, unseal };
|
|
530
|
+
export { type AuthorizationOptions, type AuthorizationState, type AuthorizationUrlStrategy, type AutoRefreshOptions, BaseProfileFetcher, type CallbackResult, type EncryptedTokenFields, GenericOAuth2Provider, GenericProfileFetcher, GitHubProfileFetcher, GoogleProfileFetcher, InMemoryStorageAdapter, MicrosoftProfileFetcher, OAuth2Client, type OAuth2Config, type OAuth2Options, OAuth2Provider, type OAuth2Token, type ProfileBasedTokenOptions, type ProfileFetcher, ProfileFetcherFactory, type ProfileFetcherOptions, type ProfileMapping, type ProviderFactory, type ProviderType, type SaveTokenInput, StandardAuthorizationUrlStrategy, StandardTokenExchangeStrategy, type StorageAdapter, type StoredToken, TokenEncryption, type TokenEncryptionConfig, type TokenExchangeStrategy, type TokenOptions, type TokenQuery, type UpdateTokenInput, type UserProfile, createCodeChallenge, createCodeVerifier, generateState, seal, unseal };
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import {createHash,randomBytes}from'crypto';import {sealData,unsealData}from'iron-session';var v=class{buildUrlParams(e){return Object.entries(e).filter(([,r])=>r!==void 0).map(([r,o])=>`${r}=${encodeURIComponent(o)}`).join("&")}generateAuthorizationUrl(e,t,r){let o={client_id:e.clientId,redirect_uri:e.redirectUri,response_type:"code",scope:e.scopes.join(" "),state:t};(e.usePKCE||e.pkce)&&r&&(o.code_challenge=r,o.code_challenge_method="S256");let n={...e.additionalParams,...e.extraAuthParams};return Object.assign(o,n),`${e.authorizationUrl}?${this.buildUrlParams(o)}`}};var x=class{buildUrlParams(e){return Object.entries(e).filter(([,r])=>r!==void 0).map(([r,o])=>`${r}=${encodeURIComponent(o)}`).join("&")}async exchangeCodeForToken(e,t,r){let o={grant_type:"authorization_code",code:e,redirect_uri:t.redirectUri,client_id:t.clientId};(t.usePKCE||t.pkce)&&r?o.code_verifier=r:t.clientSecret&&(o.client_secret=t.clientSecret);let n=await fetch(t.tokenUrl,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:this.buildUrlParams(o)});if(!n.ok){let c=await n.text();throw new Error(`Token exchange failed: ${n.statusText} - ${c}`)}let i=await n.json(),a=t.responseRootKey?i[t.responseRootKey]:i;return this.normalizeTokenResponse(a,i,void 0,t)}async refreshToken(e,t){let r={grant_type:"refresh_token",refresh_token:e,client_id:t.clientId};!(t.usePKCE||t.pkce)&&t.clientSecret&&(r.client_secret=t.clientSecret);let o=await fetch(t.tokenUrl,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:this.buildUrlParams(r)});if(!o.ok){let a=await o.text();throw new Error(`Token refresh failed: ${o.statusText} - ${a}`)}let n=await o.json(),i=t.responseRootKey?n[t.responseRootKey]:n;return this.normalizeTokenResponse(i,n,e,t)}normalizeTokenResponse(e,t,r,o){var p,l,f,m,g;let n=Date.now(),i=this.extractValue(e,(p=o==null?void 0:o.tokenPaths)==null?void 0:p.accessToken,["authed_user.access_token","authed_user.accessToken","access_token","accessToken","token.access_token","token.accessToken"]);if(!i)throw new Error("Token response did not include an access_token");let a=this.extractValue(e,(l=o==null?void 0:o.tokenPaths)==null?void 0:l.refreshToken,["authed_user.refresh_token","authed_user.refreshToken","refresh_token","refreshToken","token.refresh_token","token.refreshToken"])??r,c=this.extractValue(e,(f=o==null?void 0:o.tokenPaths)==null?void 0:f.expiresIn,["authed_user.expires_in","authed_user.expiresIn","expires_in","expiresIn","token.expires_in","token.expiresIn"]),y=(typeof c=="string"?parseInt(c,10):c)||3600,d=this.extractValue(e,(m=o==null?void 0:o.tokenPaths)==null?void 0:m.scope,["authed_user.scope","scope","token.scope"]),b=this.extractValue(e,(g=o==null?void 0:o.tokenPaths)==null?void 0:g.tokenType,["authed_user.token_type","authed_user.tokenType","token_type","tokenType","token.token_type","token.tokenType"])??"Bearer";return {accessToken:i,refreshToken:a,expiresAt:new Date(n+y*1e3),expiresIn:y,tokenType:b,scope:d,createdAt:n,raw:t}}extractValue(e,t,r){if(t){let o=Array.isArray(t)?t:[t];for(let n of o){let i=this.getNestedValue(e,n);if(i!==void 0)return i}}if(r)for(let o of r){let n=this.getNestedValue(e,o);if(n!==void 0)return n}}getNestedValue(e,t){let r=t.split("."),o=e;for(let n of r)if(o&&typeof o=="object"&&n in o)o=o[n];else return;return o}};var k=class{constructor(e,t,r,o){this.config=e;this.authUrlStrategy=t||this.createAuthorizationUrlStrategy(),this.tokenStrategy=r||this.createTokenExchangeStrategy(),this.profileFetcher=o;}authUrlStrategy;tokenStrategy;profileFetcher;async fetchProfile(e){if(!this.profileFetcher)throw new Error("Profile fetcher not configured for this provider");return this.profileFetcher.fetchUserInfo(e)}getProfileEndpoint(){if(!this.profileFetcher)throw new Error("Profile fetcher not configured for this provider");return this.profileFetcher.getEndpoint()}setProfileFetcher(e){this.profileFetcher=e;}hasProfileFetcher(){return !!this.profileFetcher}generateAuthorizationUrl(e,t){return this.authUrlStrategy.generateAuthorizationUrl(this.config,e,t)}async exchangeCodeForToken(e,t){return this.tokenStrategy.exchangeCodeForToken(e,this.config,t)}async refreshToken(e){return this.tokenStrategy.refreshToken(e,this.config)}};var A=class extends k{constructor(e,t,r,o){super(e,t,r,o);}createAuthorizationUrlStrategy(){return new v}createTokenExchangeStrategy(){return new x}};var u=class{constructor(e){this.profileEndpoint=e;}async fetchUserInfo(e){let t=await fetch(this.profileEndpoint,{headers:{Authorization:`Bearer ${e}`,Accept:"application/json",...this.getAdditionalHeaders()}});if(!t.ok)throw new Error(`Failed to fetch profile from ${this.profileEndpoint}: ${t.statusText}`);let r=await t.json();return this.mapToUserProfile(r)}getAdditionalHeaders(){return {}}getEndpoint(){return this.profileEndpoint}};var S=class extends u{constructor(){super("https://www.googleapis.com/oauth2/v2/userinfo");}mapToUserProfile(e){return {email:e.email,name:e.name,id:e.id,avatar:e.picture,username:e.email,raw:e}}};var w=class extends u{constructor(){super("https://api.github.com/user");}mapToUserProfile(e){var t;return {email:e.email,name:e.name||e.login,id:(t=e.id)==null?void 0:t.toString(),avatar:e.avatar_url,username:e.login,raw:e}}getAdditionalHeaders(){return {"User-Agent":"OAuth2-Token-Manager"}}};var U=class extends u{constructor(){super("https://graph.microsoft.com/v1.0/me");}mapToUserProfile(e){return {email:e.mail||e.userPrincipalName,name:e.displayName,id:e.id,avatar:void 0,username:e.userPrincipalName,raw:e}}};var h=class extends u{constructor(t,r,o){super(t);this.mapping=r;this.additionalHeaders=o;}mapToUserProfile(t){return this.mapping?{email:this.getNestedProperty(t,this.mapping.email),name:this.mapping.name?this.getNestedProperty(t,this.mapping.name):void 0,id:this.mapping.id?this.getNestedProperty(t,this.mapping.id):void 0,avatar:this.mapping.avatar?this.getNestedProperty(t,this.mapping.avatar):void 0,username:this.mapping.username?this.getNestedProperty(t,this.mapping.username):void 0,tenant:this.mapping.tenant?this.getNestedProperty(t,this.mapping.tenant):void 0,tenantName:this.mapping.tenantName?this.getNestedProperty(t,this.mapping.tenantName):void 0,raw:t}:{email:t.email||t.mail||t.emailAddress,name:t.name||t.displayName||t.full_name,id:t.id||t.sub||t.user_id,avatar:t.avatar||t.picture||t.avatar_url,username:t.username||t.login||t.preferred_username,tenant:t.tenant||t.workspace_id||t.organization_id||t.team_id,tenantName:t.tenantName||t.workspace_name||t.organization_name||t.team_name,raw:t}}getAdditionalHeaders(){return this.additionalHeaders||{}}getNestedProperty(t,r){return r.split(".").reduce((o,n)=>o==null?void 0:o[n],t)}};var O=class{static createProfileFetcher(e,t,r,o){if(o)return o;if(r!=null&&r.profileUrl)return new h(r.profileUrl,r.profileMapping,r.profileHeaders);switch(e){case "google":return new S;case "github":return new w;case "microsoft":case "outlook":return new U;case "facebook":return new h("https://graph.facebook.com/me?fields=id,name,email,picture");case "generic":default:let n=t.profileUrl||t.userInfoUrl;return n?new h(n):void 0}}static registerCustomProfileFetcher(e,t){this.customFetchers.set(e,t);}static customFetchers=new Map;static getCustomProfileFetcher(e){return this.customFetchers.get(e)}};var E=class s{static presetConfigs={google:{authorizationUrl:"https://accounts.google.com/o/oauth2/v2/auth",tokenUrl:"https://oauth2.googleapis.com/token",profileUrl:"https://www.googleapis.com/oauth2/v2/userinfo",usePKCE:true,extraAuthParams:{access_type:"offline",prompt:"consent"}},github:{authorizationUrl:"https://github.com/login/oauth/authorize",tokenUrl:"https://github.com/login/oauth/access_token",profileUrl:"https://api.github.com/user"},microsoft:{authorizationUrl:"https://login.microsoftonline.com/common/oauth2/v2.0/authorize",tokenUrl:"https://login.microsoftonline.com/common/oauth2/v2.0/token",profileUrl:"https://graph.microsoft.com/v1.0/me",usePKCE:true},outlook:{authorizationUrl:"https://login.microsoftonline.com/common/oauth2/v2.0/authorize",tokenUrl:"https://login.microsoftonline.com/common/oauth2/v2.0/token",profileUrl:"https://graph.microsoft.com/v1.0/me",usePKCE:true,extraAuthParams:{prompt:"select_account"}},facebook:{authorizationUrl:"https://www.facebook.com/v12.0/dialog/oauth",tokenUrl:"https://graph.facebook.com/v12.0/oauth/access_token",profileUrl:"https://graph.facebook.com/me?fields=id,name,email,picture"}};createProvider(e,t,r){let o=e!=="generic"?s.presetConfigs[e]||{}:{},n={...o,...t,authorizationUrl:t.authorizationUrl||o.authorizationUrl||"",tokenUrl:t.tokenUrl||o.tokenUrl||"",profileUrl:t.profileUrl||o.profileUrl,extraAuthParams:{...o.extraAuthParams||{},...t.extraAuthParams||{}}},i=O.createProfileFetcher(e,n,void 0,r);return new A(n,void 0,void 0,i)}static registerPreset(e,t){s.presetConfigs[e]=t;}static getPresetConfig(e){return s.presetConfigs[e]}};var T=class{tokens=new Map;states=new Map;profileFetchers=new Map;generateId(){return Math.random().toString(36).substring(2)+Date.now().toString(36)}async saveToken(e){let t=await this.getToken(e.provider,e.email,e.tenant);if(t){let o={...t,...e,id:t.id,createdAt:t.createdAt,updatedAt:new Date};return this.tokens.set(t.id,o),o}let r={...e,id:this.generateId(),createdAt:new Date,updatedAt:new Date};return this.tokens.set(r.id,r),r}async queryTokens(e){let t=Array.from(this.tokens.values());if(e.id&&(t=t.filter(r=>r.id===e.id)),e.provider&&(t=t.filter(r=>r.provider===e.provider)),e.userId&&(t=t.filter(r=>r.userId===e.userId)),e.email&&(t=t.filter(r=>r.email===e.email)),e.tenant!==void 0&&(t=t.filter(r=>r.tenant===e.tenant)),!e.includeExpired){let r=new Date().getTime();t=t.filter(o=>new Date(o.token.expiresAt).getTime()>=r);}return e.offset!==void 0&&(t=t.slice(e.offset)),e.limit!==void 0&&(t=t.slice(0,e.limit)),t}async getToken(e,t,r){return (await this.queryTokens({provider:e,email:t,tenant:r,includeExpired:true}))[0]||null}async getTokenById(e){return (await this.queryTokens({id:e,includeExpired:true}))[0]||null}async getTokensByUserId(e){return this.queryTokens({userId:e})}async getTokensByEmail(e){return this.queryTokens({email:e})}async getTokensByProvider(e){return this.queryTokens({provider:e})}async getAccounts(e,t){return this.queryTokens({userId:e,provider:t})}async getTokensForEmail(e,t,r){return (await this.queryTokens({userId:e,provider:t,email:r}))[0]||null}async getTokens(e,t){return this.queryTokens({userId:e,provider:t})}async updateToken(e,t){let r=this.tokens.get(e);if(!r)return null;let o={...r,...t.token&&{token:t.token},...t.metadata&&{metadata:{...r.metadata,...t.metadata}},updatedAt:new Date};return this.tokens.set(e,o),o}async deleteToken(e){return this.tokens.delete(e)}async deleteTokenByProviderEmail(e,t,r){let o=await this.getToken(e,t,r);return o?this.tokens.delete(o.id):false}async deleteExpiredTokens(){let e=new Date().getTime(),t=Array.from(this.tokens.entries()).filter(([,r])=>new Date(r.token.expiresAt).getTime()<e).map(([r])=>r);return t.forEach(r=>this.tokens.delete(r)),t.length}async saveAuthorizationState(e){let t={...e,createdAt:new Date(Date.now())};return this.states.set(e.state,t),t}async getAuthorizationState(e){return this.states.get(e)||null}async deleteAuthorizationState(e){return this.states.delete(e)}async cleanupExpiredStates(){let e=new Date().getTime(),t=10*60*1e3,r=Array.from(this.states.entries()).filter(([,o])=>e-o.createdAt.getTime()>t).map(([o])=>o);return r.forEach(o=>this.states.delete(o)),r.length}registerProfileFetcher(e,t){this.profileFetchers.set(e,t);}getProfileFetcher(e){return this.profileFetchers.get(e)}getProfileFetchers(){return new Map(this.profileFetchers)}};var z=()=>randomBytes(32).toString("base64").replace(/[^a-zA-Z0-9]/g,"").substring(0,128),_=s=>createHash("sha256").update(s).digest("base64url"),I=()=>randomBytes(16).toString("base64url");var F=class{storage;providerFactory;providers=new Map;providerConfigs=new Map;now;autoRefreshOptions;constructor(e={}){var t,r,o;this.storage=e.storage||new T,this.providerFactory=new E,this.now=Date.now,this.autoRefreshOptions={enabled:((t=e.autoRefresh)==null?void 0:t.enabled)??true,refreshBuffer:((r=e.autoRefresh)==null?void 0:r.refreshBuffer)??10,onRefreshError:(o=e.autoRefresh)==null?void 0:o.onRefreshError},e.providers&&Object.entries(e.providers).forEach(([n,i])=>{this.registerProvider(n,i);});}registerProvider(e,t){this.providerConfigs.set(e,t);let r=this.storage.getProfileFetcher(e),o=this.detectProviderType(e,t),n=this.providerFactory.createProvider(o,t,r);this.providers.set(e,n);}async authorize(e){var a;let t=this.providers.get(e.provider);if(!t)throw new Error(`Provider ${e.provider} not found`);let r=I(),o,n;if(((a=this.providerConfigs.get(e.provider))==null?void 0:a.usePKCE)||e.usePKCE){let c=z(),P=_(c);o=t.generateAuthorizationUrl(r,P),n={state:r,codeVerifier:c,config:this.providerConfigs.get(e.provider),metadata:{...e.metadata,userId:e.userId,email:e.email,provider:e.provider,tenant:e.tenant,tenantName:e.tenantName}};}else o=t.generateAuthorizationUrl(r),n={state:r,config:this.providerConfigs.get(e.provider),metadata:{...e.metadata,userId:e.userId,email:e.email,provider:e.provider,tenant:e.tenant,tenantName:e.tenantName}};return await this.storage.saveAuthorizationState(n),{url:o,state:r}}async handleCallback(e,t){var l,f,m,g,C;let r=await this.storage.getAuthorizationState(t);if(!r)throw new Error("Invalid or expired state");let o=(l=r.metadata)==null?void 0:l.provider;if(!o)throw new Error("Provider not found in authorization state");let n=this.providers.get(o);if(!n)throw new Error(`Provider ${o} not found`);let i=await n.exchangeCodeForToken(e,r.codeVerifier),a=(f=r.metadata)==null?void 0:f.userId,c=(m=r.metadata)==null?void 0:m.email,P=(g=r.metadata)==null?void 0:g.tenant,y=(C=r.metadata)==null?void 0:C.tenantName;if(!a||!c)throw new Error("User ID and email are required in authorization state");let d;if(n.hasProfileFetcher())try{d=await n.fetchProfile(i.accessToken);}catch(N){console.warn("Failed to fetch user profile:",N);}let b=await this.storage.saveToken({provider:o,userId:a,email:(d==null?void 0:d.email)||c,tenant:(d==null?void 0:d.tenant)||P,tenantName:(d==null?void 0:d.tenantName)||y,token:i,metadata:{...r.metadata,profileFetched:!!d}});await this.storage.deleteAuthorizationState(t);let p=this.providerConfigs.get(o);return p!=null&&p.onSuccess&&await p.onSuccess(a,i),{token:b,profile:d}}async getAccessToken(e,t,r={}){return (await this.getValidToken(e,t,r)).accessToken}async getValidToken(e,t,r={}){let o=await this.storage.getToken(e,t,r.tenant);if(!o){let c=r.tenant?` and tenant ${r.tenant}`:"";throw new Error(`No token found for provider ${e}, email ${t}${c}`)}if(!(r.autoRefresh!==false&&this.isTokenExpired(o.token,r)))return o.token;if(!o.token.refreshToken)throw new Error("Token expired and no refresh token available");let i=this.providers.get(e);if(!i)throw new Error(`Provider ${e} not found`);let a=await i.refreshToken(o.token.refreshToken);return await this.storage.updateToken(o.id,{token:a}),a}async queryTokens(e){let t=this.autoRefreshOptions.enabled&&!e.includeExpired?{...e,includeExpired:true}:e,r=await this.storage.queryTokens(t);return this.autoRefreshOptions.enabled&&!e.includeExpired?(await this.refreshTokensIfNeeded(r)).filter(n=>new Date(n.token.expiresAt).getTime()>this.now()):r}async getTokensByUserId(e){return this.queryTokens({userId:e})}async getTokensByEmail(e){return this.queryTokens({email:e})}async deleteToken(e,t,r){return this.storage.deleteTokenByProviderEmail(e,t,r)}async cleanupExpiredTokens(){return this.storage.deleteExpiredTokens()}async cleanupExpiredStates(){return this.storage.cleanupExpiredStates()}isTokenExpired(e,t={}){let{expirationBuffer:r=300}=t;if(e.createdAt&&e.expiresIn!==void 0){let i=e.createdAt+e.expiresIn*1e3;return this.now()+r*1e3>=i}let o=new Date(e.expiresAt).getTime();return this.now()+r*1e3>=o}detectProviderType(e,t){var o;let r=t.authorizationUrl.toLowerCase();return r.includes("accounts.google.com")?"google":r.includes("github.com")?"github":r.includes("facebook.com")?"facebook":r.includes("microsoft.com")||r.includes("microsoftonline.com")?e.toLowerCase().includes("outlook")||(o=t.scopes)!=null&&o.some(n=>n.includes("outlook"))?"outlook":"microsoft":"generic"}async refreshTokensIfNeeded(e){let t=e.map(async r=>{if(this.shouldRefreshToken(r))try{let o=this.providers.get(r.provider);if(!o)return console.warn(`Provider ${r.provider} not found for token refresh`),r;if(!r.token.refreshToken)return console.warn(`No refresh token available for ${r.provider}:${r.email}`),r;let n=await o.refreshToken(r.token.refreshToken);return await this.storage.updateToken(r.id,{token:n})||r}catch(o){return this.autoRefreshOptions.onRefreshError&&this.autoRefreshOptions.onRefreshError(o,r),console.error(`Failed to refresh token for ${r.provider}:${r.email}:`,o),r}return r});return Promise.all(t)}shouldRefreshToken(e){if(!e.token.refreshToken)return false;let t=new Date(e.token.expiresAt).getTime(),r=this.now();if(r>=t)return true;let o=this.autoRefreshOptions.refreshBuffer*60*1e3,n=t-o;return r>=n}updateAutoRefreshOptions(e){this.autoRefreshOptions={...this.autoRefreshOptions,...e};}async getTokensAcrossTenants(e,t){return this.queryTokens({provider:e,userId:t})}async getTenants(e,t){let o=(await this.queryTokens({provider:e,email:t})).map(n=>n.tenant).filter(n=>n!==void 0);return [...new Set(o)]}async getTokenByTenantName(e,t,r){return (await this.queryTokens({provider:e,email:t})).find(n=>n.tenantName===r)||null}async hasTokenForTenant(e,t,r){return await this.storage.getToken(e,t,r)!==null}async getMultiTenantTokens(e,t){return (await this.queryTokens({provider:e,email:t})).map(o=>({token:o,tenant:o.tenant,tenantName:o.tenantName}))}};var ye=(s,e)=>sealData(s,{password:e}),ve=(s,e)=>unsealData(s,{password:e});
|
|
2
|
-
export{
|
|
1
|
+
import {createHash,randomBytes}from'crypto';import {sealData,unsealData}from'iron-session';var v=class{buildUrlParams(e){return Object.entries(e).filter(([,r])=>r!==void 0).map(([r,o])=>`${r}=${encodeURIComponent(o)}`).join("&")}generateAuthorizationUrl(e,t,r){let o={client_id:e.clientId,redirect_uri:e.redirectUri,response_type:"code",scope:e.scopes.join(" "),state:t};(e.usePKCE||e.pkce)&&r&&(o.code_challenge=r,o.code_challenge_method="S256");let n={...e.additionalParams,...e.extraAuthParams};return Object.assign(o,n),`${e.authorizationUrl}?${this.buildUrlParams(o)}`}};var A=class{buildUrlParams(e){return Object.entries(e).filter(([,r])=>r!==void 0).map(([r,o])=>`${r}=${encodeURIComponent(o)}`).join("&")}async exchangeCodeForToken(e,t,r){let o={grant_type:"authorization_code",code:e,redirect_uri:t.redirectUri,client_id:t.clientId};(t.usePKCE||t.pkce)&&r?o.code_verifier=r:t.clientSecret&&(o.client_secret=t.clientSecret);let n=await fetch(t.tokenUrl,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:this.buildUrlParams(o)});if(!n.ok){let p=await n.text();throw new Error(`Token exchange failed: ${n.statusText} - ${p}`)}let i=await n.json(),a=t.responseRootKey?i[t.responseRootKey]:i;return this.normalizeTokenResponse(a,i,void 0,t)}async refreshToken(e,t){let r={grant_type:"refresh_token",refresh_token:e,client_id:t.clientId};!(t.usePKCE||t.pkce)&&t.clientSecret&&(r.client_secret=t.clientSecret);let o=await fetch(t.tokenUrl,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:this.buildUrlParams(r)});if(!o.ok){let a=await o.text();throw new Error(`Token refresh failed: ${o.statusText} - ${a}`)}let n=await o.json(),i=t.responseRootKey?n[t.responseRootKey]:n;return this.normalizeTokenResponse(i,n,e,t)}normalizeTokenResponse(e,t,r,o){var h,l,f,m,g;let n=Date.now(),i=this.extractValue(e,(h=o==null?void 0:o.tokenPaths)==null?void 0:h.accessToken,["authed_user.access_token","authed_user.accessToken","access_token","accessToken","token.access_token","token.accessToken"]);if(!i)throw new Error("Token response did not include an access_token");let a=this.extractValue(e,(l=o==null?void 0:o.tokenPaths)==null?void 0:l.refreshToken,["authed_user.refresh_token","authed_user.refreshToken","refresh_token","refreshToken","token.refresh_token","token.refreshToken"])??r,p=this.extractValue(e,(f=o==null?void 0:o.tokenPaths)==null?void 0:f.expiresIn,["authed_user.expires_in","authed_user.expiresIn","expires_in","expiresIn","token.expires_in","token.expiresIn"]),P=(typeof p=="string"?parseInt(p,10):p)||3600,c=this.extractValue(e,(m=o==null?void 0:o.tokenPaths)==null?void 0:m.scope,["authed_user.scope","scope","token.scope"]),F=this.extractValue(e,(g=o==null?void 0:o.tokenPaths)==null?void 0:g.tokenType,["authed_user.token_type","authed_user.tokenType","token_type","tokenType","token.token_type","token.tokenType"])??"Bearer";return {accessToken:i,refreshToken:a,expiresAt:new Date(n+P*1e3),expiresIn:P,tokenType:F,scope:c,createdAt:n,raw:t}}extractValue(e,t,r){if(t){let o=Array.isArray(t)?t:[t];for(let n of o){let i=this.getNestedValue(e,n);if(i!==void 0)return i}}if(r)for(let o of r){let n=this.getNestedValue(e,o);if(n!==void 0)return n}}getNestedValue(e,t){let r=t.split("."),o=e;for(let n of r)if(o&&typeof o=="object"&&n in o)o=o[n];else return;return o}};var k=class{constructor(e,t,r,o){this.config=e;this.authUrlStrategy=t||this.createAuthorizationUrlStrategy(),this.tokenStrategy=r||this.createTokenExchangeStrategy(),this.profileFetcher=o;}authUrlStrategy;tokenStrategy;profileFetcher;async fetchProfile(e){if(!this.profileFetcher)throw new Error("Profile fetcher not configured for this provider");return this.profileFetcher.fetchUserInfo(e)}getProfileEndpoint(){if(!this.profileFetcher)throw new Error("Profile fetcher not configured for this provider");return this.profileFetcher.getEndpoint()}setProfileFetcher(e){this.profileFetcher=e;}hasProfileFetcher(){return !!this.profileFetcher}generateAuthorizationUrl(e,t){return this.authUrlStrategy.generateAuthorizationUrl(this.config,e,t)}async exchangeCodeForToken(e,t){return this.tokenStrategy.exchangeCodeForToken(e,this.config,t)}async refreshToken(e){return this.tokenStrategy.refreshToken(e,this.config)}};var x=class extends k{constructor(e,t,r,o){super(e,t,r,o);}createAuthorizationUrlStrategy(){return new v}createTokenExchangeStrategy(){return new A}};var d=class{constructor(e){this.profileEndpoint=e;}async fetchUserInfo(e){let t=await fetch(this.profileEndpoint,{headers:{Authorization:`Bearer ${e}`,Accept:"application/json",...this.getAdditionalHeaders()}});if(!t.ok)throw new Error(`Failed to fetch profile from ${this.profileEndpoint}: ${t.statusText}`);let r=await t.json();return this.mapToUserProfile(r)}getAdditionalHeaders(){return {}}getEndpoint(){return this.profileEndpoint}};var S=class extends d{constructor(){super("https://www.googleapis.com/oauth2/v2/userinfo");}mapToUserProfile(e){return {email:e.email,name:e.name,id:e.id,avatar:e.picture,username:e.email,raw:e}}};var w=class extends d{constructor(){super("https://api.github.com/user");}mapToUserProfile(e){var t;return {email:e.email,name:e.name||e.login,id:(t=e.id)==null?void 0:t.toString(),avatar:e.avatar_url,username:e.login,raw:e}}getAdditionalHeaders(){return {"User-Agent":"OAuth2-Token-Manager"}}};var U=class extends d{constructor(){super("https://graph.microsoft.com/v1.0/me");}mapToUserProfile(e){return {email:e.mail||e.userPrincipalName,name:e.displayName,id:e.id,avatar:void 0,username:e.userPrincipalName,raw:e}}};var u=class extends d{constructor(t,r,o){super(t);this.mapping=r;this.additionalHeaders=o;}mapToUserProfile(t){return this.mapping?{email:this.getNestedProperty(t,this.mapping.email),name:this.mapping.name?this.getNestedProperty(t,this.mapping.name):void 0,id:this.mapping.id?this.getNestedProperty(t,this.mapping.id):void 0,avatar:this.mapping.avatar?this.getNestedProperty(t,this.mapping.avatar):void 0,username:this.mapping.username?this.getNestedProperty(t,this.mapping.username):void 0,tenant:this.mapping.tenant?this.getNestedProperty(t,this.mapping.tenant):void 0,tenantName:this.mapping.tenantName?this.getNestedProperty(t,this.mapping.tenantName):void 0,raw:t}:{email:t.email||t.mail||t.emailAddress,name:t.name||t.displayName||t.full_name,id:t.id||t.sub||t.user_id,avatar:t.avatar||t.picture||t.avatar_url,username:t.username||t.login||t.preferred_username,tenant:t.tenant||t.workspace_id||t.organization_id||t.team_id,tenantName:t.tenantName||t.workspace_name||t.organization_name||t.team_name,raw:t}}getAdditionalHeaders(){return this.additionalHeaders||{}}getNestedProperty(t,r){return r.split(".").reduce((o,n)=>o==null?void 0:o[n],t)}};var E=class{static createProfileFetcher(e,t,r,o){if(o)return o;if(r!=null&&r.profileUrl)return new u(r.profileUrl,r.profileMapping,r.profileHeaders);switch(e){case "google":return new S;case "github":return new w;case "microsoft":case "outlook":return new U;case "facebook":return new u("https://graph.facebook.com/me?fields=id,name,email,picture");case "generic":default:let n=t.profileUrl||t.userInfoUrl;return n?new u(n):void 0}}static registerCustomProfileFetcher(e,t){this.customFetchers.set(e,t);}static customFetchers=new Map;static getCustomProfileFetcher(e){return this.customFetchers.get(e)}};var O=class s{static presetConfigs={google:{authorizationUrl:"https://accounts.google.com/o/oauth2/v2/auth",tokenUrl:"https://oauth2.googleapis.com/token",profileUrl:"https://www.googleapis.com/oauth2/v2/userinfo",usePKCE:true,extraAuthParams:{access_type:"offline",prompt:"consent"}},github:{authorizationUrl:"https://github.com/login/oauth/authorize",tokenUrl:"https://github.com/login/oauth/access_token",profileUrl:"https://api.github.com/user"},microsoft:{authorizationUrl:"https://login.microsoftonline.com/common/oauth2/v2.0/authorize",tokenUrl:"https://login.microsoftonline.com/common/oauth2/v2.0/token",profileUrl:"https://graph.microsoft.com/v1.0/me",usePKCE:true},outlook:{authorizationUrl:"https://login.microsoftonline.com/common/oauth2/v2.0/authorize",tokenUrl:"https://login.microsoftonline.com/common/oauth2/v2.0/token",profileUrl:"https://graph.microsoft.com/v1.0/me",usePKCE:true,extraAuthParams:{prompt:"select_account"}},facebook:{authorizationUrl:"https://www.facebook.com/v12.0/dialog/oauth",tokenUrl:"https://graph.facebook.com/v12.0/oauth/access_token",profileUrl:"https://graph.facebook.com/me?fields=id,name,email,picture"}};createProvider(e,t,r){let o=e!=="generic"?s.presetConfigs[e]||{}:{},n={...o,...t,authorizationUrl:t.authorizationUrl||o.authorizationUrl||"",tokenUrl:t.tokenUrl||o.tokenUrl||"",profileUrl:t.profileUrl||o.profileUrl,extraAuthParams:{...o.extraAuthParams||{},...t.extraAuthParams||{}}},i=E.createProfileFetcher(e,n,void 0,r);return new x(n,void 0,void 0,i)}static registerPreset(e,t){s.presetConfigs[e]=t;}static getPresetConfig(e){return s.presetConfigs[e]}};var T=class{tokens=new Map;states=new Map;profileFetchers=new Map;encryption;constructor(e){this.encryption=e==null?void 0:e.encryption;}generateId(){return Math.random().toString(36).substring(2)+Date.now().toString(36)}async encryptToken(e){if(!this.encryption)return e;let t=await this.encryption.encryptTokenFields({accessToken:e.accessToken,refreshToken:e.refreshToken});return {...e,accessToken:t.accessToken,refreshToken:t.refreshToken}}async decryptToken(e){if(!this.encryption||!this.encryption.isEncrypted(e.accessToken))return e;let t=await this.encryption.decryptTokenFields({accessToken:e.accessToken,refreshToken:e.refreshToken});return {...e,accessToken:t.accessToken,refreshToken:t.refreshToken}}async decryptStoredToken(e){return this.encryption?{...e,token:await this.decryptToken(e.token)}:e}async saveToken(e){let t=await this.getTokenInternal(e.provider,e.email,e.tenant),r=await this.encryptToken(e.token),o={...e,token:r};if(t){let i={...t,...o,id:t.id,createdAt:t.createdAt,updatedAt:new Date};return this.tokens.set(t.id,i),this.decryptStoredToken(i)}let n={...o,id:this.generateId(),createdAt:new Date,updatedAt:new Date};return this.tokens.set(n.id,n),this.decryptStoredToken(n)}async queryTokensInternal(e){let t=Array.from(this.tokens.values());if(e.id&&(t=t.filter(r=>r.id===e.id)),e.provider&&(t=t.filter(r=>r.provider===e.provider)),e.userId&&(t=t.filter(r=>r.userId===e.userId)),e.email&&(t=t.filter(r=>r.email===e.email)),e.tenant!==void 0&&(t=t.filter(r=>r.tenant===e.tenant)),!e.includeExpired){let r=new Date().getTime();t=t.filter(o=>new Date(o.token.expiresAt).getTime()>=r);}return e.offset!==void 0&&(t=t.slice(e.offset)),e.limit!==void 0&&(t=t.slice(0,e.limit)),t}async queryTokens(e){let t=await this.queryTokensInternal(e);return Promise.all(t.map(r=>this.decryptStoredToken(r)))}async getTokenInternal(e,t,r){return (await this.queryTokensInternal({provider:e,email:t,tenant:r,includeExpired:true}))[0]||null}async getToken(e,t,r){let o=await this.getTokenInternal(e,t,r);return o?this.decryptStoredToken(o):null}async getTokenById(e){let r=(await this.queryTokensInternal({id:e,includeExpired:true}))[0]||null;return r?this.decryptStoredToken(r):null}async getTokensByUserId(e){return this.queryTokens({userId:e})}async getTokensByEmail(e){return this.queryTokens({email:e})}async getTokensByProvider(e){return this.queryTokens({provider:e})}async getAccounts(e,t){return this.queryTokens({userId:e,provider:t})}async getTokensForEmail(e,t,r){return (await this.queryTokens({userId:e,provider:t,email:r}))[0]||null}async getTokens(e,t){return this.queryTokens({userId:e,provider:t})}async updateToken(e,t){let r=this.tokens.get(e);if(!r)return null;let o=t.token;t.token&&this.encryption&&(o=await this.encryptToken(t.token));let n={...r,...o&&{token:o},...t.metadata&&{metadata:{...r.metadata,...t.metadata}},updatedAt:new Date};return this.tokens.set(e,n),this.decryptStoredToken(n)}async deleteToken(e){return this.tokens.delete(e)}async deleteTokenByProviderEmail(e,t,r){let o=await this.getTokenInternal(e,t,r);return o?this.tokens.delete(o.id):false}async deleteExpiredTokens(){let e=new Date().getTime(),t=Array.from(this.tokens.entries()).filter(([,r])=>new Date(r.token.expiresAt).getTime()<e).map(([r])=>r);return t.forEach(r=>this.tokens.delete(r)),t.length}async saveAuthorizationState(e){let t={...e,createdAt:new Date(Date.now())};return this.states.set(e.state,t),t}async getAuthorizationState(e){return this.states.get(e)||null}async deleteAuthorizationState(e){return this.states.delete(e)}async cleanupExpiredStates(){let e=new Date().getTime(),t=10*60*1e3,r=Array.from(this.states.entries()).filter(([,o])=>e-o.createdAt.getTime()>t).map(([o])=>o);return r.forEach(o=>this.states.delete(o)),r.length}registerProfileFetcher(e,t){this.profileFetchers.set(e,t);}getProfileFetcher(e){return this.profileFetchers.get(e)}getProfileFetchers(){return new Map(this.profileFetchers)}};var N=()=>randomBytes(32).toString("base64").replace(/[^a-zA-Z0-9]/g,"").substring(0,128),B=s=>createHash("sha256").update(s).digest("base64url"),M=()=>randomBytes(16).toString("base64url");var b=class{storage;providerFactory;providers=new Map;providerConfigs=new Map;now;autoRefreshOptions;constructor(e={}){var t,r,o;this.storage=e.storage||new T,this.providerFactory=new O,this.now=Date.now,this.autoRefreshOptions={enabled:((t=e.autoRefresh)==null?void 0:t.enabled)??true,refreshBuffer:((r=e.autoRefresh)==null?void 0:r.refreshBuffer)??10,onRefreshError:(o=e.autoRefresh)==null?void 0:o.onRefreshError},e.providers&&Object.entries(e.providers).forEach(([n,i])=>{this.registerProvider(n,i);});}registerProvider(e,t){this.providerConfigs.set(e,t);let r=this.storage.getProfileFetcher(e),o=this.detectProviderType(e,t),n=this.providerFactory.createProvider(o,t,r);this.providers.set(e,n);}async authorize(e){var a;let t=this.providers.get(e.provider);if(!t)throw new Error(`Provider ${e.provider} not found`);let r=M(),o,n;if(((a=this.providerConfigs.get(e.provider))==null?void 0:a.usePKCE)||e.usePKCE){let p=N(),y=B(p);o=t.generateAuthorizationUrl(r,y),n={state:r,codeVerifier:p,config:this.providerConfigs.get(e.provider),metadata:{...e.metadata,userId:e.userId,email:e.email,provider:e.provider,tenant:e.tenant,tenantName:e.tenantName}};}else o=t.generateAuthorizationUrl(r),n={state:r,config:this.providerConfigs.get(e.provider),metadata:{...e.metadata,userId:e.userId,email:e.email,provider:e.provider,tenant:e.tenant,tenantName:e.tenantName}};return await this.storage.saveAuthorizationState(n),{url:o,state:r}}async handleCallback(e,t){var l,f,m,g,z;let r=await this.storage.getAuthorizationState(t);if(!r)throw new Error("Invalid or expired state");let o=(l=r.metadata)==null?void 0:l.provider;if(!o)throw new Error("Provider not found in authorization state");let n=this.providers.get(o);if(!n)throw new Error(`Provider ${o} not found`);let i=await n.exchangeCodeForToken(e,r.codeVerifier),a=(f=r.metadata)==null?void 0:f.userId,p=(m=r.metadata)==null?void 0:m.email,y=(g=r.metadata)==null?void 0:g.tenant,P=(z=r.metadata)==null?void 0:z.tenantName;if(!a||!p)throw new Error("User ID and email are required in authorization state");let c;if(n.hasProfileFetcher())try{c=await n.fetchProfile(i.accessToken);}catch(K){console.warn("Failed to fetch user profile:",K);}let F=await this.storage.saveToken({provider:o,userId:a,email:(c==null?void 0:c.email)||p,tenant:(c==null?void 0:c.tenant)||y,tenantName:(c==null?void 0:c.tenantName)||P,token:i,metadata:{...r.metadata,profileFetched:!!c}});await this.storage.deleteAuthorizationState(t);let h=this.providerConfigs.get(o);return h!=null&&h.onSuccess&&await h.onSuccess(a,i),{token:F,profile:c}}async getAccessToken(e,t,r={}){return (await this.getValidToken(e,t,r)).accessToken}async getValidToken(e,t,r={}){let o=await this.storage.getToken(e,t,r.tenant);if(!o){let p=r.tenant?` and tenant ${r.tenant}`:"";throw new Error(`No token found for provider ${e}, email ${t}${p}`)}if(!(r.autoRefresh!==false&&this.isTokenExpired(o.token,r)))return o.token;if(!o.token.refreshToken)throw new Error("Token expired and no refresh token available");let i=this.providers.get(e);if(!i)throw new Error(`Provider ${e} not found`);let a=await i.refreshToken(o.token.refreshToken);return await this.storage.updateToken(o.id,{token:a}),a}async queryTokens(e){let t=this.autoRefreshOptions.enabled&&!e.includeExpired?{...e,includeExpired:true}:e,r=await this.storage.queryTokens(t);return this.autoRefreshOptions.enabled&&!e.includeExpired?(await this.refreshTokensIfNeeded(r)).filter(n=>new Date(n.token.expiresAt).getTime()>this.now()):r}async getTokensByUserId(e){return this.queryTokens({userId:e})}async getTokensByEmail(e){return this.queryTokens({email:e})}async deleteToken(e,t,r){return this.storage.deleteTokenByProviderEmail(e,t,r)}async cleanupExpiredTokens(){return this.storage.deleteExpiredTokens()}async cleanupExpiredStates(){return this.storage.cleanupExpiredStates()}isTokenExpired(e,t={}){let{expirationBuffer:r=300}=t;if(e.createdAt&&e.expiresIn!==void 0){let i=e.createdAt+e.expiresIn*1e3;return this.now()+r*1e3>=i}let o=new Date(e.expiresAt).getTime();return this.now()+r*1e3>=o}detectProviderType(e,t){var o;let r=t.authorizationUrl.toLowerCase();return r.includes("accounts.google.com")?"google":r.includes("github.com")?"github":r.includes("facebook.com")?"facebook":r.includes("microsoft.com")||r.includes("microsoftonline.com")?e.toLowerCase().includes("outlook")||(o=t.scopes)!=null&&o.some(n=>n.includes("outlook"))?"outlook":"microsoft":"generic"}async refreshTokensIfNeeded(e){let t=e.map(async r=>{if(this.shouldRefreshToken(r))try{let o=this.providers.get(r.provider);if(!o)return console.warn(`Provider ${r.provider} not found for token refresh`),r;if(!r.token.refreshToken)return console.warn(`No refresh token available for ${r.provider}:${r.email}`),r;let n=await o.refreshToken(r.token.refreshToken);return await this.storage.updateToken(r.id,{token:n})||r}catch(o){return this.autoRefreshOptions.onRefreshError&&this.autoRefreshOptions.onRefreshError(o,r),console.error(`Failed to refresh token for ${r.provider}:${r.email}:`,o),r}return r});return Promise.all(t)}shouldRefreshToken(e){if(!e.token.refreshToken)return false;let t=new Date(e.token.expiresAt).getTime(),r=this.now();if(r>=t)return true;let o=this.autoRefreshOptions.refreshBuffer*60*1e3,n=t-o;return r>=n}updateAutoRefreshOptions(e){this.autoRefreshOptions={...this.autoRefreshOptions,...e};}async getTokensAcrossTenants(e,t){return this.queryTokens({provider:e,userId:t})}async getTenants(e,t){let o=(await this.queryTokens({provider:e,email:t})).map(n=>n.tenant).filter(n=>n!==void 0);return [...new Set(o)]}async getTokenByTenantName(e,t,r){return (await this.queryTokens({provider:e,email:t})).find(n=>n.tenantName===r)||null}async hasTokenForTenant(e,t,r){return await this.storage.getToken(e,t,r)!==null}async getMultiTenantTokens(e,t){return (await this.queryTokens({provider:e,email:t})).map(o=>({token:o,tenant:o.tenant,tenantName:o.tenantName}))}};var C=(s,e)=>sealData(s,{password:e}),R=(s,e)=>unsealData(s,{password:e});var I=class{encryptionKey;constructor(e){if(!e.encryptionKey||e.encryptionKey.length<32)throw new Error("Encryption key must be at least 32 characters long");this.encryptionKey=e.encryptionKey;}async encryptTokenFields(e){let t=await C(e.accessToken,this.encryptionKey),r;return e.refreshToken&&(r=await C(e.refreshToken,this.encryptionKey)),{accessToken:t,refreshToken:r}}async decryptTokenFields(e){let t=await R(e.accessToken,this.encryptionKey),r;if(e.refreshToken&&(r=await R(e.refreshToken,this.encryptionKey)),!t||typeof t=="object"&&Object.keys(t).length===0)throw new Error("Failed to decrypt access token - invalid encryption key or corrupted data");return {accessToken:t,refreshToken:r||void 0}}isEncrypted(e){return e.startsWith("Fe26.2")}};
|
|
2
|
+
export{d as BaseProfileFetcher,x as GenericOAuth2Provider,u as GenericProfileFetcher,w as GitHubProfileFetcher,S as GoogleProfileFetcher,T as InMemoryStorageAdapter,U as MicrosoftProfileFetcher,b as OAuth2Client,k as OAuth2Provider,E as ProfileFetcherFactory,v as StandardAuthorizationUrlStrategy,A as StandardTokenExchangeStrategy,I as TokenEncryption,B as createCodeChallenge,N as createCodeVerifier,M as generateState,C as seal,R as unseal};//# sourceMappingURL=index.js.map
|
|
3
3
|
//# sourceMappingURL=index.js.map
|