@dainprotocol/oauth2-token-manager 0.3.1 → 0.3.2
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 +0 -68
- package/dist/index.cjs +2 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/package.json +23 -27
package/README.md
CHANGED
|
@@ -11,7 +11,6 @@ A simple OAuth2 token management library for Node.js. Store and manage OAuth2 to
|
|
|
11
11
|
- [API Reference](#-api-reference)
|
|
12
12
|
- [Storage Adapters](#-storage-adapters)
|
|
13
13
|
- [Examples](#-examples)
|
|
14
|
-
- [Security](#-security)
|
|
15
14
|
|
|
16
15
|
## 🚀 Features
|
|
17
16
|
|
|
@@ -21,7 +20,6 @@ A simple OAuth2 token management library for Node.js. Store and manage OAuth2 to
|
|
|
21
20
|
- **Profile Fetching**: Get user profiles during OAuth callback
|
|
22
21
|
- **Custom Profile Fetchers**: Register custom profile fetchers per storage instance
|
|
23
22
|
- **Pluggable Storage**: In-memory, PostgreSQL, Drizzle, or custom adapters
|
|
24
|
-
- **Token Encryption**: AES-256-CBC encryption for tokens at rest (CASA compliant)
|
|
25
23
|
- **Type Safe**: Full TypeScript support
|
|
26
24
|
|
|
27
25
|
## 📦 Installation
|
|
@@ -95,7 +93,6 @@ const accessToken = await oauth.getAccessToken('google', 'user@example.com');
|
|
|
95
93
|
2. **Automatic override**: Saving a token with same provider + email replaces the old one
|
|
96
94
|
3. **Auto refresh**: Expired tokens refresh automatically when you request them
|
|
97
95
|
4. **Simple storage**: Just provider (string), userId (string), and email (string)
|
|
98
|
-
5. **Encrypted tokens**: Access and refresh tokens are encrypted at rest using AES-256-CBC
|
|
99
96
|
|
|
100
97
|
## 📚 API Reference
|
|
101
98
|
|
|
@@ -505,49 +502,6 @@ const accessToken = await oauth.getAccessToken('customApi', 'user@example.com');
|
|
|
505
502
|
|
|
506
503
|
See [Custom Token Paths Guide](./CUSTOM_TOKEN_PATHS.md) for more examples.
|
|
507
504
|
|
|
508
|
-
### Token Encryption
|
|
509
|
-
|
|
510
|
-
All storage adapters support AES-256-CBC encryption for tokens at rest:
|
|
511
|
-
|
|
512
|
-
```typescript
|
|
513
|
-
// In-Memory with encryption
|
|
514
|
-
import { InMemoryStorageAdapter } from '@dainprotocol/oauth2-token-manager';
|
|
515
|
-
|
|
516
|
-
const storage = new InMemoryStorageAdapter({
|
|
517
|
-
encryptionKey: process.env.TOKEN_ENCRYPTION_KEY, // Min 32 characters
|
|
518
|
-
});
|
|
519
|
-
|
|
520
|
-
// PostgreSQL with encryption
|
|
521
|
-
import { PostgresStorageFactory } from '@dainprotocol/oauth2-storage-postgres';
|
|
522
|
-
|
|
523
|
-
const storage = await PostgresStorageFactory.create({
|
|
524
|
-
host: 'localhost',
|
|
525
|
-
port: 5432,
|
|
526
|
-
username: 'user',
|
|
527
|
-
password: 'password',
|
|
528
|
-
database: 'oauth_tokens',
|
|
529
|
-
encryption: {
|
|
530
|
-
encryptionKey: process.env.TOKEN_ENCRYPTION_KEY,
|
|
531
|
-
},
|
|
532
|
-
});
|
|
533
|
-
|
|
534
|
-
// Drizzle with encryption
|
|
535
|
-
import { DrizzleStorageAdapter } from '@dainprotocol/oauth2-storage-drizzle';
|
|
536
|
-
|
|
537
|
-
const storage = await DrizzleStorageAdapter.create(db, {
|
|
538
|
-
dialect: 'postgres',
|
|
539
|
-
encryption: {
|
|
540
|
-
encryptionKey: process.env.TOKEN_ENCRYPTION_KEY,
|
|
541
|
-
},
|
|
542
|
-
});
|
|
543
|
-
```
|
|
544
|
-
|
|
545
|
-
Generate a secure encryption key:
|
|
546
|
-
|
|
547
|
-
```bash
|
|
548
|
-
openssl rand -base64 32
|
|
549
|
-
```
|
|
550
|
-
|
|
551
505
|
### Custom Profile Fetcher Example
|
|
552
506
|
|
|
553
507
|
```typescript
|
|
@@ -608,28 +562,6 @@ const testStorage = await DrizzleStorageAdapter.create(testDb, {
|
|
|
608
562
|
});
|
|
609
563
|
```
|
|
610
564
|
|
|
611
|
-
## 🔒 Security
|
|
612
|
-
|
|
613
|
-
### Encryption Specification
|
|
614
|
-
|
|
615
|
-
| Specification | Implementation |
|
|
616
|
-
| -------------- | -------------- |
|
|
617
|
-
| Algorithm | AES-256-CBC |
|
|
618
|
-
| Key Derivation | PBKDF2 |
|
|
619
|
-
| Integrity | HMAC signature |
|
|
620
|
-
| Min Key Length | 32 characters |
|
|
621
|
-
|
|
622
|
-
### What Gets Encrypted
|
|
623
|
-
|
|
624
|
-
- `accessToken` - Always encrypted
|
|
625
|
-
- `refreshToken` - Always encrypted (when present)
|
|
626
|
-
|
|
627
|
-
### CASA Compliance
|
|
628
|
-
|
|
629
|
-
This library meets Google CASA (Cloud Application Security Assessment) requirements for token storage. For CASA submissions:
|
|
630
|
-
|
|
631
|
-
> OAuth tokens (access_token, refresh_token) are encrypted at rest using AES-256-CBC symmetric encryption with PBKDF2 key derivation via the iron-session library, which includes HMAC integrity verification.
|
|
632
|
-
|
|
633
565
|
## 📄 License
|
|
634
566
|
|
|
635
567
|
MIT © Dain Protocol
|
package/dist/index.cjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
'use strict';var crypto=require('crypto'),ironSession=require('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=()=>crypto.randomBytes(32).toString("base64").replace(/[^a-zA-Z0-9]/g,"").substring(0,128),B=s=>crypto.createHash("sha256").update(s).digest("base64url"),M=()=>crypto.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)=>ironSession.sealData(s,{password:e}),R=(s,e)=>ironSession.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
|
-
exports.BaseProfileFetcher=
|
|
1
|
+
'use strict';var crypto=require('crypto'),ironSession=require('iron-session');var v=class{buildUrlParams(e){return Object.entries(e).filter(([,r])=>r!==void 0).map(([r,n])=>`${r}=${encodeURIComponent(n)}`).join("&")}generateAuthorizationUrl(e,t,r){let n={client_id:e.clientId,redirect_uri:e.redirectUri,response_type:"code",scope:e.scopes.join(" "),state:t};(e.usePKCE||e.pkce)&&r&&(n.code_challenge=r,n.code_challenge_method="S256");let o={...e.additionalParams,...e.extraAuthParams};return Object.assign(n,o),`${e.authorizationUrl}?${this.buildUrlParams(n)}`}};var A=class{buildUrlParams(e){return Object.entries(e).filter(([,r])=>r!==void 0).map(([r,n])=>`${r}=${encodeURIComponent(n)}`).join("&")}async exchangeCodeForToken(e,t,r){let n={grant_type:"authorization_code",code:e,redirect_uri:t.redirectUri,client_id:t.clientId};(t.usePKCE||t.pkce)&&r?n.code_verifier=r:t.clientSecret&&(n.client_secret=t.clientSecret);let o={"Content-Type":"application/x-www-form-urlencoded"};if(t.useBasicAuth&&t.clientId&&t.clientSecret){let d=Buffer.from(`${t.clientId}:${t.clientSecret}`).toString("base64");o.Authorization=`Basic ${d}`;}let i=await fetch(t.tokenUrl,{method:"POST",headers:o,body:this.buildUrlParams(n)});if(!i.ok){let d=await i.text();throw new Error(`Token exchange failed: ${i.statusText} - ${d}`)}let a=await i.json(),c=t.responseRootKey?a[t.responseRootKey]:a;return this.normalizeTokenResponse(c,a,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 n={"Content-Type":"application/x-www-form-urlencoded"};if(t.useBasicAuth&&t.clientId&&t.clientSecret){let c=Buffer.from(`${t.clientId}:${t.clientSecret}`).toString("base64");n.Authorization=`Basic ${c}`;}let o=await fetch(t.tokenUrl,{method:"POST",headers:n,body:this.buildUrlParams(r)});if(!o.ok){let c=await o.text();throw new Error(`Token refresh failed: ${o.statusText} - ${c}`)}let i=await o.json(),a=t.responseRootKey?i[t.responseRootKey]:i;return this.normalizeTokenResponse(a,i,e,t)}normalizeTokenResponse(e,t,r,n){var u,f,m,g,k;let o=Date.now(),i=this.extractValue(e,(u=n==null?void 0:n.tokenPaths)==null?void 0:u.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,(f=n==null?void 0:n.tokenPaths)==null?void 0:f.refreshToken,["authed_user.refresh_token","authed_user.refreshToken","refresh_token","refreshToken","token.refresh_token","token.refreshToken"])??r,c=this.extractValue(e,(m=n==null?void 0:n.tokenPaths)==null?void 0:m.expiresIn,["authed_user.expires_in","authed_user.expiresIn","expires_in","expiresIn","token.expires_in","token.expiresIn"]),P=(typeof c=="string"?parseInt(c,10):c)||3600,p=this.extractValue(e,(g=n==null?void 0:n.tokenPaths)==null?void 0:g.scope,["authed_user.scope","scope","token.scope"]),b=this.extractValue(e,(k=n==null?void 0:n.tokenPaths)==null?void 0:k.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(o+P*1e3),expiresIn:P,tokenType:b,scope:p,createdAt:o,raw:t}}extractValue(e,t,r){if(t){let n=Array.isArray(t)?t:[t];for(let o of n){let i=this.getNestedValue(e,o);if(i!==void 0)return i}}if(r)for(let n of r){let o=this.getNestedValue(e,n);if(o!==void 0)return o}}getNestedValue(e,t){let r=t.split("."),n=e;for(let o of r)if(n&&typeof n=="object"&&o in n)n=n[o];else return;return n}};var T=class{constructor(e,t,r,n){this.config=e;this.authUrlStrategy=t||this.createAuthorizationUrlStrategy(),this.tokenStrategy=r||this.createTokenExchangeStrategy(),this.profileFetcher=n;}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 T{constructor(e,t,r,n){super(e,t,r,n);}createAuthorizationUrlStrategy(){return new v}createTokenExchangeStrategy(){return new A}};var h=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 h{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 h{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 h{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 l=class extends h{constructor(t,r,n){super(t);this.mapping=r;this.additionalHeaders=n;}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((n,o)=>n==null?void 0:n[o],t)}};var E=class{static createProfileFetcher(e,t,r,n){if(n)return n;if(r!=null&&r.profileUrl)return new l(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 l("https://graph.facebook.com/me?fields=id,name,email,picture");case "generic":default:let o=t.profileUrl||t.userInfoUrl;return o?new l(o):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 n=e!=="generic"?s.presetConfigs[e]||{}:{},o={...n,...t,authorizationUrl:t.authorizationUrl||n.authorizationUrl||"",tokenUrl:t.tokenUrl||n.tokenUrl||"",profileUrl:t.profileUrl||n.profileUrl,extraAuthParams:{...n.extraAuthParams||{},...t.extraAuthParams||{}}},i=E.createProfileFetcher(e,o,void 0,r);return new x(o,void 0,void 0,i)}static registerPreset(e,t){s.presetConfigs[e]=t;}static getPresetConfig(e){return s.presetConfigs[e]}};var y=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),n={...e,token:r};if(t){let i={...t,...n,id:t.id,createdAt:t.createdAt,updatedAt:new Date};return this.tokens.set(t.id,i),this.decryptStoredToken(i)}let o={...n,id:this.generateId(),createdAt:new Date,updatedAt:new Date};return this.tokens.set(o.id,o),this.decryptStoredToken(o)}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(n=>new Date(n.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 n=await this.getTokenInternal(e,t,r);return n?this.decryptStoredToken(n):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 n=t.token;t.token&&this.encryption&&(n=await this.encryptToken(t.token));let o={...r,...n&&{token:n},...t.metadata&&{metadata:{...r.metadata,...t.metadata}},updatedAt:new Date};return this.tokens.set(e,o),this.decryptStoredToken(o)}async deleteToken(e){return this.tokens.delete(e)}async deleteTokenByProviderEmail(e,t,r){let n=await this.getTokenInternal(e,t,r);return n?this.tokens.delete(n.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(([,n])=>e-n.createdAt.getTime()>t).map(([n])=>n);return r.forEach(n=>this.states.delete(n)),r.length}registerProfileFetcher(e,t){this.profileFetchers.set(e,t);}getProfileFetcher(e){return this.profileFetchers.get(e)}getProfileFetchers(){return new Map(this.profileFetchers)}};var B=()=>crypto.randomBytes(32).toString("base64").replace(/[^a-zA-Z0-9]/g,"").substring(0,128),N=s=>crypto.createHash("sha256").update(s).digest("base64url"),$=()=>crypto.randomBytes(16).toString("base64url");var F=class{storage;providerFactory;providers=new Map;providerConfigs=new Map;now;autoRefreshOptions;constructor(e={}){var t,r,n;this.storage=e.storage||new y,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:(n=e.autoRefresh)==null?void 0:n.onRefreshError},e.providers&&Object.entries(e.providers).forEach(([o,i])=>{this.registerProvider(o,i);});}registerProvider(e,t){this.providerConfigs.set(e,t);let r=this.storage.getProfileFetcher(e),n=this.detectProviderType(e,t),o=this.providerFactory.createProvider(n,t,r);this.providers.set(e,o);}async authorize(e){var a;let t=this.providers.get(e.provider);if(!t)throw new Error(`Provider ${e.provider} not found`);let r=$(),n,o;if(((a=this.providerConfigs.get(e.provider))==null?void 0:a.usePKCE)||e.usePKCE){let c=B(),d=N(c);n=t.generateAuthorizationUrl(r,d),o={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 n=t.generateAuthorizationUrl(r),o={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(o),{url:n,state:r}}async handleCallback(e,t){var f,m,g,k,z;let r=await this.storage.getAuthorizationState(t);if(!r)throw new Error("Invalid or expired state");let n=(f=r.metadata)==null?void 0:f.provider;if(!n)throw new Error("Provider not found in authorization state");let o=this.providers.get(n);if(!o)throw new Error(`Provider ${n} not found`);let i=await o.exchangeCodeForToken(e,r.codeVerifier),a=(m=r.metadata)==null?void 0:m.userId,c=(g=r.metadata)==null?void 0:g.email,d=(k=r.metadata)==null?void 0:k.tenant,P=(z=r.metadata)==null?void 0:z.tenantName;if(!a||!c)throw new Error("User ID and email are required in authorization state");let p;if(o.hasProfileFetcher())try{p=await o.fetchProfile(i.accessToken);}catch(M){console.warn("Failed to fetch user profile:",M);}let b=await this.storage.saveToken({provider:n,userId:a,email:(p==null?void 0:p.email)||c,tenant:(p==null?void 0:p.tenant)||d,tenantName:(p==null?void 0:p.tenantName)||P,token:i,metadata:{...r.metadata,profileFetched:!!p}});await this.storage.deleteAuthorizationState(t);let u=this.providerConfigs.get(n);return u!=null&&u.onSuccess&&await u.onSuccess(a,i),{token:b,profile:p}}async getAccessToken(e,t,r={}){return (await this.getValidToken(e,t,r)).accessToken}async getValidToken(e,t,r={}){let n=await this.storage.getToken(e,t,r.tenant);if(!n){let d=r.tenant?` and tenant ${r.tenant}`:"";throw new Error(`No token found for provider ${e}, email ${t}${d}`)}let o=this.providerConfigs.get(e);if(o&&o.refreshable===false||!(r.autoRefresh!==false&&this.isTokenExpired(n.token,r)))return n.token;if(!n.token.refreshToken)throw new Error("Token expired and no refresh token available");let a=this.providers.get(e);if(!a)throw new Error(`Provider ${e} not found`);let c=await a.refreshToken(n.token.refreshToken);return await this.storage.updateToken(n.id,{token:c}),c}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(o=>new Date(o.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 n=new Date(e.expiresAt).getTime();return this.now()+r*1e3>=n}detectProviderType(e,t){var n;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")||(n=t.scopes)!=null&&n.some(o=>o.includes("outlook"))?"outlook":"microsoft":"generic"}async refreshTokensIfNeeded(e){let t=e.map(async r=>{if(this.shouldRefreshToken(r))try{let n=this.providers.get(r.provider);if(!n)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 o=await n.refreshToken(r.token.refreshToken);return await this.storage.updateToken(r.id,{token:o})||r}catch(n){return this.autoRefreshOptions.onRefreshError&&this.autoRefreshOptions.onRefreshError(n,r),console.error(`Failed to refresh token for ${r.provider}:${r.email}:`,n),r}return r});return Promise.all(t)}shouldRefreshToken(e){let t=this.providerConfigs.get(e.provider);if(t&&t.refreshable===false||!e.token.refreshToken)return false;let r=new Date(e.token.expiresAt).getTime(),n=this.now();if(n>=r)return true;let o=this.autoRefreshOptions.refreshBuffer*60*1e3,i=r-o;return n>=i}updateAutoRefreshOptions(e){this.autoRefreshOptions={...this.autoRefreshOptions,...e};}async getTokensAcrossTenants(e,t){return this.queryTokens({provider:e,userId:t})}async getTenants(e,t){let n=(await this.queryTokens({provider:e,email:t})).map(o=>o.tenant).filter(o=>o!==void 0);return [...new Set(n)]}async getTokenByTenantName(e,t,r){return (await this.queryTokens({provider:e,email:t})).find(o=>o.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(n=>({token:n,tenant:n.tenant,tenantName:n.tenantName}))}};var C=(s,e)=>ironSession.sealData(s,{password:e}),R=(s,e)=>ironSession.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
|
+
exports.BaseProfileFetcher=h;exports.GenericOAuth2Provider=x;exports.GenericProfileFetcher=l;exports.GitHubProfileFetcher=w;exports.GoogleProfileFetcher=S;exports.InMemoryStorageAdapter=y;exports.MicrosoftProfileFetcher=U;exports.OAuth2Client=F;exports.OAuth2Provider=T;exports.ProfileFetcherFactory=E;exports.StandardAuthorizationUrlStrategy=v;exports.StandardTokenExchangeStrategy=A;exports.TokenEncryption=I;exports.createCodeChallenge=N;exports.createCodeVerifier=B;exports.generateState=$;exports.seal=C;exports.unseal=R;//# sourceMappingURL=index.cjs.map
|
|
3
3
|
//# sourceMappingURL=index.cjs.map
|