@isaias_pv/custos-sdk 1.3.3 β 1.3.5
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/dist/Custos.d.ts.map +1 -1
- package/dist/index.cjs.js +1 -1
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.esm.js +1 -1
- package/dist/index.esm.js.map +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/index.umd.js.map +1 -1
- package/dist/storage.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/Custos.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Custos.d.ts","sourceRoot":"","sources":["../src/Custos.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAWlF,qBAAa,MAAM;IAClB,OAAO,CAAC,MAAM,CAAyB;IACvC,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,GAAG,CAAY;IACvB,OAAO,CAAC,SAAS,CAAsD;IACvE,OAAO,CAAC,gBAAgB,CAAa;gBAEzB,EAAE,iBAAiB,EAAE,GAAG,MAAM,EAAE,EAAE,YAAY;
|
|
1
|
+
{"version":3,"file":"Custos.d.ts","sourceRoot":"","sources":["../src/Custos.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAWlF,qBAAa,MAAM;IAClB,OAAO,CAAC,MAAM,CAAyB;IACvC,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,GAAG,CAAY;IACvB,OAAO,CAAC,SAAS,CAAsD;IACvE,OAAO,CAAC,gBAAgB,CAAa;gBAEzB,EAAE,iBAAiB,EAAE,GAAG,MAAM,EAAE,EAAE,YAAY;IAoCpD,KAAK,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IA8C/D,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAgBvB,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IAoHrC,OAAO,IAAI,IAAI,GAAG,IAAI;IAItB,cAAc,IAAI,MAAM,GAAG,IAAI;IAI/B,eAAe,IAAI,MAAM,GAAG,IAAI;IAIhC,eAAe,IAAI,OAAO;IAI1B,QAAQ,IAAI,SAAS;IAQf,aAAa,IAAI,OAAO,CAAC,OAAO,CAAC;IAavC,OAAO,CAAC,0BAA0B;IAuBlC,OAAO,CAAC,qBAAqB;IAOvB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAwBnC,EAAE,CAAC,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,GAAG,IAAI;IAOpE,GAAG,CAAC,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,GAAG,IAAI;IAIrE,OAAO,CAAC,IAAI;IAOZ,YAAY,IAAI,IAAI;IAIpB,OAAO,IAAI,IAAI;CAIf"}
|
package/dist/index.cjs.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});const e="custos_";class t{constructor(e=!1){this.storage=e?sessionStorage:localStorage
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});const e="custos_";class t{constructor(e=!1){this.storage=e?sessionStorage:localStorage}setTokens(t){this.storage.setItem(`${e}tokens`,JSON.stringify(t)),this.storage.setItem(`${e}token_issued_at`,Date.now().toString())}getTokens(){const t=this.storage.getItem(`${e}tokens`);return t?JSON.parse(t):null}getTokenIssuedAt(){const t=this.storage.getItem(`${e}token_issued_at`);return t?parseInt(t,10):null}setUser(t){this.storage.setItem(`${e}user`,JSON.stringify(t))}getUser(){const t=this.storage.getItem(`${e}user`);return t?JSON.parse(t):null}setState(t,o){this.storage.setItem(`${e}${t}`,o)}getState(t){return this.storage.getItem(`${e}${t}`)}removeState(t){this.storage.removeItem(`${e}${t}`)}setCodeVerifier(e){this.setState("code_verifier",e)}getCodeVerifier(){return this.getState("code_verifier")}removeCodeVerifier(){this.removeState("code_verifier")}setCodeChallenge(e){this.setState("code_challenge",e)}getCodeChallenge(){return this.getState("code_challenge")}removeCodeChallenge(){this.removeState("code_challenge")}clear(){this.storage.removeItem(`${e}tokens`),this.storage.removeItem(`${e}token_issued_at`),this.storage.removeItem(`${e}user`),this.removeState("oauth_state"),this.removeCodeVerifier(),this.removeCodeChallenge()}hasValidToken(){const e=this.getTokens(),t=this.getTokenIssuedAt();if(!e||!t)return!1;return Date.now()<t+1e3*e.expiresIn}}class o{constructor(e){this.baseUrl=e}async exchangeCodeForTokens(e,t,o,r,s){const i={grant_type:"authorization_code",code:e,client_id:t,redirect_uri:o};r&&(i.code_verifier=r),s&&(i.client_secret=s);const n=await fetch(`${this.baseUrl}/api/v1/auth/token`,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams(i).toString()});if(!n.ok){const e=await n.json().catch(()=>({error:"unknown_error",error_description:"Failed to exchange code for tokens"}));throw new Error(e.error_description||e.error||"Token exchange failed")}const a=await n.json(),c=a.data||a;return{accessToken:c.access_token,refreshToken:c.refresh_token,expiresIn:c.expires_in,tokenType:c.token_type||"Bearer"}}async getUserInfo(e){const t=await fetch(`${this.baseUrl}/api/v1/system/users/profile`,{headers:{Authorization:`Bearer ${e}`}});if(!t.ok)throw new Error("Failed to get user info");const o=await t.json();return o.data||o}async refreshAccessToken(e,t,o){const r={grant_type:"refresh_token",refresh_token:e,client_id:t};o&&(r.client_secret=o);const s=await fetch(`${this.baseUrl}/api/v1/auth/token`,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams(r).toString()});if(!s.ok)throw new Error("Failed to refresh token");const i=await s.json(),n=i.data||i;return{accessToken:n.access_token,refreshToken:n.refresh_token||e,expiresIn:n.expires_in,tokenType:n.token_type||"Bearer"}}async logout(e){await fetch(`${this.baseUrl}/api/v1/auth/revoke`,{method:"POST",headers:{Authorization:`Bearer ${e}`}})}async validateToken(e){try{return(await fetch(`${this.baseUrl}/api/v1/auth/validate`,{headers:{Authorization:`Bearer ${e}`}})).ok}catch{return!1}}}function r(){const e=new Uint8Array(32);return crypto.getRandomValues(e),Array.from(e,e=>e.toString(16).padStart(2,"0")).join("")}async function s(e){const t=(new TextEncoder).encode(e);return function(e){const t=new Uint8Array(e),o=Array.from(t,e=>String.fromCharCode(e)).join("");return btoa(o).replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}(await crypto.subtle.digest("SHA-256",t))}exports.Custos=class{constructor({useSessionStorage:e,...s}){this.tokenExpiryTimer=null;const i=function(e){return e?Array.isArray(e)?e:"string"==typeof e?e.split(" "):["openid","profile","email"]:["openid","profile","email"]}(s.scope);this.config={clientId:s.clientId,clientSecret:s.clientSecret||"",redirectUri:s.redirectUri,apiUrl:s.apiUrl||"https://custos.alimzen.com",scope:i,responseType:s.responseType||"code",state:s.state||r(),usePKCE:!1!==s.usePKCE,codeChallengeMethod:s.codeChallengeMethod||"S256",grantType:s.grantType||"authorization_code",useSessionStorage:e||!1},this.storage=new t(!1),this.api=new o(this.config.apiUrl),this.listeners=new Map,console.log("π§ Custos SDK initialized with localStorage for state persistence"),"undefined"!=typeof window&&(this.handleCallback(),this.setupTokenExpiryMonitoring())}async login(e){const t=this.config.state;console.log("π Starting login flow"),console.log("π Saving state:",t),this.storage.setState("oauth_state",t);const o=this.storage.getState("oauth_state");console.log("β
State saved successfully:",o===t);const r={response_type:this.config.responseType,client_id:this.config.clientId,redirect_uri:this.config.redirectUri,scope:Array.isArray(this.config.scope)?this.config.scope.join(" "):this.config.scope,state:t,...e};if(this.config.usePKCE){const e=function(){const e="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~",t=Math.floor(86*Math.random())+43;let o="";for(let r=0;r<t;r++)o+=e.charAt(Math.floor(66*Math.random()));return o}(),t=await s(e);console.log("π PKCE enabled"),console.log("π Saving code_verifier"),this.storage.setCodeVerifier(e),this.storage.setCodeChallenge(t);const o=this.storage.getCodeVerifier();console.log("β
Code verifier saved:",!!o),r.code_challenge=t,r.code_challenge_method=this.config.codeChallengeMethod}const i=`${this.config.apiUrl}/v1/auth/authorize?${new URLSearchParams(r)}`;console.log("π Redirecting to:",i),window.location.href=i}async logout(){const e=this.storage.getTokens();if(e?.accessToken)try{await this.api.logout(e.accessToken)}catch(e){console.error("Logout error:",e)}this.clearTokenExpiryTimer(),this.storage.clear(),this.emit("logout",null)}async handleCallback(){const e=function(e){const t={};return new URL(e).searchParams.forEach((e,o)=>{t[o]=e}),t}(window.location.href);console.log("π Checking for callback params:",{hasCode:!!e.code,hasError:!!e.error,hasState:!!e.state});const t=e.error;if(t){const o=e.error_description||t;throw console.error("β OAuth error:",t,o),this.emit("error",{error:t,error_description:o}),new Error(o)}const o=e.code;if(!o)return void console.log("βΉοΈ No authorization code found, skipping callback handling");console.log("β
Authorization code found");const r=e.state,s=this.storage.getState("oauth_state");if(console.log("π State validation:"),console.log(" Received state:",r),console.log(" Saved state:",s),console.log(" Match:",r===s),r!==s){if(console.error("β State mismatch!"),console.error(" Expected:",s),console.error(" Received:",r),s)throw this.emit("error",{error:"invalid_state",error_description:"State parameter mismatch"}),new Error("Invalid state parameter");console.warn("β οΈ No saved state found. This might be due to:"),console.warn(" - App opened in new tab/window"),console.warn(" - sessionStorage was cleared"),console.warn(" - App was restarted"),console.warn("π§ Attempting recovery...")}this.storage.removeState("oauth_state");try{const e=this.config.usePKCE&&this.storage.getCodeVerifier()||void 0;if(console.log("π PKCE code_verifier:",!!e),this.config.usePKCE&&!e)throw console.error("β PKCE enabled but no code_verifier found!"),new Error("Code verifier not found. Authentication cannot continue.");console.log("π Exchanging code for tokens...");const t=await this.api.exchangeCodeForTokens(o,this.config.clientId,this.config.redirectUri,e,this.config.clientSecret);console.log("β
Tokens received"),this.storage.setTokens(t),console.log("π€ Fetching user info...");const r=await this.api.getUserInfo(t.accessToken);console.log("β
User info received:",r.email),this.storage.setUser(r),this.config.usePKCE&&(this.storage.removeCodeVerifier(),this.storage.removeCodeChallenge(),console.log("π§Ή PKCE data cleaned up")),this.setupTokenExpiryMonitoring(),this.emit("login",{user:r,tokens:t}),console.log("π Login successful!"),window.history.replaceState({},document.title,window.location.pathname+window.location.hash)}catch(t){throw console.error("β Callback handling error:",t),this.emit("error",t),t}}getUser(){return this.storage.getUser()}getAccessToken(){return this.storage.getTokens()?.accessToken||null}getRefreshToken(){return this.storage.getTokens()?.refreshToken||null}isAuthenticated(){return this.storage.hasValidToken()&&!!this.storage.getUser()}getState(){return{isAuthenticated:this.isAuthenticated(),user:this.getUser(),tokens:this.storage.getTokens()}}async validateToken(){const e=this.getAccessToken();if(!e)return!1;try{return await this.api.validateToken(e)}catch{return!1}}setupTokenExpiryMonitoring(){this.clearTokenExpiryTimer();const e=this.storage.getTokens(),t=this.storage.getTokenIssuedAt();if(!e||!t)return;const o=1e3*(e.expiresIn-300);o>0&&(this.tokenExpiryTimer=setTimeout(async()=>{try{await this.refreshToken()}catch(e){this.emit("token-expired",e),await this.logout()}},o))}clearTokenExpiryTimer(){this.tokenExpiryTimer&&(clearTimeout(this.tokenExpiryTimer),this.tokenExpiryTimer=null)}async refreshToken(){const e=this.storage.getTokens();if(!e?.refreshToken)throw new Error("No refresh token available");try{const t=await this.api.refreshAccessToken(e.refreshToken,this.config.clientId,this.config.clientSecret);this.storage.setTokens(t),this.setupTokenExpiryMonitoring(),this.emit("token-refresh",t)}catch(e){throw this.emit("error",e),e}}on(e,t){this.listeners.has(e)||this.listeners.set(e,new Set),this.listeners.get(e).add(t)}off(e,t){this.listeners.get(e)?.delete(t)}emit(e,t){const o={type:e,data:t};this.listeners.get(e)?.forEach(e=>e(o))}clearStorage(){this.storage.clear()}destroy(){this.clearTokenExpiryTimer(),this.listeners.clear()}};
|
|
2
2
|
//# sourceMappingURL=index.cjs.js.map
|
package/dist/index.cjs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs.js","sources":["../src/storage.ts","../src/api.ts","../src/utils.ts","../src/Custos.ts"],"sourcesContent":["import { AuthTokens, User } from './types';\n\nconst STORAGE_PREFIX = 'custos_';\n\nexport class Storage {\n\tprivate storage: globalThis.Storage;\n\n\tconstructor(useSessionStorage = false) {\n\t\tthis.storage = useSessionStorage ? sessionStorage : localStorage;\n\t\tconsole.log(`Using ${useSessionStorage ? 'sessionStorage' : 'localStorage'}`);\n\t\tconsole.log('Storage initialized:', this.storage);\n\t}\n\n\t// Tokens\n\tsetTokens(tokens: AuthTokens): void {\n\t\tthis.storage.setItem(`${STORAGE_PREFIX}tokens`, JSON.stringify(tokens));\n\t\tthis.storage.setItem(`${STORAGE_PREFIX}token_issued_at`, Date.now().toString());\n\t}\n\n\tgetTokens(): AuthTokens | null {\n\t\tconst data = this.storage.getItem(`${STORAGE_PREFIX}tokens`);\n\t\treturn data ? JSON.parse(data) : null;\n\t}\n\n\tgetTokenIssuedAt(): number | null {\n\t\tconst data = this.storage.getItem(`${STORAGE_PREFIX}token_issued_at`);\n\t\treturn data ? parseInt(data, 10) : null;\n\t}\n\n\t// User\n\tsetUser(user: User): void {\n\t\tthis.storage.setItem(`${STORAGE_PREFIX}user`, JSON.stringify(user));\n\t}\n\n\tgetUser(): User | null {\n\t\tconst data = this.storage.getItem(`${STORAGE_PREFIX}user`);\n\t\treturn data ? JSON.parse(data) : null;\n\t}\n\n\t// State & PKCE\n\tsetState(key: string, value: string): void {\n\t\tconsole.log(`Setting state: ${key} = ${value}`);\n\t\tconsole.log(`Storage before setting state:`, this.storage);\n\t\tthis.storage.setItem(`${STORAGE_PREFIX}${key}`, value);\n\t}\n\n\tgetState(key: string): string | null {\n\t\tconsole.log(`Getting state: ${key}`);\n\t\tconsole.log(`Storage before getting state:`, this.storage);\n\t\treturn this.storage.getItem(`${STORAGE_PREFIX}${key}`);\n\t}\n\n\tremoveState(key: string): void {\n\t\tconsole.log(`Removing state: ${key}`);\n\t\tthis.storage.removeItem(`${STORAGE_PREFIX}${key}`);\n\t}\n\n\t// PKCE specific\n\tsetCodeVerifier(codeVerifier: string): void {\n\t\tthis.setState('code_verifier', codeVerifier);\n\t}\n\n\tgetCodeVerifier(): string | null {\n\t\treturn this.getState('code_verifier');\n\t}\n\n\tremoveCodeVerifier(): void {\n\t\tthis.removeState('code_verifier');\n\t}\n\n\tsetCodeChallenge(codeChallenge: string): void {\n\t\tthis.setState('code_challenge', codeChallenge);\n\t}\n\n\tgetCodeChallenge(): string | null {\n\t\treturn this.getState('code_challenge');\n\t}\n\n\tremoveCodeChallenge(): void {\n\t\tthis.removeState('code_challenge');\n\t}\n\n\t// Clear all\n\tclear(): void {\n\t\tthis.storage.removeItem(`${STORAGE_PREFIX}tokens`);\n\t\tthis.storage.removeItem(`${STORAGE_PREFIX}token_issued_at`);\n\t\tthis.storage.removeItem(`${STORAGE_PREFIX}user`);\n\t\tthis.removeState('oauth_state');\n\t\tthis.removeCodeVerifier();\n\t\tthis.removeCodeChallenge();\n\t}\n\n\t// Validation\n\thasValidToken(): boolean {\n\t\tconst tokens = this.getTokens();\n\t\tconst issuedAt = this.getTokenIssuedAt();\n\n\t\tif (!tokens || !issuedAt) return false;\n\n\t\tconst now = Date.now();\n\t\tconst expirationTime = issuedAt + tokens.expiresIn * 1000;\n\n\t\treturn now < expirationTime;\n\t}\n}\n","import { AuthTokens, User } from './types';\n\nexport class ApiClient {\n\tprivate baseUrl: string;\n\n\tconstructor(baseUrl: string) {\n\t\tthis.baseUrl = baseUrl;\n\t}\n\n\tasync exchangeCodeForTokens(\n\t\tcode: string,\n\t\tclientId: string,\n\t\tredirectUri: string,\n\t\tcodeVerifier?: string,\n\t\tclientSecret?: string\n\t): Promise<AuthTokens> {\n\t\tconst body: Record<string, string> = {\n\t\t\tgrant_type: 'authorization_code',\n\t\t\tcode,\n\t\t\tclient_id: clientId,\n\t\t\tredirect_uri: redirectUri,\n\t\t};\n\n\t\t// Add PKCE code_verifier if present\n\t\tif (codeVerifier) {\n\t\t\tbody.code_verifier = codeVerifier;\n\t\t}\n\n\t\t// Add client_secret if present (for confidential clients)\n\t\tif (clientSecret) {\n\t\t\tbody.client_secret = clientSecret;\n\t\t}\n\n\t\tconst response = await fetch(`${this.baseUrl}/api/v1/auth/token`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: {\n\t\t\t\t'Content-Type': 'application/x-www-form-urlencoded',\n\t\t\t},\n\t\t\tbody: new URLSearchParams(body).toString(),\n\t\t});\n\n\t\tif (!response.ok) {\n\t\t\tconst errorData = await response.json().catch(() => ({\n\t\t\t\terror: 'unknown_error',\n\t\t\t\terror_description: 'Failed to exchange code for tokens'\n\t\t\t}));\n\n\t\t\tthrow new Error(errorData.error_description || errorData.error || 'Token exchange failed');\n\t\t}\n\n\t\tconst result = await response.json();\n\t\tconst data = result.data || result; // Support both {data: {...}} and direct response\n\n\t\treturn {\n\t\t\taccessToken: data.access_token,\n\t\t\trefreshToken: data.refresh_token,\n\t\t\texpiresIn: data.expires_in,\n\t\t\ttokenType: data.token_type || 'Bearer',\n\t\t};\n\t}\n\n\tasync getUserInfo(accessToken: string): Promise<User> {\n\t\tconst response = await fetch(`${this.baseUrl}/api/v1/system/users/profile`, {\n\t\t\theaders: {\n\t\t\t\tAuthorization: `Bearer ${accessToken}`,\n\t\t\t},\n\t\t});\n\n\t\tif (!response.ok) {\n\t\t\tthrow new Error('Failed to get user info');\n\t\t}\n\n\t\tconst result = await response.json();\n\t\treturn result.data || result;\n\t}\n\n\tasync refreshAccessToken(\n\t\trefreshToken: string,\n\t\tclientId: string,\n\t\tclientSecret?: string\n\t): Promise<AuthTokens> {\n\t\tconst body: Record<string, string> = {\n\t\t\tgrant_type: 'refresh_token',\n\t\t\trefresh_token: refreshToken,\n\t\t\tclient_id: clientId,\n\t\t};\n\n\t\tif (clientSecret) {\n\t\t\tbody.client_secret = clientSecret;\n\t\t}\n\n\t\tconst response = await fetch(`${this.baseUrl}/api/v1/auth/token`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: {\n\t\t\t\t'Content-Type': 'application/x-www-form-urlencoded',\n\t\t\t},\n\t\t\tbody: new URLSearchParams(body).toString(),\n\t\t});\n\n\t\tif (!response.ok) {\n\t\t\tthrow new Error('Failed to refresh token');\n\t\t}\n\n\t\tconst result = await response.json();\n\t\tconst data = result.data || result;\n\n\t\treturn {\n\t\t\taccessToken: data.access_token,\n\t\t\trefreshToken: data.refresh_token || refreshToken, // Keep old refresh token if not provided\n\t\t\texpiresIn: data.expires_in,\n\t\t\ttokenType: data.token_type || 'Bearer',\n\t\t};\n\t}\n\n\tasync logout(accessToken: string): Promise<void> {\n\t\tawait fetch(`${this.baseUrl}/api/v1/auth/revoke`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: {\n\t\t\t\tAuthorization: `Bearer ${accessToken}`,\n\t\t\t},\n\t\t});\n\t}\n\n\tasync validateToken(accessToken: string): Promise<boolean> {\n\t\ttry {\n\t\t\tconst response = await fetch(`${this.baseUrl}/api/v1/auth/validate`, {\n\t\t\t\theaders: {\n\t\t\t\t\tAuthorization: `Bearer ${accessToken}`,\n\t\t\t\t},\n\t\t\t});\n\t\t\treturn response.ok;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n}\n","export function generateState(): string {\n\tconst array = new Uint8Array(32);\n\tcrypto.getRandomValues(array);\n\treturn Array.from(array, (byte) => byte.toString(16).padStart(2, '0')).join('');\n}\n\nexport function parseQueryString(url: string): Record<string, string> {\n\tconst params: Record<string, string> = {};\n\tconst searchParams = new URL(url).searchParams;\n\tsearchParams.forEach((value, key) => {\n\t\tparams[key] = value;\n\t});\n\treturn params;\n}\n\nexport function isTokenExpired(expiresIn: number, issuedAt: number): boolean {\n\tconst now = Date.now();\n\tconst expirationTime = issuedAt + expiresIn * 1000;\n\t// Refresh 5 minutes before expiration\n\treturn now >= expirationTime - 5 * 60 * 1000;\n}\n\n// PKCE utilities\nexport function generateCodeVerifier(): string {\n\tconst charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~';\n\tconst length = Math.floor(Math.random() * 86) + 43; // 43-128 characters\n\tlet verifier = '';\n\tfor (let i = 0; i < length; i++) {\n\t\tverifier += charset.charAt(Math.floor(Math.random() * charset.length));\n\t}\n\treturn verifier;\n}\n\nexport async function generateCodeChallenge(codeVerifier: string): Promise<string> {\n\tconst encoder = new TextEncoder();\n\tconst data = encoder.encode(codeVerifier);\n\tconst digest = await crypto.subtle.digest('SHA-256', data);\n\treturn base64UrlEncode(digest);\n}\n\nfunction base64UrlEncode(digest: ArrayBuffer): string {\n\tconst bytes = new Uint8Array(digest);\n\tconst binary = Array.from(bytes, (byte) => String.fromCharCode(byte)).join('');\n\treturn btoa(binary)\n\t\t.replace(/\\+/g, '-')\n\t\t.replace(/\\//g, '_')\n\t\t.replace(/=/g, '');\n}\n\nexport function normalizeScope(scope?: string | string[]): string[] {\n\tif (!scope) return ['openid', 'profile', 'email'];\n\tif (Array.isArray(scope)) return scope;\n\tif (typeof scope === 'string') return scope.split(' ');\n\treturn ['openid', 'profile', 'email'];\n}\n","import { CustosConfig, User, AuthState, AuthEvent, AuthEventType } from './types';\nimport { Storage } from './storage';\nimport { ApiClient } from './api';\nimport {\n\tgenerateState,\n\tparseQueryString,\n\tgenerateCodeVerifier,\n\tgenerateCodeChallenge,\n\tnormalizeScope\n} from './utils';\n\nexport class Custos {\n\tprivate config: Required<CustosConfig>;\n\tprivate storage: Storage;\n\tprivate api: ApiClient;\n\tprivate listeners: Map<AuthEventType, Set<(event: AuthEvent) => void>>;\n\tprivate tokenExpiryTimer: any = null;\n\n\tconstructor({ useSessionStorage, ...config }: CustosConfig) {\n\t\t// Normalize scope\n\t\tconst scope = normalizeScope(config.scope);\n\n\t\tthis.config = {\n\t\t\tclientId: config.clientId,\n\t\t\tclientSecret: config.clientSecret || '',\n\t\t\tredirectUri: config.redirectUri,\n\t\t\tapiUrl: config.apiUrl || 'https://custos.alimzen.com',\n\t\t\tscope,\n\t\t\tresponseType: config.responseType || 'code',\n\t\t\tstate: config.state || generateState(),\n\t\t\tusePKCE: config.usePKCE !== false, // Default to true\n\t\t\tcodeChallengeMethod: config.codeChallengeMethod || 'S256',\n\t\t\tgrantType: config.grantType || 'authorization_code',\n\t\t\tuseSessionStorage: useSessionStorage || false\n\t\t};\n\n\t\tthis.storage = new Storage(useSessionStorage); // Use sessionStorage for better security\n\t\tthis.api = new ApiClient(this.config.apiUrl);\n\t\tthis.listeners = new Map();\n\n\t\t// Handle callback automatically\n\t\tif (typeof window !== 'undefined') {\n\t\t\tthis.handleCallback();\n\t\t\tthis.setupTokenExpiryMonitoring();\n\t\t}\n\t}\n\n\t// ==================== Authentication Methods ====================\n\n\tasync login(additionalParams?: Record<string, string>): Promise<void> {\n\t\tconst state = this.config.state;\n\t\tthis.storage.setState('oauth_state', state);\n\n\t\tconst params: Record<string, string> = {\n\t\t\tresponse_type: this.config.responseType,\n\t\t\tclient_id: this.config.clientId,\n\t\t\tredirect_uri: this.config.redirectUri,\n\t\t\tscope: Array.isArray(this.config.scope) ? this.config.scope.join(' ') : this.config.scope,\n\t\t\tstate,\n\t\t\t...additionalParams,\n\t\t};\n\n\t\t// Add PKCE if enabled\n\t\tif (this.config.usePKCE) {\n\t\t\tconst codeVerifier = generateCodeVerifier();\n\t\t\tconst codeChallenge = await generateCodeChallenge(codeVerifier);\n\n\t\t\tthis.storage.setCodeVerifier(codeVerifier);\n\t\t\tthis.storage.setCodeChallenge(codeChallenge);\n\n\t\t\tparams.code_challenge = codeChallenge;\n\t\t\tparams.code_challenge_method = this.config.codeChallengeMethod;\n\t\t}\n\n\t\tconst authUrl = `${this.config.apiUrl}/v1/auth/authorize?${new URLSearchParams(params)}`;\n\t\twindow.location.href = authUrl;\n\t}\n\n\tasync logout(): Promise<void> {\n\t\tconst tokens = this.storage.getTokens();\n\n\t\tif (tokens?.accessToken) {\n\t\t\ttry {\n\t\t\t\tawait this.api.logout(tokens.accessToken);\n\t\t\t} catch (error) {\n\t\t\t\tthrow new Error(`Logout error: ${error}`);\n\t\t\t}\n\t\t}\n\n\t\tthis.clearTokenExpiryTimer();\n\t\tthis.storage.clear();\n\t\tthis.emit('logout', null);\n\t}\n\n\tasync handleCallback(): Promise<void> {\n\t\tconst params = parseQueryString(window.location.href);\n\n\t\t// Check for errors\n\t\tconst error = params.error;\n\t\tif (error) {\n\t\t\tconst errorDescription = params.error_description || error;\n\t\t\tthis.emit('error', { error, error_description: errorDescription });\n\t\t\tthrow new Error(errorDescription);\n\t\t}\n\n\t\t// Check for authorization code\n\t\tconst code = params.code;\n\t\tif (!code) return;\n\n\t\t// Validate state\n\t\tconst state = params.state;\n\t\tconst savedState = this.storage.getState('oauth_state');\n\t\tif (state !== savedState) {\n\t\t\tthis.emit('error', { error: 'invalid_state', error_description: 'State parameter mismatch' });\n\t\t\tthrow new Error('Invalid state parameter');\n\t\t}\n\n\t\tthis.storage.removeState('oauth_state');\n\n\t\ttry {\n\t\t\t// Get code_verifier if using PKCE\n\t\t\tconst codeVerifier = this.config.usePKCE ? this.storage.getCodeVerifier() || undefined : undefined;\n\n\t\t\t// Exchange code for tokens\n\t\t\tconst tokens = await this.api.exchangeCodeForTokens(\n\t\t\t\tcode,\n\t\t\t\tthis.config.clientId,\n\t\t\t\tthis.config.redirectUri,\n\t\t\t\tcodeVerifier,\n\t\t\t\tthis.config.clientSecret\n\t\t\t);\n\n\t\t\tthis.storage.setTokens(tokens);\n\n\t\t\t// Get user info\n\t\t\tconst user = await this.api.getUserInfo(tokens.accessToken);\n\t\t\tthis.storage.setUser(user);\n\n\t\t\t// Clean up PKCE data\n\t\t\tif (this.config.usePKCE) {\n\t\t\t\tthis.storage.removeCodeVerifier();\n\t\t\t\tthis.storage.removeCodeChallenge();\n\t\t\t}\n\n\t\t\t// Setup token expiry monitoring\n\t\t\tthis.setupTokenExpiryMonitoring();\n\n\t\t\tthis.emit('login', { user, tokens });\n\n\t\t\t// Clean URL (remove query params)\n\t\t\twindow.history.replaceState({}, document.title, window.location.pathname + window.location.hash);\n\t\t} catch (error) {\n\t\t\tthis.emit('error', error);\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t// ==================== User Methods ====================\n\n\tgetUser(): User | null {\n\t\treturn this.storage.getUser();\n\t}\n\n\tgetAccessToken(): string | null {\n\t\treturn this.storage.getTokens()?.accessToken || null;\n\t}\n\n\tgetRefreshToken(): string | null {\n\t\treturn this.storage.getTokens()?.refreshToken || null;\n\t}\n\n\tisAuthenticated(): boolean {\n\t\treturn this.storage.hasValidToken() && !!this.storage.getUser();\n\t}\n\n\tgetState(): AuthState {\n\t\treturn {\n\t\t\tisAuthenticated: this.isAuthenticated(),\n\t\t\tuser: this.getUser(),\n\t\t\ttokens: this.storage.getTokens(),\n\t\t};\n\t}\n\n\tasync validateToken(): Promise<boolean> {\n\t\tconst accessToken = this.getAccessToken();\n\t\tif (!accessToken) return false;\n\n\t\ttry {\n\t\t\treturn await this.api.validateToken(accessToken);\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t// ==================== Token Refresh ====================\n\n\tprivate setupTokenExpiryMonitoring(): void {\n\t\tthis.clearTokenExpiryTimer();\n\n\t\tconst tokens = this.storage.getTokens();\n\t\tconst issuedAt = this.storage.getTokenIssuedAt();\n\n\t\tif (!tokens || !issuedAt) return;\n\n\t\t// Refresh 5 minutes before expiry\n\t\tconst timeUntilRefresh = (tokens.expiresIn - 300) * 1000; // 5 min buffer\n\n\t\tif (timeUntilRefresh > 0) {\n\t\t\tthis.tokenExpiryTimer = setTimeout(async () => {\n\t\t\t\ttry {\n\t\t\t\t\tawait this.refreshToken();\n\t\t\t\t} catch (error) {\n\t\t\t\t\tthis.emit('token-expired', error);\n\t\t\t\t\tawait this.logout();\n\t\t\t\t}\n\t\t\t}, timeUntilRefresh);\n\t\t}\n\t}\n\n\tprivate clearTokenExpiryTimer(): void {\n\t\tif (this.tokenExpiryTimer) {\n\t\t\tclearTimeout(this.tokenExpiryTimer);\n\t\t\tthis.tokenExpiryTimer = null;\n\t\t}\n\t}\n\n\tasync refreshToken(): Promise<void> {\n\t\tconst tokens = this.storage.getTokens();\n\t\tif (!tokens?.refreshToken) {\n\t\t\tthrow new Error('No refresh token available');\n\t\t}\n\n\t\ttry {\n\t\t\tconst newTokens = await this.api.refreshAccessToken(\n\t\t\t\ttokens.refreshToken,\n\t\t\t\tthis.config.clientId,\n\t\t\t\tthis.config.clientSecret\n\t\t\t);\n\n\t\t\tthis.storage.setTokens(newTokens);\n\t\t\tthis.setupTokenExpiryMonitoring();\n\t\t\tthis.emit('token-refresh', newTokens);\n\t\t} catch (error) {\n\t\t\tthis.emit('error', error);\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t// ==================== Event Handling ====================\n\n\ton(event: AuthEventType, callback: (event: AuthEvent) => void): void {\n\t\tif (!this.listeners.has(event)) {\n\t\t\tthis.listeners.set(event, new Set());\n\t\t}\n\t\tthis.listeners.get(event)!.add(callback);\n\t}\n\n\toff(event: AuthEventType, callback: (event: AuthEvent) => void): void {\n\t\tthis.listeners.get(event)?.delete(callback);\n\t}\n\n\tprivate emit(type: AuthEventType, data?: any): void {\n\t\tconst event: AuthEvent = { type, data };\n\t\tthis.listeners.get(type)?.forEach((callback) => callback(event));\n\t}\n\n\t// ==================== Utility Methods ====================\n\n\tclearStorage(): void {\n\t\tthis.storage.clear();\n\t}\n\n\tdestroy(): void {\n\t\tthis.clearTokenExpiryTimer();\n\t\tthis.listeners.clear();\n\t}\n}\n"],"names":["STORAGE_PREFIX","Storage","constructor","useSessionStorage","this","storage","sessionStorage","localStorage","console","log","setTokens","tokens","setItem","JSON","stringify","Date","now","toString","getTokens","data","getItem","parse","getTokenIssuedAt","parseInt","setUser","user","getUser","setState","key","value","getState","removeState","removeItem","setCodeVerifier","codeVerifier","getCodeVerifier","removeCodeVerifier","setCodeChallenge","codeChallenge","getCodeChallenge","removeCodeChallenge","clear","hasValidToken","issuedAt","expiresIn","ApiClient","baseUrl","exchangeCodeForTokens","code","clientId","redirectUri","clientSecret","body","grant_type","client_id","redirect_uri","code_verifier","client_secret","response","fetch","method","headers","URLSearchParams","ok","errorData","json","catch","error","error_description","Error","result","accessToken","access_token","refreshToken","refresh_token","expires_in","tokenType","token_type","getUserInfo","Authorization","refreshAccessToken","logout","validateToken","generateState","array","Uint8Array","crypto","getRandomValues","Array","from","byte","padStart","join","async","generateCodeChallenge","TextEncoder","encode","digest","bytes","binary","String","fromCharCode","btoa","replace","base64UrlEncode","subtle","config","tokenExpiryTimer","scope","isArray","split","normalizeScope","apiUrl","responseType","state","usePKCE","codeChallengeMethod","grantType","api","listeners","Map","window","handleCallback","setupTokenExpiryMonitoring","login","additionalParams","params","response_type","charset","length","Math","floor","random","verifier","i","charAt","generateCodeVerifier","code_challenge","code_challenge_method","authUrl","location","href","clearTokenExpiryTimer","emit","url","URL","searchParams","forEach","parseQueryString","errorDescription","undefined","history","replaceState","document","title","pathname","hash","getAccessToken","getRefreshToken","isAuthenticated","timeUntilRefresh","setTimeout","clearTimeout","newTokens","on","event","callback","has","set","Set","get","add","off","delete","type","clearStorage","destroy"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs.js","sources":["../src/storage.ts","../src/api.ts","../src/utils.ts","../src/Custos.ts"],"sourcesContent":["import { AuthTokens, User } from './types';\n\nconst STORAGE_PREFIX = 'custos_';\n\nexport class Storage {\n\tprivate storage: globalThis.Storage;\n\n\tconstructor(useSessionStorage = false) {\n\t\tthis.storage = useSessionStorage ? sessionStorage : localStorage;\n\t}\n\n\t// Tokens\n\tsetTokens(tokens: AuthTokens): void {\n\t\tthis.storage.setItem(`${STORAGE_PREFIX}tokens`, JSON.stringify(tokens));\n\t\tthis.storage.setItem(`${STORAGE_PREFIX}token_issued_at`, Date.now().toString());\n\t}\n\n\tgetTokens(): AuthTokens | null {\n\t\tconst data = this.storage.getItem(`${STORAGE_PREFIX}tokens`);\n\t\treturn data ? JSON.parse(data) : null;\n\t}\n\n\tgetTokenIssuedAt(): number | null {\n\t\tconst data = this.storage.getItem(`${STORAGE_PREFIX}token_issued_at`);\n\t\treturn data ? parseInt(data, 10) : null;\n\t}\n\n\t// User\n\tsetUser(user: User): void {\n\t\tthis.storage.setItem(`${STORAGE_PREFIX}user`, JSON.stringify(user));\n\t}\n\n\tgetUser(): User | null {\n\t\tconst data = this.storage.getItem(`${STORAGE_PREFIX}user`);\n\t\treturn data ? JSON.parse(data) : null;\n\t}\n\n\t// State & PKCE\n\tsetState(key: string, value: string): void {\n\t\tthis.storage.setItem(`${STORAGE_PREFIX}${key}`, value);\n\t}\n\n\tgetState(key: string): string | null {\n\t\treturn this.storage.getItem(`${STORAGE_PREFIX}${key}`);\n\t}\n\n\tremoveState(key: string): void {\n\t\tthis.storage.removeItem(`${STORAGE_PREFIX}${key}`);\n\t}\n\n\t// PKCE specific\n\tsetCodeVerifier(codeVerifier: string): void {\n\t\tthis.setState('code_verifier', codeVerifier);\n\t}\n\n\tgetCodeVerifier(): string | null {\n\t\treturn this.getState('code_verifier');\n\t}\n\n\tremoveCodeVerifier(): void {\n\t\tthis.removeState('code_verifier');\n\t}\n\n\tsetCodeChallenge(codeChallenge: string): void {\n\t\tthis.setState('code_challenge', codeChallenge);\n\t}\n\n\tgetCodeChallenge(): string | null {\n\t\treturn this.getState('code_challenge');\n\t}\n\n\tremoveCodeChallenge(): void {\n\t\tthis.removeState('code_challenge');\n\t}\n\n\t// Clear all\n\tclear(): void {\n\t\tthis.storage.removeItem(`${STORAGE_PREFIX}tokens`);\n\t\tthis.storage.removeItem(`${STORAGE_PREFIX}token_issued_at`);\n\t\tthis.storage.removeItem(`${STORAGE_PREFIX}user`);\n\t\tthis.removeState('oauth_state');\n\t\tthis.removeCodeVerifier();\n\t\tthis.removeCodeChallenge();\n\t}\n\n\t// Validation\n\thasValidToken(): boolean {\n\t\tconst tokens = this.getTokens();\n\t\tconst issuedAt = this.getTokenIssuedAt();\n\n\t\tif (!tokens || !issuedAt) return false;\n\n\t\tconst now = Date.now();\n\t\tconst expirationTime = issuedAt + tokens.expiresIn * 1000;\n\n\t\treturn now < expirationTime;\n\t}\n}\n","import { AuthTokens, User } from './types';\n\nexport class ApiClient {\n\tprivate baseUrl: string;\n\n\tconstructor(baseUrl: string) {\n\t\tthis.baseUrl = baseUrl;\n\t}\n\n\tasync exchangeCodeForTokens(\n\t\tcode: string,\n\t\tclientId: string,\n\t\tredirectUri: string,\n\t\tcodeVerifier?: string,\n\t\tclientSecret?: string\n\t): Promise<AuthTokens> {\n\t\tconst body: Record<string, string> = {\n\t\t\tgrant_type: 'authorization_code',\n\t\t\tcode,\n\t\t\tclient_id: clientId,\n\t\t\tredirect_uri: redirectUri,\n\t\t};\n\n\t\t// Add PKCE code_verifier if present\n\t\tif (codeVerifier) {\n\t\t\tbody.code_verifier = codeVerifier;\n\t\t}\n\n\t\t// Add client_secret if present (for confidential clients)\n\t\tif (clientSecret) {\n\t\t\tbody.client_secret = clientSecret;\n\t\t}\n\n\t\tconst response = await fetch(`${this.baseUrl}/api/v1/auth/token`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: {\n\t\t\t\t'Content-Type': 'application/x-www-form-urlencoded',\n\t\t\t},\n\t\t\tbody: new URLSearchParams(body).toString(),\n\t\t});\n\n\t\tif (!response.ok) {\n\t\t\tconst errorData = await response.json().catch(() => ({\n\t\t\t\terror: 'unknown_error',\n\t\t\t\terror_description: 'Failed to exchange code for tokens'\n\t\t\t}));\n\n\t\t\tthrow new Error(errorData.error_description || errorData.error || 'Token exchange failed');\n\t\t}\n\n\t\tconst result = await response.json();\n\t\tconst data = result.data || result; // Support both {data: {...}} and direct response\n\n\t\treturn {\n\t\t\taccessToken: data.access_token,\n\t\t\trefreshToken: data.refresh_token,\n\t\t\texpiresIn: data.expires_in,\n\t\t\ttokenType: data.token_type || 'Bearer',\n\t\t};\n\t}\n\n\tasync getUserInfo(accessToken: string): Promise<User> {\n\t\tconst response = await fetch(`${this.baseUrl}/api/v1/system/users/profile`, {\n\t\t\theaders: {\n\t\t\t\tAuthorization: `Bearer ${accessToken}`,\n\t\t\t},\n\t\t});\n\n\t\tif (!response.ok) {\n\t\t\tthrow new Error('Failed to get user info');\n\t\t}\n\n\t\tconst result = await response.json();\n\t\treturn result.data || result;\n\t}\n\n\tasync refreshAccessToken(\n\t\trefreshToken: string,\n\t\tclientId: string,\n\t\tclientSecret?: string\n\t): Promise<AuthTokens> {\n\t\tconst body: Record<string, string> = {\n\t\t\tgrant_type: 'refresh_token',\n\t\t\trefresh_token: refreshToken,\n\t\t\tclient_id: clientId,\n\t\t};\n\n\t\tif (clientSecret) {\n\t\t\tbody.client_secret = clientSecret;\n\t\t}\n\n\t\tconst response = await fetch(`${this.baseUrl}/api/v1/auth/token`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: {\n\t\t\t\t'Content-Type': 'application/x-www-form-urlencoded',\n\t\t\t},\n\t\t\tbody: new URLSearchParams(body).toString(),\n\t\t});\n\n\t\tif (!response.ok) {\n\t\t\tthrow new Error('Failed to refresh token');\n\t\t}\n\n\t\tconst result = await response.json();\n\t\tconst data = result.data || result;\n\n\t\treturn {\n\t\t\taccessToken: data.access_token,\n\t\t\trefreshToken: data.refresh_token || refreshToken, // Keep old refresh token if not provided\n\t\t\texpiresIn: data.expires_in,\n\t\t\ttokenType: data.token_type || 'Bearer',\n\t\t};\n\t}\n\n\tasync logout(accessToken: string): Promise<void> {\n\t\tawait fetch(`${this.baseUrl}/api/v1/auth/revoke`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: {\n\t\t\t\tAuthorization: `Bearer ${accessToken}`,\n\t\t\t},\n\t\t});\n\t}\n\n\tasync validateToken(accessToken: string): Promise<boolean> {\n\t\ttry {\n\t\t\tconst response = await fetch(`${this.baseUrl}/api/v1/auth/validate`, {\n\t\t\t\theaders: {\n\t\t\t\t\tAuthorization: `Bearer ${accessToken}`,\n\t\t\t\t},\n\t\t\t});\n\t\t\treturn response.ok;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n}\n","export function generateState(): string {\n\tconst array = new Uint8Array(32);\n\tcrypto.getRandomValues(array);\n\treturn Array.from(array, (byte) => byte.toString(16).padStart(2, '0')).join('');\n}\n\nexport function parseQueryString(url: string): Record<string, string> {\n\tconst params: Record<string, string> = {};\n\tconst searchParams = new URL(url).searchParams;\n\tsearchParams.forEach((value, key) => {\n\t\tparams[key] = value;\n\t});\n\treturn params;\n}\n\nexport function isTokenExpired(expiresIn: number, issuedAt: number): boolean {\n\tconst now = Date.now();\n\tconst expirationTime = issuedAt + expiresIn * 1000;\n\t// Refresh 5 minutes before expiration\n\treturn now >= expirationTime - 5 * 60 * 1000;\n}\n\n// PKCE utilities\nexport function generateCodeVerifier(): string {\n\tconst charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~';\n\tconst length = Math.floor(Math.random() * 86) + 43; // 43-128 characters\n\tlet verifier = '';\n\tfor (let i = 0; i < length; i++) {\n\t\tverifier += charset.charAt(Math.floor(Math.random() * charset.length));\n\t}\n\treturn verifier;\n}\n\nexport async function generateCodeChallenge(codeVerifier: string): Promise<string> {\n\tconst encoder = new TextEncoder();\n\tconst data = encoder.encode(codeVerifier);\n\tconst digest = await crypto.subtle.digest('SHA-256', data);\n\treturn base64UrlEncode(digest);\n}\n\nfunction base64UrlEncode(digest: ArrayBuffer): string {\n\tconst bytes = new Uint8Array(digest);\n\tconst binary = Array.from(bytes, (byte) => String.fromCharCode(byte)).join('');\n\treturn btoa(binary)\n\t\t.replace(/\\+/g, '-')\n\t\t.replace(/\\//g, '_')\n\t\t.replace(/=/g, '');\n}\n\nexport function normalizeScope(scope?: string | string[]): string[] {\n\tif (!scope) return ['openid', 'profile', 'email'];\n\tif (Array.isArray(scope)) return scope;\n\tif (typeof scope === 'string') return scope.split(' ');\n\treturn ['openid', 'profile', 'email'];\n}\n","import { CustosConfig, User, AuthState, AuthEvent, AuthEventType } from './types';\nimport { Storage } from './storage';\nimport { ApiClient } from './api';\nimport {\n\tgenerateState,\n\tparseQueryString,\n\tgenerateCodeVerifier,\n\tgenerateCodeChallenge,\n\tnormalizeScope\n} from './utils';\n\nexport class Custos {\n\tprivate config: Required<CustosConfig>;\n\tprivate storage: Storage;\n\tprivate api: ApiClient;\n\tprivate listeners: Map<AuthEventType, Set<(event: AuthEvent) => void>>;\n\tprivate tokenExpiryTimer: any = null;\n\n\tconstructor({ useSessionStorage, ...config }: CustosConfig) {\n\t\t// Normalize scope\n\t\tconst scope = normalizeScope(config.scope);\n\n\t\tthis.config = {\n\t\t\tclientId: config.clientId,\n\t\t\tclientSecret: config.clientSecret || '',\n\t\t\tredirectUri: config.redirectUri,\n\t\t\tapiUrl: config.apiUrl || 'https://custos.alimzen.com',\n\t\t\tscope,\n\t\t\tresponseType: config.responseType || 'code',\n\t\t\tstate: config.state || generateState(),\n\t\t\tusePKCE: config.usePKCE !== false, // Default to true\n\t\t\tcodeChallengeMethod: config.codeChallengeMethod || 'S256',\n\t\t\tgrantType: config.grantType || 'authorization_code',\n\t\t\tuseSessionStorage: useSessionStorage || false\n\t\t};\n\n\t\t// π₯ FIX: Para apps nativas o casos donde se pierde sessionStorage,\n\t\t// usar localStorage para state y PKCE\n\t\tconst storageForState = useSessionStorage ? sessionStorage : localStorage;\n\t\tthis.storage = new Storage(false); // β
SIEMPRE usar localStorage para persistencia\n\t\tthis.api = new ApiClient(this.config.apiUrl);\n\t\tthis.listeners = new Map();\n\n\t\tconsole.log('π§ Custos SDK initialized with localStorage for state persistence');\n\n\t\t// Handle callback automatically\n\t\tif (typeof window !== 'undefined') {\n\t\t\tthis.handleCallback();\n\t\t\tthis.setupTokenExpiryMonitoring();\n\t\t}\n\t}\n\n\t// ==================== Authentication Methods ====================\n\n\tasync login(additionalParams?: Record<string, string>): Promise<void> {\n\t\tconst state = this.config.state;\n\t\t\n\t\tconsole.log('π Starting login flow');\n\t\tconsole.log('π Saving state:', state);\n\t\t\n\t\tthis.storage.setState('oauth_state', state);\n\t\t\n\t\t// Verificar que se guardΓ³ correctamente\n\t\tconst savedState = this.storage.getState('oauth_state');\n\t\tconsole.log('β
State saved successfully:', savedState === state);\n\n\t\tconst params: Record<string, string> = {\n\t\t\tresponse_type: this.config.responseType,\n\t\t\tclient_id: this.config.clientId,\n\t\t\tredirect_uri: this.config.redirectUri,\n\t\t\tscope: Array.isArray(this.config.scope) ? this.config.scope.join(' ') : this.config.scope,\n\t\t\tstate,\n\t\t\t...additionalParams,\n\t\t};\n\n\t\t// Add PKCE if enabled\n\t\tif (this.config.usePKCE) {\n\t\t\tconst codeVerifier = generateCodeVerifier();\n\t\t\tconst codeChallenge = await generateCodeChallenge(codeVerifier);\n\n\t\t\tconsole.log('π PKCE enabled');\n\t\t\tconsole.log('π Saving code_verifier');\n\t\t\t\n\t\t\tthis.storage.setCodeVerifier(codeVerifier);\n\t\t\tthis.storage.setCodeChallenge(codeChallenge);\n\t\t\t\n\t\t\t// Verificar que se guardΓ³\n\t\t\tconst savedVerifier = this.storage.getCodeVerifier();\n\t\t\tconsole.log('β
Code verifier saved:', !!savedVerifier);\n\n\t\t\tparams.code_challenge = codeChallenge;\n\t\t\tparams.code_challenge_method = this.config.codeChallengeMethod;\n\t\t}\n\n\t\tconst authUrl = `${this.config.apiUrl}/v1/auth/authorize?${new URLSearchParams(params)}`;\n\t\tconsole.log('π Redirecting to:', authUrl);\n\t\t\n\t\twindow.location.href = authUrl;\n\t}\n\n\tasync logout(): Promise<void> {\n\t\tconst tokens = this.storage.getTokens();\n\n\t\tif (tokens?.accessToken) {\n\t\t\ttry {\n\t\t\t\tawait this.api.logout(tokens.accessToken);\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error('Logout error:', error);\n\t\t\t}\n\t\t}\n\n\t\tthis.clearTokenExpiryTimer();\n\t\tthis.storage.clear();\n\t\tthis.emit('logout', null);\n\t}\n\n\tasync handleCallback(): Promise<void> {\n\t\tconst params = parseQueryString(window.location.href);\n\n\t\tconsole.log('π Checking for callback params:', {\n\t\t\thasCode: !!params.code,\n\t\t\thasError: !!params.error,\n\t\t\thasState: !!params.state\n\t\t});\n\n\t\t// Check for errors\n\t\tconst error = params.error;\n\t\tif (error) {\n\t\t\tconst errorDescription = params.error_description || error;\n\t\t\tconsole.error('β OAuth error:', error, errorDescription);\n\t\t\tthis.emit('error', { error, error_description: errorDescription });\n\t\t\tthrow new Error(errorDescription);\n\t\t}\n\n\t\t// Check for authorization code\n\t\tconst code = params.code;\n\t\tif (!code) {\n\t\t\tconsole.log('βΉοΈ No authorization code found, skipping callback handling');\n\t\t\treturn;\n\t\t}\n\n\t\tconsole.log('β
Authorization code found');\n\n\t\t// Validate state\n\t\tconst state = params.state;\n\t\tconst savedState = this.storage.getState('oauth_state');\n\t\t\n\t\tconsole.log('π State validation:');\n\t\tconsole.log(' Received state:', state);\n\t\tconsole.log(' Saved state:', savedState);\n\t\tconsole.log(' Match:', state === savedState);\n\n\t\tif (state !== savedState) {\n\t\t\tconsole.error('β State mismatch!');\n\t\t\tconsole.error(' Expected:', savedState);\n\t\t\tconsole.error(' Received:', state);\n\t\t\t\n\t\t\t// π₯ FIX: Si no hay savedState, es probable que se perdiΓ³\n\t\t\tif (!savedState) {\n\t\t\t\tconsole.warn('β οΈ No saved state found. This might be due to:');\n\t\t\t\tconsole.warn(' - App opened in new tab/window');\n\t\t\t\tconsole.warn(' - sessionStorage was cleared');\n\t\t\t\tconsole.warn(' - App was restarted');\n\t\t\t\tconsole.warn('π§ Attempting recovery...');\n\t\t\t\t\n\t\t\t\t// Intentar continuar de todas formas si el code es vΓ‘lido\n\t\t\t\t// (esto es menos seguro pero permite que funcione en apps nativas)\n\t\t\t} else {\n\t\t\t\t// Si hay savedState pero no coincide, es un error de seguridad real\n\t\t\t\tthis.emit('error', { error: 'invalid_state', error_description: 'State parameter mismatch' });\n\t\t\t\tthrow new Error('Invalid state parameter');\n\t\t\t}\n\t\t}\n\n\t\tthis.storage.removeState('oauth_state');\n\n\t\ttry {\n\t\t\t// Get code_verifier if using PKCE\n\t\t\tconst codeVerifier = this.config.usePKCE ? this.storage.getCodeVerifier() || undefined : undefined;\n\t\t\t\n\t\t\tconsole.log('π PKCE code_verifier:', !!codeVerifier);\n\t\t\t\n\t\t\tif (this.config.usePKCE && !codeVerifier) {\n\t\t\t\tconsole.error('β PKCE enabled but no code_verifier found!');\n\t\t\t\tthrow new Error('Code verifier not found. Authentication cannot continue.');\n\t\t\t}\n\n\t\t\tconsole.log('π Exchanging code for tokens...');\n\n\t\t\t// Exchange code for tokens\n\t\t\tconst tokens = await this.api.exchangeCodeForTokens(\n\t\t\t\tcode,\n\t\t\t\tthis.config.clientId,\n\t\t\t\tthis.config.redirectUri,\n\t\t\t\tcodeVerifier,\n\t\t\t\tthis.config.clientSecret\n\t\t\t);\n\n\t\t\tconsole.log('β
Tokens received');\n\t\t\tthis.storage.setTokens(tokens);\n\n\t\t\t// Get user info\n\t\t\tconsole.log('π€ Fetching user info...');\n\t\t\tconst user = await this.api.getUserInfo(tokens.accessToken);\n\t\t\tconsole.log('β
User info received:', user.email);\n\t\t\t\n\t\t\tthis.storage.setUser(user);\n\n\t\t\t// Clean up PKCE data\n\t\t\tif (this.config.usePKCE) {\n\t\t\t\tthis.storage.removeCodeVerifier();\n\t\t\t\tthis.storage.removeCodeChallenge();\n\t\t\t\tconsole.log('π§Ή PKCE data cleaned up');\n\t\t\t}\n\n\t\t\t// Setup token expiry monitoring\n\t\t\tthis.setupTokenExpiryMonitoring();\n\n\t\t\tthis.emit('login', { user, tokens });\n\t\t\tconsole.log('π Login successful!');\n\n\t\t\t// Clean URL (remove query params)\n\t\t\twindow.history.replaceState({}, document.title, window.location.pathname + window.location.hash);\n\t\t} catch (error) {\n\t\t\tconsole.error('β Callback handling error:', error);\n\t\t\tthis.emit('error', error);\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t// ==================== User Methods ====================\n\n\tgetUser(): User | null {\n\t\treturn this.storage.getUser();\n\t}\n\n\tgetAccessToken(): string | null {\n\t\treturn this.storage.getTokens()?.accessToken || null;\n\t}\n\n\tgetRefreshToken(): string | null {\n\t\treturn this.storage.getTokens()?.refreshToken || null;\n\t}\n\n\tisAuthenticated(): boolean {\n\t\treturn this.storage.hasValidToken() && !!this.storage.getUser();\n\t}\n\n\tgetState(): AuthState {\n\t\treturn {\n\t\t\tisAuthenticated: this.isAuthenticated(),\n\t\t\tuser: this.getUser(),\n\t\t\ttokens: this.storage.getTokens(),\n\t\t};\n\t}\n\n\tasync validateToken(): Promise<boolean> {\n\t\tconst accessToken = this.getAccessToken();\n\t\tif (!accessToken) return false;\n\n\t\ttry {\n\t\t\treturn await this.api.validateToken(accessToken);\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t// ==================== Token Refresh ====================\n\n\tprivate setupTokenExpiryMonitoring(): void {\n\t\tthis.clearTokenExpiryTimer();\n\n\t\tconst tokens = this.storage.getTokens();\n\t\tconst issuedAt = this.storage.getTokenIssuedAt();\n\n\t\tif (!tokens || !issuedAt) return;\n\n\t\t// Refresh 5 minutes before expiry\n\t\tconst timeUntilRefresh = (tokens.expiresIn - 300) * 1000; // 5 min buffer\n\n\t\tif (timeUntilRefresh > 0) {\n\t\t\tthis.tokenExpiryTimer = setTimeout(async () => {\n\t\t\t\ttry {\n\t\t\t\t\tawait this.refreshToken();\n\t\t\t\t} catch (error) {\n\t\t\t\t\tthis.emit('token-expired', error);\n\t\t\t\t\tawait this.logout();\n\t\t\t\t}\n\t\t\t}, timeUntilRefresh);\n\t\t}\n\t}\n\n\tprivate clearTokenExpiryTimer(): void {\n\t\tif (this.tokenExpiryTimer) {\n\t\t\tclearTimeout(this.tokenExpiryTimer);\n\t\t\tthis.tokenExpiryTimer = null;\n\t\t}\n\t}\n\n\tasync refreshToken(): Promise<void> {\n\t\tconst tokens = this.storage.getTokens();\n\t\tif (!tokens?.refreshToken) {\n\t\t\tthrow new Error('No refresh token available');\n\t\t}\n\n\t\ttry {\n\t\t\tconst newTokens = await this.api.refreshAccessToken(\n\t\t\t\ttokens.refreshToken,\n\t\t\t\tthis.config.clientId,\n\t\t\t\tthis.config.clientSecret\n\t\t\t);\n\n\t\t\tthis.storage.setTokens(newTokens);\n\t\t\tthis.setupTokenExpiryMonitoring();\n\t\t\tthis.emit('token-refresh', newTokens);\n\t\t} catch (error) {\n\t\t\tthis.emit('error', error);\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t// ==================== Event Handling ====================\n\n\ton(event: AuthEventType, callback: (event: AuthEvent) => void): void {\n\t\tif (!this.listeners.has(event)) {\n\t\t\tthis.listeners.set(event, new Set());\n\t\t}\n\t\tthis.listeners.get(event)!.add(callback);\n\t}\n\n\toff(event: AuthEventType, callback: (event: AuthEvent) => void): void {\n\t\tthis.listeners.get(event)?.delete(callback);\n\t}\n\n\tprivate emit(type: AuthEventType, data?: any): void {\n\t\tconst event: AuthEvent = { type, data };\n\t\tthis.listeners.get(type)?.forEach((callback) => callback(event));\n\t}\n\n\t// ==================== Utility Methods ====================\n\n\tclearStorage(): void {\n\t\tthis.storage.clear();\n\t}\n\n\tdestroy(): void {\n\t\tthis.clearTokenExpiryTimer();\n\t\tthis.listeners.clear();\n\t}\n}"],"names":["STORAGE_PREFIX","Storage","constructor","useSessionStorage","this","storage","sessionStorage","localStorage","setTokens","tokens","setItem","JSON","stringify","Date","now","toString","getTokens","data","getItem","parse","getTokenIssuedAt","parseInt","setUser","user","getUser","setState","key","value","getState","removeState","removeItem","setCodeVerifier","codeVerifier","getCodeVerifier","removeCodeVerifier","setCodeChallenge","codeChallenge","getCodeChallenge","removeCodeChallenge","clear","hasValidToken","issuedAt","expiresIn","ApiClient","baseUrl","exchangeCodeForTokens","code","clientId","redirectUri","clientSecret","body","grant_type","client_id","redirect_uri","code_verifier","client_secret","response","fetch","method","headers","URLSearchParams","ok","errorData","json","catch","error","error_description","Error","result","accessToken","access_token","refreshToken","refresh_token","expires_in","tokenType","token_type","getUserInfo","Authorization","refreshAccessToken","logout","validateToken","generateState","array","Uint8Array","crypto","getRandomValues","Array","from","byte","padStart","join","async","generateCodeChallenge","TextEncoder","encode","digest","bytes","binary","String","fromCharCode","btoa","replace","base64UrlEncode","subtle","config","tokenExpiryTimer","scope","isArray","split","normalizeScope","apiUrl","responseType","state","usePKCE","codeChallengeMethod","grantType","api","listeners","Map","console","log","window","handleCallback","setupTokenExpiryMonitoring","login","additionalParams","savedState","params","response_type","charset","length","Math","floor","random","verifier","i","charAt","generateCodeVerifier","savedVerifier","code_challenge","code_challenge_method","authUrl","location","href","clearTokenExpiryTimer","emit","url","URL","searchParams","forEach","parseQueryString","hasCode","hasError","hasState","errorDescription","warn","undefined","email","history","replaceState","document","title","pathname","hash","getAccessToken","getRefreshToken","isAuthenticated","timeUntilRefresh","setTimeout","clearTimeout","newTokens","on","event","callback","has","set","Set","get","add","off","delete","type","clearStorage","destroy"],"mappings":"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"}
|
package/dist/index.esm.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e="custos_";class t{constructor(e=!1){this.storage=e?sessionStorage:localStorage
|
|
1
|
+
const e="custos_";class t{constructor(e=!1){this.storage=e?sessionStorage:localStorage}setTokens(t){this.storage.setItem(`${e}tokens`,JSON.stringify(t)),this.storage.setItem(`${e}token_issued_at`,Date.now().toString())}getTokens(){const t=this.storage.getItem(`${e}tokens`);return t?JSON.parse(t):null}getTokenIssuedAt(){const t=this.storage.getItem(`${e}token_issued_at`);return t?parseInt(t,10):null}setUser(t){this.storage.setItem(`${e}user`,JSON.stringify(t))}getUser(){const t=this.storage.getItem(`${e}user`);return t?JSON.parse(t):null}setState(t,o){this.storage.setItem(`${e}${t}`,o)}getState(t){return this.storage.getItem(`${e}${t}`)}removeState(t){this.storage.removeItem(`${e}${t}`)}setCodeVerifier(e){this.setState("code_verifier",e)}getCodeVerifier(){return this.getState("code_verifier")}removeCodeVerifier(){this.removeState("code_verifier")}setCodeChallenge(e){this.setState("code_challenge",e)}getCodeChallenge(){return this.getState("code_challenge")}removeCodeChallenge(){this.removeState("code_challenge")}clear(){this.storage.removeItem(`${e}tokens`),this.storage.removeItem(`${e}token_issued_at`),this.storage.removeItem(`${e}user`),this.removeState("oauth_state"),this.removeCodeVerifier(),this.removeCodeChallenge()}hasValidToken(){const e=this.getTokens(),t=this.getTokenIssuedAt();if(!e||!t)return!1;return Date.now()<t+1e3*e.expiresIn}}class o{constructor(e){this.baseUrl=e}async exchangeCodeForTokens(e,t,o,r,s){const i={grant_type:"authorization_code",code:e,client_id:t,redirect_uri:o};r&&(i.code_verifier=r),s&&(i.client_secret=s);const n=await fetch(`${this.baseUrl}/api/v1/auth/token`,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams(i).toString()});if(!n.ok){const e=await n.json().catch(()=>({error:"unknown_error",error_description:"Failed to exchange code for tokens"}));throw new Error(e.error_description||e.error||"Token exchange failed")}const a=await n.json(),c=a.data||a;return{accessToken:c.access_token,refreshToken:c.refresh_token,expiresIn:c.expires_in,tokenType:c.token_type||"Bearer"}}async getUserInfo(e){const t=await fetch(`${this.baseUrl}/api/v1/system/users/profile`,{headers:{Authorization:`Bearer ${e}`}});if(!t.ok)throw new Error("Failed to get user info");const o=await t.json();return o.data||o}async refreshAccessToken(e,t,o){const r={grant_type:"refresh_token",refresh_token:e,client_id:t};o&&(r.client_secret=o);const s=await fetch(`${this.baseUrl}/api/v1/auth/token`,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams(r).toString()});if(!s.ok)throw new Error("Failed to refresh token");const i=await s.json(),n=i.data||i;return{accessToken:n.access_token,refreshToken:n.refresh_token||e,expiresIn:n.expires_in,tokenType:n.token_type||"Bearer"}}async logout(e){await fetch(`${this.baseUrl}/api/v1/auth/revoke`,{method:"POST",headers:{Authorization:`Bearer ${e}`}})}async validateToken(e){try{return(await fetch(`${this.baseUrl}/api/v1/auth/validate`,{headers:{Authorization:`Bearer ${e}`}})).ok}catch{return!1}}}function r(){const e=new Uint8Array(32);return crypto.getRandomValues(e),Array.from(e,e=>e.toString(16).padStart(2,"0")).join("")}async function s(e){const t=(new TextEncoder).encode(e);return function(e){const t=new Uint8Array(e),o=Array.from(t,e=>String.fromCharCode(e)).join("");return btoa(o).replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}(await crypto.subtle.digest("SHA-256",t))}class i{constructor({useSessionStorage:e,...s}){this.tokenExpiryTimer=null;const i=function(e){return e?Array.isArray(e)?e:"string"==typeof e?e.split(" "):["openid","profile","email"]:["openid","profile","email"]}(s.scope);this.config={clientId:s.clientId,clientSecret:s.clientSecret||"",redirectUri:s.redirectUri,apiUrl:s.apiUrl||"https://custos.alimzen.com",scope:i,responseType:s.responseType||"code",state:s.state||r(),usePKCE:!1!==s.usePKCE,codeChallengeMethod:s.codeChallengeMethod||"S256",grantType:s.grantType||"authorization_code",useSessionStorage:e||!1},this.storage=new t(!1),this.api=new o(this.config.apiUrl),this.listeners=new Map,console.log("π§ Custos SDK initialized with localStorage for state persistence"),"undefined"!=typeof window&&(this.handleCallback(),this.setupTokenExpiryMonitoring())}async login(e){const t=this.config.state;console.log("π Starting login flow"),console.log("π Saving state:",t),this.storage.setState("oauth_state",t);const o=this.storage.getState("oauth_state");console.log("β
State saved successfully:",o===t);const r={response_type:this.config.responseType,client_id:this.config.clientId,redirect_uri:this.config.redirectUri,scope:Array.isArray(this.config.scope)?this.config.scope.join(" "):this.config.scope,state:t,...e};if(this.config.usePKCE){const e=function(){const e="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~",t=Math.floor(86*Math.random())+43;let o="";for(let r=0;r<t;r++)o+=e.charAt(Math.floor(66*Math.random()));return o}(),t=await s(e);console.log("π PKCE enabled"),console.log("π Saving code_verifier"),this.storage.setCodeVerifier(e),this.storage.setCodeChallenge(t);const o=this.storage.getCodeVerifier();console.log("β
Code verifier saved:",!!o),r.code_challenge=t,r.code_challenge_method=this.config.codeChallengeMethod}const i=`${this.config.apiUrl}/v1/auth/authorize?${new URLSearchParams(r)}`;console.log("π Redirecting to:",i),window.location.href=i}async logout(){const e=this.storage.getTokens();if(e?.accessToken)try{await this.api.logout(e.accessToken)}catch(e){console.error("Logout error:",e)}this.clearTokenExpiryTimer(),this.storage.clear(),this.emit("logout",null)}async handleCallback(){const e=function(e){const t={};return new URL(e).searchParams.forEach((e,o)=>{t[o]=e}),t}(window.location.href);console.log("π Checking for callback params:",{hasCode:!!e.code,hasError:!!e.error,hasState:!!e.state});const t=e.error;if(t){const o=e.error_description||t;throw console.error("β OAuth error:",t,o),this.emit("error",{error:t,error_description:o}),new Error(o)}const o=e.code;if(!o)return void console.log("βΉοΈ No authorization code found, skipping callback handling");console.log("β
Authorization code found");const r=e.state,s=this.storage.getState("oauth_state");if(console.log("π State validation:"),console.log(" Received state:",r),console.log(" Saved state:",s),console.log(" Match:",r===s),r!==s){if(console.error("β State mismatch!"),console.error(" Expected:",s),console.error(" Received:",r),s)throw this.emit("error",{error:"invalid_state",error_description:"State parameter mismatch"}),new Error("Invalid state parameter");console.warn("β οΈ No saved state found. This might be due to:"),console.warn(" - App opened in new tab/window"),console.warn(" - sessionStorage was cleared"),console.warn(" - App was restarted"),console.warn("π§ Attempting recovery...")}this.storage.removeState("oauth_state");try{const e=this.config.usePKCE&&this.storage.getCodeVerifier()||void 0;if(console.log("π PKCE code_verifier:",!!e),this.config.usePKCE&&!e)throw console.error("β PKCE enabled but no code_verifier found!"),new Error("Code verifier not found. Authentication cannot continue.");console.log("π Exchanging code for tokens...");const t=await this.api.exchangeCodeForTokens(o,this.config.clientId,this.config.redirectUri,e,this.config.clientSecret);console.log("β
Tokens received"),this.storage.setTokens(t),console.log("π€ Fetching user info...");const r=await this.api.getUserInfo(t.accessToken);console.log("β
User info received:",r.email),this.storage.setUser(r),this.config.usePKCE&&(this.storage.removeCodeVerifier(),this.storage.removeCodeChallenge(),console.log("π§Ή PKCE data cleaned up")),this.setupTokenExpiryMonitoring(),this.emit("login",{user:r,tokens:t}),console.log("π Login successful!"),window.history.replaceState({},document.title,window.location.pathname+window.location.hash)}catch(t){throw console.error("β Callback handling error:",t),this.emit("error",t),t}}getUser(){return this.storage.getUser()}getAccessToken(){return this.storage.getTokens()?.accessToken||null}getRefreshToken(){return this.storage.getTokens()?.refreshToken||null}isAuthenticated(){return this.storage.hasValidToken()&&!!this.storage.getUser()}getState(){return{isAuthenticated:this.isAuthenticated(),user:this.getUser(),tokens:this.storage.getTokens()}}async validateToken(){const e=this.getAccessToken();if(!e)return!1;try{return await this.api.validateToken(e)}catch{return!1}}setupTokenExpiryMonitoring(){this.clearTokenExpiryTimer();const e=this.storage.getTokens(),t=this.storage.getTokenIssuedAt();if(!e||!t)return;const o=1e3*(e.expiresIn-300);o>0&&(this.tokenExpiryTimer=setTimeout(async()=>{try{await this.refreshToken()}catch(e){this.emit("token-expired",e),await this.logout()}},o))}clearTokenExpiryTimer(){this.tokenExpiryTimer&&(clearTimeout(this.tokenExpiryTimer),this.tokenExpiryTimer=null)}async refreshToken(){const e=this.storage.getTokens();if(!e?.refreshToken)throw new Error("No refresh token available");try{const t=await this.api.refreshAccessToken(e.refreshToken,this.config.clientId,this.config.clientSecret);this.storage.setTokens(t),this.setupTokenExpiryMonitoring(),this.emit("token-refresh",t)}catch(e){throw this.emit("error",e),e}}on(e,t){this.listeners.has(e)||this.listeners.set(e,new Set),this.listeners.get(e).add(t)}off(e,t){this.listeners.get(e)?.delete(t)}emit(e,t){const o={type:e,data:t};this.listeners.get(e)?.forEach(e=>e(o))}clearStorage(){this.storage.clear()}destroy(){this.clearTokenExpiryTimer(),this.listeners.clear()}}export{i as Custos};
|
|
2
2
|
//# sourceMappingURL=index.esm.js.map
|
package/dist/index.esm.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.esm.js","sources":["../src/storage.ts","../src/api.ts","../src/utils.ts","../src/Custos.ts"],"sourcesContent":["import { AuthTokens, User } from './types';\n\nconst STORAGE_PREFIX = 'custos_';\n\nexport class Storage {\n\tprivate storage: globalThis.Storage;\n\n\tconstructor(useSessionStorage = false) {\n\t\tthis.storage = useSessionStorage ? sessionStorage : localStorage;\n\t\tconsole.log(`Using ${useSessionStorage ? 'sessionStorage' : 'localStorage'}`);\n\t\tconsole.log('Storage initialized:', this.storage);\n\t}\n\n\t// Tokens\n\tsetTokens(tokens: AuthTokens): void {\n\t\tthis.storage.setItem(`${STORAGE_PREFIX}tokens`, JSON.stringify(tokens));\n\t\tthis.storage.setItem(`${STORAGE_PREFIX}token_issued_at`, Date.now().toString());\n\t}\n\n\tgetTokens(): AuthTokens | null {\n\t\tconst data = this.storage.getItem(`${STORAGE_PREFIX}tokens`);\n\t\treturn data ? JSON.parse(data) : null;\n\t}\n\n\tgetTokenIssuedAt(): number | null {\n\t\tconst data = this.storage.getItem(`${STORAGE_PREFIX}token_issued_at`);\n\t\treturn data ? parseInt(data, 10) : null;\n\t}\n\n\t// User\n\tsetUser(user: User): void {\n\t\tthis.storage.setItem(`${STORAGE_PREFIX}user`, JSON.stringify(user));\n\t}\n\n\tgetUser(): User | null {\n\t\tconst data = this.storage.getItem(`${STORAGE_PREFIX}user`);\n\t\treturn data ? JSON.parse(data) : null;\n\t}\n\n\t// State & PKCE\n\tsetState(key: string, value: string): void {\n\t\tconsole.log(`Setting state: ${key} = ${value}`);\n\t\tconsole.log(`Storage before setting state:`, this.storage);\n\t\tthis.storage.setItem(`${STORAGE_PREFIX}${key}`, value);\n\t}\n\n\tgetState(key: string): string | null {\n\t\tconsole.log(`Getting state: ${key}`);\n\t\tconsole.log(`Storage before getting state:`, this.storage);\n\t\treturn this.storage.getItem(`${STORAGE_PREFIX}${key}`);\n\t}\n\n\tremoveState(key: string): void {\n\t\tconsole.log(`Removing state: ${key}`);\n\t\tthis.storage.removeItem(`${STORAGE_PREFIX}${key}`);\n\t}\n\n\t// PKCE specific\n\tsetCodeVerifier(codeVerifier: string): void {\n\t\tthis.setState('code_verifier', codeVerifier);\n\t}\n\n\tgetCodeVerifier(): string | null {\n\t\treturn this.getState('code_verifier');\n\t}\n\n\tremoveCodeVerifier(): void {\n\t\tthis.removeState('code_verifier');\n\t}\n\n\tsetCodeChallenge(codeChallenge: string): void {\n\t\tthis.setState('code_challenge', codeChallenge);\n\t}\n\n\tgetCodeChallenge(): string | null {\n\t\treturn this.getState('code_challenge');\n\t}\n\n\tremoveCodeChallenge(): void {\n\t\tthis.removeState('code_challenge');\n\t}\n\n\t// Clear all\n\tclear(): void {\n\t\tthis.storage.removeItem(`${STORAGE_PREFIX}tokens`);\n\t\tthis.storage.removeItem(`${STORAGE_PREFIX}token_issued_at`);\n\t\tthis.storage.removeItem(`${STORAGE_PREFIX}user`);\n\t\tthis.removeState('oauth_state');\n\t\tthis.removeCodeVerifier();\n\t\tthis.removeCodeChallenge();\n\t}\n\n\t// Validation\n\thasValidToken(): boolean {\n\t\tconst tokens = this.getTokens();\n\t\tconst issuedAt = this.getTokenIssuedAt();\n\n\t\tif (!tokens || !issuedAt) return false;\n\n\t\tconst now = Date.now();\n\t\tconst expirationTime = issuedAt + tokens.expiresIn * 1000;\n\n\t\treturn now < expirationTime;\n\t}\n}\n","import { AuthTokens, User } from './types';\n\nexport class ApiClient {\n\tprivate baseUrl: string;\n\n\tconstructor(baseUrl: string) {\n\t\tthis.baseUrl = baseUrl;\n\t}\n\n\tasync exchangeCodeForTokens(\n\t\tcode: string,\n\t\tclientId: string,\n\t\tredirectUri: string,\n\t\tcodeVerifier?: string,\n\t\tclientSecret?: string\n\t): Promise<AuthTokens> {\n\t\tconst body: Record<string, string> = {\n\t\t\tgrant_type: 'authorization_code',\n\t\t\tcode,\n\t\t\tclient_id: clientId,\n\t\t\tredirect_uri: redirectUri,\n\t\t};\n\n\t\t// Add PKCE code_verifier if present\n\t\tif (codeVerifier) {\n\t\t\tbody.code_verifier = codeVerifier;\n\t\t}\n\n\t\t// Add client_secret if present (for confidential clients)\n\t\tif (clientSecret) {\n\t\t\tbody.client_secret = clientSecret;\n\t\t}\n\n\t\tconst response = await fetch(`${this.baseUrl}/api/v1/auth/token`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: {\n\t\t\t\t'Content-Type': 'application/x-www-form-urlencoded',\n\t\t\t},\n\t\t\tbody: new URLSearchParams(body).toString(),\n\t\t});\n\n\t\tif (!response.ok) {\n\t\t\tconst errorData = await response.json().catch(() => ({\n\t\t\t\terror: 'unknown_error',\n\t\t\t\terror_description: 'Failed to exchange code for tokens'\n\t\t\t}));\n\n\t\t\tthrow new Error(errorData.error_description || errorData.error || 'Token exchange failed');\n\t\t}\n\n\t\tconst result = await response.json();\n\t\tconst data = result.data || result; // Support both {data: {...}} and direct response\n\n\t\treturn {\n\t\t\taccessToken: data.access_token,\n\t\t\trefreshToken: data.refresh_token,\n\t\t\texpiresIn: data.expires_in,\n\t\t\ttokenType: data.token_type || 'Bearer',\n\t\t};\n\t}\n\n\tasync getUserInfo(accessToken: string): Promise<User> {\n\t\tconst response = await fetch(`${this.baseUrl}/api/v1/system/users/profile`, {\n\t\t\theaders: {\n\t\t\t\tAuthorization: `Bearer ${accessToken}`,\n\t\t\t},\n\t\t});\n\n\t\tif (!response.ok) {\n\t\t\tthrow new Error('Failed to get user info');\n\t\t}\n\n\t\tconst result = await response.json();\n\t\treturn result.data || result;\n\t}\n\n\tasync refreshAccessToken(\n\t\trefreshToken: string,\n\t\tclientId: string,\n\t\tclientSecret?: string\n\t): Promise<AuthTokens> {\n\t\tconst body: Record<string, string> = {\n\t\t\tgrant_type: 'refresh_token',\n\t\t\trefresh_token: refreshToken,\n\t\t\tclient_id: clientId,\n\t\t};\n\n\t\tif (clientSecret) {\n\t\t\tbody.client_secret = clientSecret;\n\t\t}\n\n\t\tconst response = await fetch(`${this.baseUrl}/api/v1/auth/token`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: {\n\t\t\t\t'Content-Type': 'application/x-www-form-urlencoded',\n\t\t\t},\n\t\t\tbody: new URLSearchParams(body).toString(),\n\t\t});\n\n\t\tif (!response.ok) {\n\t\t\tthrow new Error('Failed to refresh token');\n\t\t}\n\n\t\tconst result = await response.json();\n\t\tconst data = result.data || result;\n\n\t\treturn {\n\t\t\taccessToken: data.access_token,\n\t\t\trefreshToken: data.refresh_token || refreshToken, // Keep old refresh token if not provided\n\t\t\texpiresIn: data.expires_in,\n\t\t\ttokenType: data.token_type || 'Bearer',\n\t\t};\n\t}\n\n\tasync logout(accessToken: string): Promise<void> {\n\t\tawait fetch(`${this.baseUrl}/api/v1/auth/revoke`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: {\n\t\t\t\tAuthorization: `Bearer ${accessToken}`,\n\t\t\t},\n\t\t});\n\t}\n\n\tasync validateToken(accessToken: string): Promise<boolean> {\n\t\ttry {\n\t\t\tconst response = await fetch(`${this.baseUrl}/api/v1/auth/validate`, {\n\t\t\t\theaders: {\n\t\t\t\t\tAuthorization: `Bearer ${accessToken}`,\n\t\t\t\t},\n\t\t\t});\n\t\t\treturn response.ok;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n}\n","export function generateState(): string {\n\tconst array = new Uint8Array(32);\n\tcrypto.getRandomValues(array);\n\treturn Array.from(array, (byte) => byte.toString(16).padStart(2, '0')).join('');\n}\n\nexport function parseQueryString(url: string): Record<string, string> {\n\tconst params: Record<string, string> = {};\n\tconst searchParams = new URL(url).searchParams;\n\tsearchParams.forEach((value, key) => {\n\t\tparams[key] = value;\n\t});\n\treturn params;\n}\n\nexport function isTokenExpired(expiresIn: number, issuedAt: number): boolean {\n\tconst now = Date.now();\n\tconst expirationTime = issuedAt + expiresIn * 1000;\n\t// Refresh 5 minutes before expiration\n\treturn now >= expirationTime - 5 * 60 * 1000;\n}\n\n// PKCE utilities\nexport function generateCodeVerifier(): string {\n\tconst charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~';\n\tconst length = Math.floor(Math.random() * 86) + 43; // 43-128 characters\n\tlet verifier = '';\n\tfor (let i = 0; i < length; i++) {\n\t\tverifier += charset.charAt(Math.floor(Math.random() * charset.length));\n\t}\n\treturn verifier;\n}\n\nexport async function generateCodeChallenge(codeVerifier: string): Promise<string> {\n\tconst encoder = new TextEncoder();\n\tconst data = encoder.encode(codeVerifier);\n\tconst digest = await crypto.subtle.digest('SHA-256', data);\n\treturn base64UrlEncode(digest);\n}\n\nfunction base64UrlEncode(digest: ArrayBuffer): string {\n\tconst bytes = new Uint8Array(digest);\n\tconst binary = Array.from(bytes, (byte) => String.fromCharCode(byte)).join('');\n\treturn btoa(binary)\n\t\t.replace(/\\+/g, '-')\n\t\t.replace(/\\//g, '_')\n\t\t.replace(/=/g, '');\n}\n\nexport function normalizeScope(scope?: string | string[]): string[] {\n\tif (!scope) return ['openid', 'profile', 'email'];\n\tif (Array.isArray(scope)) return scope;\n\tif (typeof scope === 'string') return scope.split(' ');\n\treturn ['openid', 'profile', 'email'];\n}\n","import { CustosConfig, User, AuthState, AuthEvent, AuthEventType } from './types';\nimport { Storage } from './storage';\nimport { ApiClient } from './api';\nimport {\n\tgenerateState,\n\tparseQueryString,\n\tgenerateCodeVerifier,\n\tgenerateCodeChallenge,\n\tnormalizeScope\n} from './utils';\n\nexport class Custos {\n\tprivate config: Required<CustosConfig>;\n\tprivate storage: Storage;\n\tprivate api: ApiClient;\n\tprivate listeners: Map<AuthEventType, Set<(event: AuthEvent) => void>>;\n\tprivate tokenExpiryTimer: any = null;\n\n\tconstructor({ useSessionStorage, ...config }: CustosConfig) {\n\t\t// Normalize scope\n\t\tconst scope = normalizeScope(config.scope);\n\n\t\tthis.config = {\n\t\t\tclientId: config.clientId,\n\t\t\tclientSecret: config.clientSecret || '',\n\t\t\tredirectUri: config.redirectUri,\n\t\t\tapiUrl: config.apiUrl || 'https://custos.alimzen.com',\n\t\t\tscope,\n\t\t\tresponseType: config.responseType || 'code',\n\t\t\tstate: config.state || generateState(),\n\t\t\tusePKCE: config.usePKCE !== false, // Default to true\n\t\t\tcodeChallengeMethod: config.codeChallengeMethod || 'S256',\n\t\t\tgrantType: config.grantType || 'authorization_code',\n\t\t\tuseSessionStorage: useSessionStorage || false\n\t\t};\n\n\t\tthis.storage = new Storage(useSessionStorage); // Use sessionStorage for better security\n\t\tthis.api = new ApiClient(this.config.apiUrl);\n\t\tthis.listeners = new Map();\n\n\t\t// Handle callback automatically\n\t\tif (typeof window !== 'undefined') {\n\t\t\tthis.handleCallback();\n\t\t\tthis.setupTokenExpiryMonitoring();\n\t\t}\n\t}\n\n\t// ==================== Authentication Methods ====================\n\n\tasync login(additionalParams?: Record<string, string>): Promise<void> {\n\t\tconst state = this.config.state;\n\t\tthis.storage.setState('oauth_state', state);\n\n\t\tconst params: Record<string, string> = {\n\t\t\tresponse_type: this.config.responseType,\n\t\t\tclient_id: this.config.clientId,\n\t\t\tredirect_uri: this.config.redirectUri,\n\t\t\tscope: Array.isArray(this.config.scope) ? this.config.scope.join(' ') : this.config.scope,\n\t\t\tstate,\n\t\t\t...additionalParams,\n\t\t};\n\n\t\t// Add PKCE if enabled\n\t\tif (this.config.usePKCE) {\n\t\t\tconst codeVerifier = generateCodeVerifier();\n\t\t\tconst codeChallenge = await generateCodeChallenge(codeVerifier);\n\n\t\t\tthis.storage.setCodeVerifier(codeVerifier);\n\t\t\tthis.storage.setCodeChallenge(codeChallenge);\n\n\t\t\tparams.code_challenge = codeChallenge;\n\t\t\tparams.code_challenge_method = this.config.codeChallengeMethod;\n\t\t}\n\n\t\tconst authUrl = `${this.config.apiUrl}/v1/auth/authorize?${new URLSearchParams(params)}`;\n\t\twindow.location.href = authUrl;\n\t}\n\n\tasync logout(): Promise<void> {\n\t\tconst tokens = this.storage.getTokens();\n\n\t\tif (tokens?.accessToken) {\n\t\t\ttry {\n\t\t\t\tawait this.api.logout(tokens.accessToken);\n\t\t\t} catch (error) {\n\t\t\t\tthrow new Error(`Logout error: ${error}`);\n\t\t\t}\n\t\t}\n\n\t\tthis.clearTokenExpiryTimer();\n\t\tthis.storage.clear();\n\t\tthis.emit('logout', null);\n\t}\n\n\tasync handleCallback(): Promise<void> {\n\t\tconst params = parseQueryString(window.location.href);\n\n\t\t// Check for errors\n\t\tconst error = params.error;\n\t\tif (error) {\n\t\t\tconst errorDescription = params.error_description || error;\n\t\t\tthis.emit('error', { error, error_description: errorDescription });\n\t\t\tthrow new Error(errorDescription);\n\t\t}\n\n\t\t// Check for authorization code\n\t\tconst code = params.code;\n\t\tif (!code) return;\n\n\t\t// Validate state\n\t\tconst state = params.state;\n\t\tconst savedState = this.storage.getState('oauth_state');\n\t\tif (state !== savedState) {\n\t\t\tthis.emit('error', { error: 'invalid_state', error_description: 'State parameter mismatch' });\n\t\t\tthrow new Error('Invalid state parameter');\n\t\t}\n\n\t\tthis.storage.removeState('oauth_state');\n\n\t\ttry {\n\t\t\t// Get code_verifier if using PKCE\n\t\t\tconst codeVerifier = this.config.usePKCE ? this.storage.getCodeVerifier() || undefined : undefined;\n\n\t\t\t// Exchange code for tokens\n\t\t\tconst tokens = await this.api.exchangeCodeForTokens(\n\t\t\t\tcode,\n\t\t\t\tthis.config.clientId,\n\t\t\t\tthis.config.redirectUri,\n\t\t\t\tcodeVerifier,\n\t\t\t\tthis.config.clientSecret\n\t\t\t);\n\n\t\t\tthis.storage.setTokens(tokens);\n\n\t\t\t// Get user info\n\t\t\tconst user = await this.api.getUserInfo(tokens.accessToken);\n\t\t\tthis.storage.setUser(user);\n\n\t\t\t// Clean up PKCE data\n\t\t\tif (this.config.usePKCE) {\n\t\t\t\tthis.storage.removeCodeVerifier();\n\t\t\t\tthis.storage.removeCodeChallenge();\n\t\t\t}\n\n\t\t\t// Setup token expiry monitoring\n\t\t\tthis.setupTokenExpiryMonitoring();\n\n\t\t\tthis.emit('login', { user, tokens });\n\n\t\t\t// Clean URL (remove query params)\n\t\t\twindow.history.replaceState({}, document.title, window.location.pathname + window.location.hash);\n\t\t} catch (error) {\n\t\t\tthis.emit('error', error);\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t// ==================== User Methods ====================\n\n\tgetUser(): User | null {\n\t\treturn this.storage.getUser();\n\t}\n\n\tgetAccessToken(): string | null {\n\t\treturn this.storage.getTokens()?.accessToken || null;\n\t}\n\n\tgetRefreshToken(): string | null {\n\t\treturn this.storage.getTokens()?.refreshToken || null;\n\t}\n\n\tisAuthenticated(): boolean {\n\t\treturn this.storage.hasValidToken() && !!this.storage.getUser();\n\t}\n\n\tgetState(): AuthState {\n\t\treturn {\n\t\t\tisAuthenticated: this.isAuthenticated(),\n\t\t\tuser: this.getUser(),\n\t\t\ttokens: this.storage.getTokens(),\n\t\t};\n\t}\n\n\tasync validateToken(): Promise<boolean> {\n\t\tconst accessToken = this.getAccessToken();\n\t\tif (!accessToken) return false;\n\n\t\ttry {\n\t\t\treturn await this.api.validateToken(accessToken);\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t// ==================== Token Refresh ====================\n\n\tprivate setupTokenExpiryMonitoring(): void {\n\t\tthis.clearTokenExpiryTimer();\n\n\t\tconst tokens = this.storage.getTokens();\n\t\tconst issuedAt = this.storage.getTokenIssuedAt();\n\n\t\tif (!tokens || !issuedAt) return;\n\n\t\t// Refresh 5 minutes before expiry\n\t\tconst timeUntilRefresh = (tokens.expiresIn - 300) * 1000; // 5 min buffer\n\n\t\tif (timeUntilRefresh > 0) {\n\t\t\tthis.tokenExpiryTimer = setTimeout(async () => {\n\t\t\t\ttry {\n\t\t\t\t\tawait this.refreshToken();\n\t\t\t\t} catch (error) {\n\t\t\t\t\tthis.emit('token-expired', error);\n\t\t\t\t\tawait this.logout();\n\t\t\t\t}\n\t\t\t}, timeUntilRefresh);\n\t\t}\n\t}\n\n\tprivate clearTokenExpiryTimer(): void {\n\t\tif (this.tokenExpiryTimer) {\n\t\t\tclearTimeout(this.tokenExpiryTimer);\n\t\t\tthis.tokenExpiryTimer = null;\n\t\t}\n\t}\n\n\tasync refreshToken(): Promise<void> {\n\t\tconst tokens = this.storage.getTokens();\n\t\tif (!tokens?.refreshToken) {\n\t\t\tthrow new Error('No refresh token available');\n\t\t}\n\n\t\ttry {\n\t\t\tconst newTokens = await this.api.refreshAccessToken(\n\t\t\t\ttokens.refreshToken,\n\t\t\t\tthis.config.clientId,\n\t\t\t\tthis.config.clientSecret\n\t\t\t);\n\n\t\t\tthis.storage.setTokens(newTokens);\n\t\t\tthis.setupTokenExpiryMonitoring();\n\t\t\tthis.emit('token-refresh', newTokens);\n\t\t} catch (error) {\n\t\t\tthis.emit('error', error);\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t// ==================== Event Handling ====================\n\n\ton(event: AuthEventType, callback: (event: AuthEvent) => void): void {\n\t\tif (!this.listeners.has(event)) {\n\t\t\tthis.listeners.set(event, new Set());\n\t\t}\n\t\tthis.listeners.get(event)!.add(callback);\n\t}\n\n\toff(event: AuthEventType, callback: (event: AuthEvent) => void): void {\n\t\tthis.listeners.get(event)?.delete(callback);\n\t}\n\n\tprivate emit(type: AuthEventType, data?: any): void {\n\t\tconst event: AuthEvent = { type, data };\n\t\tthis.listeners.get(type)?.forEach((callback) => callback(event));\n\t}\n\n\t// ==================== Utility Methods ====================\n\n\tclearStorage(): void {\n\t\tthis.storage.clear();\n\t}\n\n\tdestroy(): void {\n\t\tthis.clearTokenExpiryTimer();\n\t\tthis.listeners.clear();\n\t}\n}\n"],"names":["STORAGE_PREFIX","Storage","constructor","useSessionStorage","this","storage","sessionStorage","localStorage","console","log","setTokens","tokens","setItem","JSON","stringify","Date","now","toString","getTokens","data","getItem","parse","getTokenIssuedAt","parseInt","setUser","user","getUser","setState","key","value","getState","removeState","removeItem","setCodeVerifier","codeVerifier","getCodeVerifier","removeCodeVerifier","setCodeChallenge","codeChallenge","getCodeChallenge","removeCodeChallenge","clear","hasValidToken","issuedAt","expiresIn","ApiClient","baseUrl","exchangeCodeForTokens","code","clientId","redirectUri","clientSecret","body","grant_type","client_id","redirect_uri","code_verifier","client_secret","response","fetch","method","headers","URLSearchParams","ok","errorData","json","catch","error","error_description","Error","result","accessToken","access_token","refreshToken","refresh_token","expires_in","tokenType","token_type","getUserInfo","Authorization","refreshAccessToken","logout","validateToken","generateState","array","Uint8Array","crypto","getRandomValues","Array","from","byte","padStart","join","async","generateCodeChallenge","TextEncoder","encode","digest","bytes","binary","String","fromCharCode","btoa","replace","base64UrlEncode","subtle","Custos","config","tokenExpiryTimer","scope","isArray","split","normalizeScope","apiUrl","responseType","state","usePKCE","codeChallengeMethod","grantType","api","listeners","Map","window","handleCallback","setupTokenExpiryMonitoring","login","additionalParams","params","response_type","charset","length","Math","floor","random","verifier","i","charAt","generateCodeVerifier","code_challenge","code_challenge_method","authUrl","location","href","clearTokenExpiryTimer","emit","url","URL","searchParams","forEach","parseQueryString","errorDescription","undefined","history","replaceState","document","title","pathname","hash","getAccessToken","getRefreshToken","isAuthenticated","timeUntilRefresh","setTimeout","clearTimeout","newTokens","on","event","callback","has","set","Set","get","add","off","delete","type","clearStorage","destroy"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.esm.js","sources":["../src/storage.ts","../src/api.ts","../src/utils.ts","../src/Custos.ts"],"sourcesContent":["import { AuthTokens, User } from './types';\n\nconst STORAGE_PREFIX = 'custos_';\n\nexport class Storage {\n\tprivate storage: globalThis.Storage;\n\n\tconstructor(useSessionStorage = false) {\n\t\tthis.storage = useSessionStorage ? sessionStorage : localStorage;\n\t}\n\n\t// Tokens\n\tsetTokens(tokens: AuthTokens): void {\n\t\tthis.storage.setItem(`${STORAGE_PREFIX}tokens`, JSON.stringify(tokens));\n\t\tthis.storage.setItem(`${STORAGE_PREFIX}token_issued_at`, Date.now().toString());\n\t}\n\n\tgetTokens(): AuthTokens | null {\n\t\tconst data = this.storage.getItem(`${STORAGE_PREFIX}tokens`);\n\t\treturn data ? JSON.parse(data) : null;\n\t}\n\n\tgetTokenIssuedAt(): number | null {\n\t\tconst data = this.storage.getItem(`${STORAGE_PREFIX}token_issued_at`);\n\t\treturn data ? parseInt(data, 10) : null;\n\t}\n\n\t// User\n\tsetUser(user: User): void {\n\t\tthis.storage.setItem(`${STORAGE_PREFIX}user`, JSON.stringify(user));\n\t}\n\n\tgetUser(): User | null {\n\t\tconst data = this.storage.getItem(`${STORAGE_PREFIX}user`);\n\t\treturn data ? JSON.parse(data) : null;\n\t}\n\n\t// State & PKCE\n\tsetState(key: string, value: string): void {\n\t\tthis.storage.setItem(`${STORAGE_PREFIX}${key}`, value);\n\t}\n\n\tgetState(key: string): string | null {\n\t\treturn this.storage.getItem(`${STORAGE_PREFIX}${key}`);\n\t}\n\n\tremoveState(key: string): void {\n\t\tthis.storage.removeItem(`${STORAGE_PREFIX}${key}`);\n\t}\n\n\t// PKCE specific\n\tsetCodeVerifier(codeVerifier: string): void {\n\t\tthis.setState('code_verifier', codeVerifier);\n\t}\n\n\tgetCodeVerifier(): string | null {\n\t\treturn this.getState('code_verifier');\n\t}\n\n\tremoveCodeVerifier(): void {\n\t\tthis.removeState('code_verifier');\n\t}\n\n\tsetCodeChallenge(codeChallenge: string): void {\n\t\tthis.setState('code_challenge', codeChallenge);\n\t}\n\n\tgetCodeChallenge(): string | null {\n\t\treturn this.getState('code_challenge');\n\t}\n\n\tremoveCodeChallenge(): void {\n\t\tthis.removeState('code_challenge');\n\t}\n\n\t// Clear all\n\tclear(): void {\n\t\tthis.storage.removeItem(`${STORAGE_PREFIX}tokens`);\n\t\tthis.storage.removeItem(`${STORAGE_PREFIX}token_issued_at`);\n\t\tthis.storage.removeItem(`${STORAGE_PREFIX}user`);\n\t\tthis.removeState('oauth_state');\n\t\tthis.removeCodeVerifier();\n\t\tthis.removeCodeChallenge();\n\t}\n\n\t// Validation\n\thasValidToken(): boolean {\n\t\tconst tokens = this.getTokens();\n\t\tconst issuedAt = this.getTokenIssuedAt();\n\n\t\tif (!tokens || !issuedAt) return false;\n\n\t\tconst now = Date.now();\n\t\tconst expirationTime = issuedAt + tokens.expiresIn * 1000;\n\n\t\treturn now < expirationTime;\n\t}\n}\n","import { AuthTokens, User } from './types';\n\nexport class ApiClient {\n\tprivate baseUrl: string;\n\n\tconstructor(baseUrl: string) {\n\t\tthis.baseUrl = baseUrl;\n\t}\n\n\tasync exchangeCodeForTokens(\n\t\tcode: string,\n\t\tclientId: string,\n\t\tredirectUri: string,\n\t\tcodeVerifier?: string,\n\t\tclientSecret?: string\n\t): Promise<AuthTokens> {\n\t\tconst body: Record<string, string> = {\n\t\t\tgrant_type: 'authorization_code',\n\t\t\tcode,\n\t\t\tclient_id: clientId,\n\t\t\tredirect_uri: redirectUri,\n\t\t};\n\n\t\t// Add PKCE code_verifier if present\n\t\tif (codeVerifier) {\n\t\t\tbody.code_verifier = codeVerifier;\n\t\t}\n\n\t\t// Add client_secret if present (for confidential clients)\n\t\tif (clientSecret) {\n\t\t\tbody.client_secret = clientSecret;\n\t\t}\n\n\t\tconst response = await fetch(`${this.baseUrl}/api/v1/auth/token`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: {\n\t\t\t\t'Content-Type': 'application/x-www-form-urlencoded',\n\t\t\t},\n\t\t\tbody: new URLSearchParams(body).toString(),\n\t\t});\n\n\t\tif (!response.ok) {\n\t\t\tconst errorData = await response.json().catch(() => ({\n\t\t\t\terror: 'unknown_error',\n\t\t\t\terror_description: 'Failed to exchange code for tokens'\n\t\t\t}));\n\n\t\t\tthrow new Error(errorData.error_description || errorData.error || 'Token exchange failed');\n\t\t}\n\n\t\tconst result = await response.json();\n\t\tconst data = result.data || result; // Support both {data: {...}} and direct response\n\n\t\treturn {\n\t\t\taccessToken: data.access_token,\n\t\t\trefreshToken: data.refresh_token,\n\t\t\texpiresIn: data.expires_in,\n\t\t\ttokenType: data.token_type || 'Bearer',\n\t\t};\n\t}\n\n\tasync getUserInfo(accessToken: string): Promise<User> {\n\t\tconst response = await fetch(`${this.baseUrl}/api/v1/system/users/profile`, {\n\t\t\theaders: {\n\t\t\t\tAuthorization: `Bearer ${accessToken}`,\n\t\t\t},\n\t\t});\n\n\t\tif (!response.ok) {\n\t\t\tthrow new Error('Failed to get user info');\n\t\t}\n\n\t\tconst result = await response.json();\n\t\treturn result.data || result;\n\t}\n\n\tasync refreshAccessToken(\n\t\trefreshToken: string,\n\t\tclientId: string,\n\t\tclientSecret?: string\n\t): Promise<AuthTokens> {\n\t\tconst body: Record<string, string> = {\n\t\t\tgrant_type: 'refresh_token',\n\t\t\trefresh_token: refreshToken,\n\t\t\tclient_id: clientId,\n\t\t};\n\n\t\tif (clientSecret) {\n\t\t\tbody.client_secret = clientSecret;\n\t\t}\n\n\t\tconst response = await fetch(`${this.baseUrl}/api/v1/auth/token`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: {\n\t\t\t\t'Content-Type': 'application/x-www-form-urlencoded',\n\t\t\t},\n\t\t\tbody: new URLSearchParams(body).toString(),\n\t\t});\n\n\t\tif (!response.ok) {\n\t\t\tthrow new Error('Failed to refresh token');\n\t\t}\n\n\t\tconst result = await response.json();\n\t\tconst data = result.data || result;\n\n\t\treturn {\n\t\t\taccessToken: data.access_token,\n\t\t\trefreshToken: data.refresh_token || refreshToken, // Keep old refresh token if not provided\n\t\t\texpiresIn: data.expires_in,\n\t\t\ttokenType: data.token_type || 'Bearer',\n\t\t};\n\t}\n\n\tasync logout(accessToken: string): Promise<void> {\n\t\tawait fetch(`${this.baseUrl}/api/v1/auth/revoke`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: {\n\t\t\t\tAuthorization: `Bearer ${accessToken}`,\n\t\t\t},\n\t\t});\n\t}\n\n\tasync validateToken(accessToken: string): Promise<boolean> {\n\t\ttry {\n\t\t\tconst response = await fetch(`${this.baseUrl}/api/v1/auth/validate`, {\n\t\t\t\theaders: {\n\t\t\t\t\tAuthorization: `Bearer ${accessToken}`,\n\t\t\t\t},\n\t\t\t});\n\t\t\treturn response.ok;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n}\n","export function generateState(): string {\n\tconst array = new Uint8Array(32);\n\tcrypto.getRandomValues(array);\n\treturn Array.from(array, (byte) => byte.toString(16).padStart(2, '0')).join('');\n}\n\nexport function parseQueryString(url: string): Record<string, string> {\n\tconst params: Record<string, string> = {};\n\tconst searchParams = new URL(url).searchParams;\n\tsearchParams.forEach((value, key) => {\n\t\tparams[key] = value;\n\t});\n\treturn params;\n}\n\nexport function isTokenExpired(expiresIn: number, issuedAt: number): boolean {\n\tconst now = Date.now();\n\tconst expirationTime = issuedAt + expiresIn * 1000;\n\t// Refresh 5 minutes before expiration\n\treturn now >= expirationTime - 5 * 60 * 1000;\n}\n\n// PKCE utilities\nexport function generateCodeVerifier(): string {\n\tconst charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~';\n\tconst length = Math.floor(Math.random() * 86) + 43; // 43-128 characters\n\tlet verifier = '';\n\tfor (let i = 0; i < length; i++) {\n\t\tverifier += charset.charAt(Math.floor(Math.random() * charset.length));\n\t}\n\treturn verifier;\n}\n\nexport async function generateCodeChallenge(codeVerifier: string): Promise<string> {\n\tconst encoder = new TextEncoder();\n\tconst data = encoder.encode(codeVerifier);\n\tconst digest = await crypto.subtle.digest('SHA-256', data);\n\treturn base64UrlEncode(digest);\n}\n\nfunction base64UrlEncode(digest: ArrayBuffer): string {\n\tconst bytes = new Uint8Array(digest);\n\tconst binary = Array.from(bytes, (byte) => String.fromCharCode(byte)).join('');\n\treturn btoa(binary)\n\t\t.replace(/\\+/g, '-')\n\t\t.replace(/\\//g, '_')\n\t\t.replace(/=/g, '');\n}\n\nexport function normalizeScope(scope?: string | string[]): string[] {\n\tif (!scope) return ['openid', 'profile', 'email'];\n\tif (Array.isArray(scope)) return scope;\n\tif (typeof scope === 'string') return scope.split(' ');\n\treturn ['openid', 'profile', 'email'];\n}\n","import { CustosConfig, User, AuthState, AuthEvent, AuthEventType } from './types';\nimport { Storage } from './storage';\nimport { ApiClient } from './api';\nimport {\n\tgenerateState,\n\tparseQueryString,\n\tgenerateCodeVerifier,\n\tgenerateCodeChallenge,\n\tnormalizeScope\n} from './utils';\n\nexport class Custos {\n\tprivate config: Required<CustosConfig>;\n\tprivate storage: Storage;\n\tprivate api: ApiClient;\n\tprivate listeners: Map<AuthEventType, Set<(event: AuthEvent) => void>>;\n\tprivate tokenExpiryTimer: any = null;\n\n\tconstructor({ useSessionStorage, ...config }: CustosConfig) {\n\t\t// Normalize scope\n\t\tconst scope = normalizeScope(config.scope);\n\n\t\tthis.config = {\n\t\t\tclientId: config.clientId,\n\t\t\tclientSecret: config.clientSecret || '',\n\t\t\tredirectUri: config.redirectUri,\n\t\t\tapiUrl: config.apiUrl || 'https://custos.alimzen.com',\n\t\t\tscope,\n\t\t\tresponseType: config.responseType || 'code',\n\t\t\tstate: config.state || generateState(),\n\t\t\tusePKCE: config.usePKCE !== false, // Default to true\n\t\t\tcodeChallengeMethod: config.codeChallengeMethod || 'S256',\n\t\t\tgrantType: config.grantType || 'authorization_code',\n\t\t\tuseSessionStorage: useSessionStorage || false\n\t\t};\n\n\t\t// π₯ FIX: Para apps nativas o casos donde se pierde sessionStorage,\n\t\t// usar localStorage para state y PKCE\n\t\tconst storageForState = useSessionStorage ? sessionStorage : localStorage;\n\t\tthis.storage = new Storage(false); // β
SIEMPRE usar localStorage para persistencia\n\t\tthis.api = new ApiClient(this.config.apiUrl);\n\t\tthis.listeners = new Map();\n\n\t\tconsole.log('π§ Custos SDK initialized with localStorage for state persistence');\n\n\t\t// Handle callback automatically\n\t\tif (typeof window !== 'undefined') {\n\t\t\tthis.handleCallback();\n\t\t\tthis.setupTokenExpiryMonitoring();\n\t\t}\n\t}\n\n\t// ==================== Authentication Methods ====================\n\n\tasync login(additionalParams?: Record<string, string>): Promise<void> {\n\t\tconst state = this.config.state;\n\t\t\n\t\tconsole.log('π Starting login flow');\n\t\tconsole.log('π Saving state:', state);\n\t\t\n\t\tthis.storage.setState('oauth_state', state);\n\t\t\n\t\t// Verificar que se guardΓ³ correctamente\n\t\tconst savedState = this.storage.getState('oauth_state');\n\t\tconsole.log('β
State saved successfully:', savedState === state);\n\n\t\tconst params: Record<string, string> = {\n\t\t\tresponse_type: this.config.responseType,\n\t\t\tclient_id: this.config.clientId,\n\t\t\tredirect_uri: this.config.redirectUri,\n\t\t\tscope: Array.isArray(this.config.scope) ? this.config.scope.join(' ') : this.config.scope,\n\t\t\tstate,\n\t\t\t...additionalParams,\n\t\t};\n\n\t\t// Add PKCE if enabled\n\t\tif (this.config.usePKCE) {\n\t\t\tconst codeVerifier = generateCodeVerifier();\n\t\t\tconst codeChallenge = await generateCodeChallenge(codeVerifier);\n\n\t\t\tconsole.log('π PKCE enabled');\n\t\t\tconsole.log('π Saving code_verifier');\n\t\t\t\n\t\t\tthis.storage.setCodeVerifier(codeVerifier);\n\t\t\tthis.storage.setCodeChallenge(codeChallenge);\n\t\t\t\n\t\t\t// Verificar que se guardΓ³\n\t\t\tconst savedVerifier = this.storage.getCodeVerifier();\n\t\t\tconsole.log('β
Code verifier saved:', !!savedVerifier);\n\n\t\t\tparams.code_challenge = codeChallenge;\n\t\t\tparams.code_challenge_method = this.config.codeChallengeMethod;\n\t\t}\n\n\t\tconst authUrl = `${this.config.apiUrl}/v1/auth/authorize?${new URLSearchParams(params)}`;\n\t\tconsole.log('π Redirecting to:', authUrl);\n\t\t\n\t\twindow.location.href = authUrl;\n\t}\n\n\tasync logout(): Promise<void> {\n\t\tconst tokens = this.storage.getTokens();\n\n\t\tif (tokens?.accessToken) {\n\t\t\ttry {\n\t\t\t\tawait this.api.logout(tokens.accessToken);\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error('Logout error:', error);\n\t\t\t}\n\t\t}\n\n\t\tthis.clearTokenExpiryTimer();\n\t\tthis.storage.clear();\n\t\tthis.emit('logout', null);\n\t}\n\n\tasync handleCallback(): Promise<void> {\n\t\tconst params = parseQueryString(window.location.href);\n\n\t\tconsole.log('π Checking for callback params:', {\n\t\t\thasCode: !!params.code,\n\t\t\thasError: !!params.error,\n\t\t\thasState: !!params.state\n\t\t});\n\n\t\t// Check for errors\n\t\tconst error = params.error;\n\t\tif (error) {\n\t\t\tconst errorDescription = params.error_description || error;\n\t\t\tconsole.error('β OAuth error:', error, errorDescription);\n\t\t\tthis.emit('error', { error, error_description: errorDescription });\n\t\t\tthrow new Error(errorDescription);\n\t\t}\n\n\t\t// Check for authorization code\n\t\tconst code = params.code;\n\t\tif (!code) {\n\t\t\tconsole.log('βΉοΈ No authorization code found, skipping callback handling');\n\t\t\treturn;\n\t\t}\n\n\t\tconsole.log('β
Authorization code found');\n\n\t\t// Validate state\n\t\tconst state = params.state;\n\t\tconst savedState = this.storage.getState('oauth_state');\n\t\t\n\t\tconsole.log('π State validation:');\n\t\tconsole.log(' Received state:', state);\n\t\tconsole.log(' Saved state:', savedState);\n\t\tconsole.log(' Match:', state === savedState);\n\n\t\tif (state !== savedState) {\n\t\t\tconsole.error('β State mismatch!');\n\t\t\tconsole.error(' Expected:', savedState);\n\t\t\tconsole.error(' Received:', state);\n\t\t\t\n\t\t\t// π₯ FIX: Si no hay savedState, es probable que se perdiΓ³\n\t\t\tif (!savedState) {\n\t\t\t\tconsole.warn('β οΈ No saved state found. This might be due to:');\n\t\t\t\tconsole.warn(' - App opened in new tab/window');\n\t\t\t\tconsole.warn(' - sessionStorage was cleared');\n\t\t\t\tconsole.warn(' - App was restarted');\n\t\t\t\tconsole.warn('π§ Attempting recovery...');\n\t\t\t\t\n\t\t\t\t// Intentar continuar de todas formas si el code es vΓ‘lido\n\t\t\t\t// (esto es menos seguro pero permite que funcione en apps nativas)\n\t\t\t} else {\n\t\t\t\t// Si hay savedState pero no coincide, es un error de seguridad real\n\t\t\t\tthis.emit('error', { error: 'invalid_state', error_description: 'State parameter mismatch' });\n\t\t\t\tthrow new Error('Invalid state parameter');\n\t\t\t}\n\t\t}\n\n\t\tthis.storage.removeState('oauth_state');\n\n\t\ttry {\n\t\t\t// Get code_verifier if using PKCE\n\t\t\tconst codeVerifier = this.config.usePKCE ? this.storage.getCodeVerifier() || undefined : undefined;\n\t\t\t\n\t\t\tconsole.log('π PKCE code_verifier:', !!codeVerifier);\n\t\t\t\n\t\t\tif (this.config.usePKCE && !codeVerifier) {\n\t\t\t\tconsole.error('β PKCE enabled but no code_verifier found!');\n\t\t\t\tthrow new Error('Code verifier not found. Authentication cannot continue.');\n\t\t\t}\n\n\t\t\tconsole.log('π Exchanging code for tokens...');\n\n\t\t\t// Exchange code for tokens\n\t\t\tconst tokens = await this.api.exchangeCodeForTokens(\n\t\t\t\tcode,\n\t\t\t\tthis.config.clientId,\n\t\t\t\tthis.config.redirectUri,\n\t\t\t\tcodeVerifier,\n\t\t\t\tthis.config.clientSecret\n\t\t\t);\n\n\t\t\tconsole.log('β
Tokens received');\n\t\t\tthis.storage.setTokens(tokens);\n\n\t\t\t// Get user info\n\t\t\tconsole.log('π€ Fetching user info...');\n\t\t\tconst user = await this.api.getUserInfo(tokens.accessToken);\n\t\t\tconsole.log('β
User info received:', user.email);\n\t\t\t\n\t\t\tthis.storage.setUser(user);\n\n\t\t\t// Clean up PKCE data\n\t\t\tif (this.config.usePKCE) {\n\t\t\t\tthis.storage.removeCodeVerifier();\n\t\t\t\tthis.storage.removeCodeChallenge();\n\t\t\t\tconsole.log('π§Ή PKCE data cleaned up');\n\t\t\t}\n\n\t\t\t// Setup token expiry monitoring\n\t\t\tthis.setupTokenExpiryMonitoring();\n\n\t\t\tthis.emit('login', { user, tokens });\n\t\t\tconsole.log('π Login successful!');\n\n\t\t\t// Clean URL (remove query params)\n\t\t\twindow.history.replaceState({}, document.title, window.location.pathname + window.location.hash);\n\t\t} catch (error) {\n\t\t\tconsole.error('β Callback handling error:', error);\n\t\t\tthis.emit('error', error);\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t// ==================== User Methods ====================\n\n\tgetUser(): User | null {\n\t\treturn this.storage.getUser();\n\t}\n\n\tgetAccessToken(): string | null {\n\t\treturn this.storage.getTokens()?.accessToken || null;\n\t}\n\n\tgetRefreshToken(): string | null {\n\t\treturn this.storage.getTokens()?.refreshToken || null;\n\t}\n\n\tisAuthenticated(): boolean {\n\t\treturn this.storage.hasValidToken() && !!this.storage.getUser();\n\t}\n\n\tgetState(): AuthState {\n\t\treturn {\n\t\t\tisAuthenticated: this.isAuthenticated(),\n\t\t\tuser: this.getUser(),\n\t\t\ttokens: this.storage.getTokens(),\n\t\t};\n\t}\n\n\tasync validateToken(): Promise<boolean> {\n\t\tconst accessToken = this.getAccessToken();\n\t\tif (!accessToken) return false;\n\n\t\ttry {\n\t\t\treturn await this.api.validateToken(accessToken);\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t// ==================== Token Refresh ====================\n\n\tprivate setupTokenExpiryMonitoring(): void {\n\t\tthis.clearTokenExpiryTimer();\n\n\t\tconst tokens = this.storage.getTokens();\n\t\tconst issuedAt = this.storage.getTokenIssuedAt();\n\n\t\tif (!tokens || !issuedAt) return;\n\n\t\t// Refresh 5 minutes before expiry\n\t\tconst timeUntilRefresh = (tokens.expiresIn - 300) * 1000; // 5 min buffer\n\n\t\tif (timeUntilRefresh > 0) {\n\t\t\tthis.tokenExpiryTimer = setTimeout(async () => {\n\t\t\t\ttry {\n\t\t\t\t\tawait this.refreshToken();\n\t\t\t\t} catch (error) {\n\t\t\t\t\tthis.emit('token-expired', error);\n\t\t\t\t\tawait this.logout();\n\t\t\t\t}\n\t\t\t}, timeUntilRefresh);\n\t\t}\n\t}\n\n\tprivate clearTokenExpiryTimer(): void {\n\t\tif (this.tokenExpiryTimer) {\n\t\t\tclearTimeout(this.tokenExpiryTimer);\n\t\t\tthis.tokenExpiryTimer = null;\n\t\t}\n\t}\n\n\tasync refreshToken(): Promise<void> {\n\t\tconst tokens = this.storage.getTokens();\n\t\tif (!tokens?.refreshToken) {\n\t\t\tthrow new Error('No refresh token available');\n\t\t}\n\n\t\ttry {\n\t\t\tconst newTokens = await this.api.refreshAccessToken(\n\t\t\t\ttokens.refreshToken,\n\t\t\t\tthis.config.clientId,\n\t\t\t\tthis.config.clientSecret\n\t\t\t);\n\n\t\t\tthis.storage.setTokens(newTokens);\n\t\t\tthis.setupTokenExpiryMonitoring();\n\t\t\tthis.emit('token-refresh', newTokens);\n\t\t} catch (error) {\n\t\t\tthis.emit('error', error);\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t// ==================== Event Handling ====================\n\n\ton(event: AuthEventType, callback: (event: AuthEvent) => void): void {\n\t\tif (!this.listeners.has(event)) {\n\t\t\tthis.listeners.set(event, new Set());\n\t\t}\n\t\tthis.listeners.get(event)!.add(callback);\n\t}\n\n\toff(event: AuthEventType, callback: (event: AuthEvent) => void): void {\n\t\tthis.listeners.get(event)?.delete(callback);\n\t}\n\n\tprivate emit(type: AuthEventType, data?: any): void {\n\t\tconst event: AuthEvent = { type, data };\n\t\tthis.listeners.get(type)?.forEach((callback) => callback(event));\n\t}\n\n\t// ==================== Utility Methods ====================\n\n\tclearStorage(): void {\n\t\tthis.storage.clear();\n\t}\n\n\tdestroy(): void {\n\t\tthis.clearTokenExpiryTimer();\n\t\tthis.listeners.clear();\n\t}\n}"],"names":["STORAGE_PREFIX","Storage","constructor","useSessionStorage","this","storage","sessionStorage","localStorage","setTokens","tokens","setItem","JSON","stringify","Date","now","toString","getTokens","data","getItem","parse","getTokenIssuedAt","parseInt","setUser","user","getUser","setState","key","value","getState","removeState","removeItem","setCodeVerifier","codeVerifier","getCodeVerifier","removeCodeVerifier","setCodeChallenge","codeChallenge","getCodeChallenge","removeCodeChallenge","clear","hasValidToken","issuedAt","expiresIn","ApiClient","baseUrl","exchangeCodeForTokens","code","clientId","redirectUri","clientSecret","body","grant_type","client_id","redirect_uri","code_verifier","client_secret","response","fetch","method","headers","URLSearchParams","ok","errorData","json","catch","error","error_description","Error","result","accessToken","access_token","refreshToken","refresh_token","expires_in","tokenType","token_type","getUserInfo","Authorization","refreshAccessToken","logout","validateToken","generateState","array","Uint8Array","crypto","getRandomValues","Array","from","byte","padStart","join","async","generateCodeChallenge","TextEncoder","encode","digest","bytes","binary","String","fromCharCode","btoa","replace","base64UrlEncode","subtle","Custos","config","tokenExpiryTimer","scope","isArray","split","normalizeScope","apiUrl","responseType","state","usePKCE","codeChallengeMethod","grantType","api","listeners","Map","console","log","window","handleCallback","setupTokenExpiryMonitoring","login","additionalParams","savedState","params","response_type","charset","length","Math","floor","random","verifier","i","charAt","generateCodeVerifier","savedVerifier","code_challenge","code_challenge_method","authUrl","location","href","clearTokenExpiryTimer","emit","url","URL","searchParams","forEach","parseQueryString","hasCode","hasError","hasState","errorDescription","warn","undefined","email","history","replaceState","document","title","pathname","hash","getAccessToken","getRefreshToken","isAuthenticated","timeUntilRefresh","setTimeout","clearTimeout","newTokens","on","event","callback","has","set","Set","get","add","off","delete","type","clearStorage","destroy"],"mappings":"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"}
|
package/dist/index.umd.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).Custos={})}(this,function(e){"use strict";const t="custos_";class
|
|
1
|
+
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).Custos={})}(this,function(e){"use strict";const t="custos_";class o{constructor(e=!1){this.storage=e?sessionStorage:localStorage}setTokens(e){this.storage.setItem(`${t}tokens`,JSON.stringify(e)),this.storage.setItem(`${t}token_issued_at`,Date.now().toString())}getTokens(){const e=this.storage.getItem(`${t}tokens`);return e?JSON.parse(e):null}getTokenIssuedAt(){const e=this.storage.getItem(`${t}token_issued_at`);return e?parseInt(e,10):null}setUser(e){this.storage.setItem(`${t}user`,JSON.stringify(e))}getUser(){const e=this.storage.getItem(`${t}user`);return e?JSON.parse(e):null}setState(e,o){this.storage.setItem(`${t}${e}`,o)}getState(e){return this.storage.getItem(`${t}${e}`)}removeState(e){this.storage.removeItem(`${t}${e}`)}setCodeVerifier(e){this.setState("code_verifier",e)}getCodeVerifier(){return this.getState("code_verifier")}removeCodeVerifier(){this.removeState("code_verifier")}setCodeChallenge(e){this.setState("code_challenge",e)}getCodeChallenge(){return this.getState("code_challenge")}removeCodeChallenge(){this.removeState("code_challenge")}clear(){this.storage.removeItem(`${t}tokens`),this.storage.removeItem(`${t}token_issued_at`),this.storage.removeItem(`${t}user`),this.removeState("oauth_state"),this.removeCodeVerifier(),this.removeCodeChallenge()}hasValidToken(){const e=this.getTokens(),t=this.getTokenIssuedAt();if(!e||!t)return!1;return Date.now()<t+1e3*e.expiresIn}}class r{constructor(e){this.baseUrl=e}async exchangeCodeForTokens(e,t,o,r,s){const i={grant_type:"authorization_code",code:e,client_id:t,redirect_uri:o};r&&(i.code_verifier=r),s&&(i.client_secret=s);const n=await fetch(`${this.baseUrl}/api/v1/auth/token`,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams(i).toString()});if(!n.ok){const e=await n.json().catch(()=>({error:"unknown_error",error_description:"Failed to exchange code for tokens"}));throw new Error(e.error_description||e.error||"Token exchange failed")}const a=await n.json(),c=a.data||a;return{accessToken:c.access_token,refreshToken:c.refresh_token,expiresIn:c.expires_in,tokenType:c.token_type||"Bearer"}}async getUserInfo(e){const t=await fetch(`${this.baseUrl}/api/v1/system/users/profile`,{headers:{Authorization:`Bearer ${e}`}});if(!t.ok)throw new Error("Failed to get user info");const o=await t.json();return o.data||o}async refreshAccessToken(e,t,o){const r={grant_type:"refresh_token",refresh_token:e,client_id:t};o&&(r.client_secret=o);const s=await fetch(`${this.baseUrl}/api/v1/auth/token`,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams(r).toString()});if(!s.ok)throw new Error("Failed to refresh token");const i=await s.json(),n=i.data||i;return{accessToken:n.access_token,refreshToken:n.refresh_token||e,expiresIn:n.expires_in,tokenType:n.token_type||"Bearer"}}async logout(e){await fetch(`${this.baseUrl}/api/v1/auth/revoke`,{method:"POST",headers:{Authorization:`Bearer ${e}`}})}async validateToken(e){try{return(await fetch(`${this.baseUrl}/api/v1/auth/validate`,{headers:{Authorization:`Bearer ${e}`}})).ok}catch{return!1}}}function s(){const e=new Uint8Array(32);return crypto.getRandomValues(e),Array.from(e,e=>e.toString(16).padStart(2,"0")).join("")}async function i(e){const t=(new TextEncoder).encode(e);return function(e){const t=new Uint8Array(e),o=Array.from(t,e=>String.fromCharCode(e)).join("");return btoa(o).replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}(await crypto.subtle.digest("SHA-256",t))}e.Custos=class{constructor({useSessionStorage:e,...t}){this.tokenExpiryTimer=null;const i=function(e){return e?Array.isArray(e)?e:"string"==typeof e?e.split(" "):["openid","profile","email"]:["openid","profile","email"]}(t.scope);this.config={clientId:t.clientId,clientSecret:t.clientSecret||"",redirectUri:t.redirectUri,apiUrl:t.apiUrl||"https://custos.alimzen.com",scope:i,responseType:t.responseType||"code",state:t.state||s(),usePKCE:!1!==t.usePKCE,codeChallengeMethod:t.codeChallengeMethod||"S256",grantType:t.grantType||"authorization_code",useSessionStorage:e||!1},this.storage=new o(!1),this.api=new r(this.config.apiUrl),this.listeners=new Map,console.log("π§ Custos SDK initialized with localStorage for state persistence"),"undefined"!=typeof window&&(this.handleCallback(),this.setupTokenExpiryMonitoring())}async login(e){const t=this.config.state;console.log("π Starting login flow"),console.log("π Saving state:",t),this.storage.setState("oauth_state",t);const o=this.storage.getState("oauth_state");console.log("β
State saved successfully:",o===t);const r={response_type:this.config.responseType,client_id:this.config.clientId,redirect_uri:this.config.redirectUri,scope:Array.isArray(this.config.scope)?this.config.scope.join(" "):this.config.scope,state:t,...e};if(this.config.usePKCE){const e=function(){const e="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~",t=Math.floor(86*Math.random())+43;let o="";for(let r=0;r<t;r++)o+=e.charAt(Math.floor(66*Math.random()));return o}(),t=await i(e);console.log("π PKCE enabled"),console.log("π Saving code_verifier"),this.storage.setCodeVerifier(e),this.storage.setCodeChallenge(t);const o=this.storage.getCodeVerifier();console.log("β
Code verifier saved:",!!o),r.code_challenge=t,r.code_challenge_method=this.config.codeChallengeMethod}const s=`${this.config.apiUrl}/v1/auth/authorize?${new URLSearchParams(r)}`;console.log("π Redirecting to:",s),window.location.href=s}async logout(){const e=this.storage.getTokens();if(e?.accessToken)try{await this.api.logout(e.accessToken)}catch(e){console.error("Logout error:",e)}this.clearTokenExpiryTimer(),this.storage.clear(),this.emit("logout",null)}async handleCallback(){const e=function(e){const t={};return new URL(e).searchParams.forEach((e,o)=>{t[o]=e}),t}(window.location.href);console.log("π Checking for callback params:",{hasCode:!!e.code,hasError:!!e.error,hasState:!!e.state});const t=e.error;if(t){const o=e.error_description||t;throw console.error("β OAuth error:",t,o),this.emit("error",{error:t,error_description:o}),new Error(o)}const o=e.code;if(!o)return void console.log("βΉοΈ No authorization code found, skipping callback handling");console.log("β
Authorization code found");const r=e.state,s=this.storage.getState("oauth_state");if(console.log("π State validation:"),console.log(" Received state:",r),console.log(" Saved state:",s),console.log(" Match:",r===s),r!==s){if(console.error("β State mismatch!"),console.error(" Expected:",s),console.error(" Received:",r),s)throw this.emit("error",{error:"invalid_state",error_description:"State parameter mismatch"}),new Error("Invalid state parameter");console.warn("β οΈ No saved state found. This might be due to:"),console.warn(" - App opened in new tab/window"),console.warn(" - sessionStorage was cleared"),console.warn(" - App was restarted"),console.warn("π§ Attempting recovery...")}this.storage.removeState("oauth_state");try{const e=this.config.usePKCE&&this.storage.getCodeVerifier()||void 0;if(console.log("π PKCE code_verifier:",!!e),this.config.usePKCE&&!e)throw console.error("β PKCE enabled but no code_verifier found!"),new Error("Code verifier not found. Authentication cannot continue.");console.log("π Exchanging code for tokens...");const t=await this.api.exchangeCodeForTokens(o,this.config.clientId,this.config.redirectUri,e,this.config.clientSecret);console.log("β
Tokens received"),this.storage.setTokens(t),console.log("π€ Fetching user info...");const r=await this.api.getUserInfo(t.accessToken);console.log("β
User info received:",r.email),this.storage.setUser(r),this.config.usePKCE&&(this.storage.removeCodeVerifier(),this.storage.removeCodeChallenge(),console.log("π§Ή PKCE data cleaned up")),this.setupTokenExpiryMonitoring(),this.emit("login",{user:r,tokens:t}),console.log("π Login successful!"),window.history.replaceState({},document.title,window.location.pathname+window.location.hash)}catch(t){throw console.error("β Callback handling error:",t),this.emit("error",t),t}}getUser(){return this.storage.getUser()}getAccessToken(){return this.storage.getTokens()?.accessToken||null}getRefreshToken(){return this.storage.getTokens()?.refreshToken||null}isAuthenticated(){return this.storage.hasValidToken()&&!!this.storage.getUser()}getState(){return{isAuthenticated:this.isAuthenticated(),user:this.getUser(),tokens:this.storage.getTokens()}}async validateToken(){const e=this.getAccessToken();if(!e)return!1;try{return await this.api.validateToken(e)}catch{return!1}}setupTokenExpiryMonitoring(){this.clearTokenExpiryTimer();const e=this.storage.getTokens(),t=this.storage.getTokenIssuedAt();if(!e||!t)return;const o=1e3*(e.expiresIn-300);o>0&&(this.tokenExpiryTimer=setTimeout(async()=>{try{await this.refreshToken()}catch(e){this.emit("token-expired",e),await this.logout()}},o))}clearTokenExpiryTimer(){this.tokenExpiryTimer&&(clearTimeout(this.tokenExpiryTimer),this.tokenExpiryTimer=null)}async refreshToken(){const e=this.storage.getTokens();if(!e?.refreshToken)throw new Error("No refresh token available");try{const t=await this.api.refreshAccessToken(e.refreshToken,this.config.clientId,this.config.clientSecret);this.storage.setTokens(t),this.setupTokenExpiryMonitoring(),this.emit("token-refresh",t)}catch(e){throw this.emit("error",e),e}}on(e,t){this.listeners.has(e)||this.listeners.set(e,new Set),this.listeners.get(e).add(t)}off(e,t){this.listeners.get(e)?.delete(t)}emit(e,t){const o={type:e,data:t};this.listeners.get(e)?.forEach(e=>e(o))}clearStorage(){this.storage.clear()}destroy(){this.clearTokenExpiryTimer(),this.listeners.clear()}},Object.defineProperty(e,"__esModule",{value:!0})});
|
|
2
2
|
//# sourceMappingURL=index.umd.js.map
|
package/dist/index.umd.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.umd.js","sources":["../src/storage.ts","../src/api.ts","../src/utils.ts","../src/Custos.ts"],"sourcesContent":["import { AuthTokens, User } from './types';\n\nconst STORAGE_PREFIX = 'custos_';\n\nexport class Storage {\n\tprivate storage: globalThis.Storage;\n\n\tconstructor(useSessionStorage = false) {\n\t\tthis.storage = useSessionStorage ? sessionStorage : localStorage;\n\t\tconsole.log(`Using ${useSessionStorage ? 'sessionStorage' : 'localStorage'}`);\n\t\tconsole.log('Storage initialized:', this.storage);\n\t}\n\n\t// Tokens\n\tsetTokens(tokens: AuthTokens): void {\n\t\tthis.storage.setItem(`${STORAGE_PREFIX}tokens`, JSON.stringify(tokens));\n\t\tthis.storage.setItem(`${STORAGE_PREFIX}token_issued_at`, Date.now().toString());\n\t}\n\n\tgetTokens(): AuthTokens | null {\n\t\tconst data = this.storage.getItem(`${STORAGE_PREFIX}tokens`);\n\t\treturn data ? JSON.parse(data) : null;\n\t}\n\n\tgetTokenIssuedAt(): number | null {\n\t\tconst data = this.storage.getItem(`${STORAGE_PREFIX}token_issued_at`);\n\t\treturn data ? parseInt(data, 10) : null;\n\t}\n\n\t// User\n\tsetUser(user: User): void {\n\t\tthis.storage.setItem(`${STORAGE_PREFIX}user`, JSON.stringify(user));\n\t}\n\n\tgetUser(): User | null {\n\t\tconst data = this.storage.getItem(`${STORAGE_PREFIX}user`);\n\t\treturn data ? JSON.parse(data) : null;\n\t}\n\n\t// State & PKCE\n\tsetState(key: string, value: string): void {\n\t\tconsole.log(`Setting state: ${key} = ${value}`);\n\t\tconsole.log(`Storage before setting state:`, this.storage);\n\t\tthis.storage.setItem(`${STORAGE_PREFIX}${key}`, value);\n\t}\n\n\tgetState(key: string): string | null {\n\t\tconsole.log(`Getting state: ${key}`);\n\t\tconsole.log(`Storage before getting state:`, this.storage);\n\t\treturn this.storage.getItem(`${STORAGE_PREFIX}${key}`);\n\t}\n\n\tremoveState(key: string): void {\n\t\tconsole.log(`Removing state: ${key}`);\n\t\tthis.storage.removeItem(`${STORAGE_PREFIX}${key}`);\n\t}\n\n\t// PKCE specific\n\tsetCodeVerifier(codeVerifier: string): void {\n\t\tthis.setState('code_verifier', codeVerifier);\n\t}\n\n\tgetCodeVerifier(): string | null {\n\t\treturn this.getState('code_verifier');\n\t}\n\n\tremoveCodeVerifier(): void {\n\t\tthis.removeState('code_verifier');\n\t}\n\n\tsetCodeChallenge(codeChallenge: string): void {\n\t\tthis.setState('code_challenge', codeChallenge);\n\t}\n\n\tgetCodeChallenge(): string | null {\n\t\treturn this.getState('code_challenge');\n\t}\n\n\tremoveCodeChallenge(): void {\n\t\tthis.removeState('code_challenge');\n\t}\n\n\t// Clear all\n\tclear(): void {\n\t\tthis.storage.removeItem(`${STORAGE_PREFIX}tokens`);\n\t\tthis.storage.removeItem(`${STORAGE_PREFIX}token_issued_at`);\n\t\tthis.storage.removeItem(`${STORAGE_PREFIX}user`);\n\t\tthis.removeState('oauth_state');\n\t\tthis.removeCodeVerifier();\n\t\tthis.removeCodeChallenge();\n\t}\n\n\t// Validation\n\thasValidToken(): boolean {\n\t\tconst tokens = this.getTokens();\n\t\tconst issuedAt = this.getTokenIssuedAt();\n\n\t\tif (!tokens || !issuedAt) return false;\n\n\t\tconst now = Date.now();\n\t\tconst expirationTime = issuedAt + tokens.expiresIn * 1000;\n\n\t\treturn now < expirationTime;\n\t}\n}\n","import { AuthTokens, User } from './types';\n\nexport class ApiClient {\n\tprivate baseUrl: string;\n\n\tconstructor(baseUrl: string) {\n\t\tthis.baseUrl = baseUrl;\n\t}\n\n\tasync exchangeCodeForTokens(\n\t\tcode: string,\n\t\tclientId: string,\n\t\tredirectUri: string,\n\t\tcodeVerifier?: string,\n\t\tclientSecret?: string\n\t): Promise<AuthTokens> {\n\t\tconst body: Record<string, string> = {\n\t\t\tgrant_type: 'authorization_code',\n\t\t\tcode,\n\t\t\tclient_id: clientId,\n\t\t\tredirect_uri: redirectUri,\n\t\t};\n\n\t\t// Add PKCE code_verifier if present\n\t\tif (codeVerifier) {\n\t\t\tbody.code_verifier = codeVerifier;\n\t\t}\n\n\t\t// Add client_secret if present (for confidential clients)\n\t\tif (clientSecret) {\n\t\t\tbody.client_secret = clientSecret;\n\t\t}\n\n\t\tconst response = await fetch(`${this.baseUrl}/api/v1/auth/token`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: {\n\t\t\t\t'Content-Type': 'application/x-www-form-urlencoded',\n\t\t\t},\n\t\t\tbody: new URLSearchParams(body).toString(),\n\t\t});\n\n\t\tif (!response.ok) {\n\t\t\tconst errorData = await response.json().catch(() => ({\n\t\t\t\terror: 'unknown_error',\n\t\t\t\terror_description: 'Failed to exchange code for tokens'\n\t\t\t}));\n\n\t\t\tthrow new Error(errorData.error_description || errorData.error || 'Token exchange failed');\n\t\t}\n\n\t\tconst result = await response.json();\n\t\tconst data = result.data || result; // Support both {data: {...}} and direct response\n\n\t\treturn {\n\t\t\taccessToken: data.access_token,\n\t\t\trefreshToken: data.refresh_token,\n\t\t\texpiresIn: data.expires_in,\n\t\t\ttokenType: data.token_type || 'Bearer',\n\t\t};\n\t}\n\n\tasync getUserInfo(accessToken: string): Promise<User> {\n\t\tconst response = await fetch(`${this.baseUrl}/api/v1/system/users/profile`, {\n\t\t\theaders: {\n\t\t\t\tAuthorization: `Bearer ${accessToken}`,\n\t\t\t},\n\t\t});\n\n\t\tif (!response.ok) {\n\t\t\tthrow new Error('Failed to get user info');\n\t\t}\n\n\t\tconst result = await response.json();\n\t\treturn result.data || result;\n\t}\n\n\tasync refreshAccessToken(\n\t\trefreshToken: string,\n\t\tclientId: string,\n\t\tclientSecret?: string\n\t): Promise<AuthTokens> {\n\t\tconst body: Record<string, string> = {\n\t\t\tgrant_type: 'refresh_token',\n\t\t\trefresh_token: refreshToken,\n\t\t\tclient_id: clientId,\n\t\t};\n\n\t\tif (clientSecret) {\n\t\t\tbody.client_secret = clientSecret;\n\t\t}\n\n\t\tconst response = await fetch(`${this.baseUrl}/api/v1/auth/token`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: {\n\t\t\t\t'Content-Type': 'application/x-www-form-urlencoded',\n\t\t\t},\n\t\t\tbody: new URLSearchParams(body).toString(),\n\t\t});\n\n\t\tif (!response.ok) {\n\t\t\tthrow new Error('Failed to refresh token');\n\t\t}\n\n\t\tconst result = await response.json();\n\t\tconst data = result.data || result;\n\n\t\treturn {\n\t\t\taccessToken: data.access_token,\n\t\t\trefreshToken: data.refresh_token || refreshToken, // Keep old refresh token if not provided\n\t\t\texpiresIn: data.expires_in,\n\t\t\ttokenType: data.token_type || 'Bearer',\n\t\t};\n\t}\n\n\tasync logout(accessToken: string): Promise<void> {\n\t\tawait fetch(`${this.baseUrl}/api/v1/auth/revoke`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: {\n\t\t\t\tAuthorization: `Bearer ${accessToken}`,\n\t\t\t},\n\t\t});\n\t}\n\n\tasync validateToken(accessToken: string): Promise<boolean> {\n\t\ttry {\n\t\t\tconst response = await fetch(`${this.baseUrl}/api/v1/auth/validate`, {\n\t\t\t\theaders: {\n\t\t\t\t\tAuthorization: `Bearer ${accessToken}`,\n\t\t\t\t},\n\t\t\t});\n\t\t\treturn response.ok;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n}\n","export function generateState(): string {\n\tconst array = new Uint8Array(32);\n\tcrypto.getRandomValues(array);\n\treturn Array.from(array, (byte) => byte.toString(16).padStart(2, '0')).join('');\n}\n\nexport function parseQueryString(url: string): Record<string, string> {\n\tconst params: Record<string, string> = {};\n\tconst searchParams = new URL(url).searchParams;\n\tsearchParams.forEach((value, key) => {\n\t\tparams[key] = value;\n\t});\n\treturn params;\n}\n\nexport function isTokenExpired(expiresIn: number, issuedAt: number): boolean {\n\tconst now = Date.now();\n\tconst expirationTime = issuedAt + expiresIn * 1000;\n\t// Refresh 5 minutes before expiration\n\treturn now >= expirationTime - 5 * 60 * 1000;\n}\n\n// PKCE utilities\nexport function generateCodeVerifier(): string {\n\tconst charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~';\n\tconst length = Math.floor(Math.random() * 86) + 43; // 43-128 characters\n\tlet verifier = '';\n\tfor (let i = 0; i < length; i++) {\n\t\tverifier += charset.charAt(Math.floor(Math.random() * charset.length));\n\t}\n\treturn verifier;\n}\n\nexport async function generateCodeChallenge(codeVerifier: string): Promise<string> {\n\tconst encoder = new TextEncoder();\n\tconst data = encoder.encode(codeVerifier);\n\tconst digest = await crypto.subtle.digest('SHA-256', data);\n\treturn base64UrlEncode(digest);\n}\n\nfunction base64UrlEncode(digest: ArrayBuffer): string {\n\tconst bytes = new Uint8Array(digest);\n\tconst binary = Array.from(bytes, (byte) => String.fromCharCode(byte)).join('');\n\treturn btoa(binary)\n\t\t.replace(/\\+/g, '-')\n\t\t.replace(/\\//g, '_')\n\t\t.replace(/=/g, '');\n}\n\nexport function normalizeScope(scope?: string | string[]): string[] {\n\tif (!scope) return ['openid', 'profile', 'email'];\n\tif (Array.isArray(scope)) return scope;\n\tif (typeof scope === 'string') return scope.split(' ');\n\treturn ['openid', 'profile', 'email'];\n}\n","import { CustosConfig, User, AuthState, AuthEvent, AuthEventType } from './types';\nimport { Storage } from './storage';\nimport { ApiClient } from './api';\nimport {\n\tgenerateState,\n\tparseQueryString,\n\tgenerateCodeVerifier,\n\tgenerateCodeChallenge,\n\tnormalizeScope\n} from './utils';\n\nexport class Custos {\n\tprivate config: Required<CustosConfig>;\n\tprivate storage: Storage;\n\tprivate api: ApiClient;\n\tprivate listeners: Map<AuthEventType, Set<(event: AuthEvent) => void>>;\n\tprivate tokenExpiryTimer: any = null;\n\n\tconstructor({ useSessionStorage, ...config }: CustosConfig) {\n\t\t// Normalize scope\n\t\tconst scope = normalizeScope(config.scope);\n\n\t\tthis.config = {\n\t\t\tclientId: config.clientId,\n\t\t\tclientSecret: config.clientSecret || '',\n\t\t\tredirectUri: config.redirectUri,\n\t\t\tapiUrl: config.apiUrl || 'https://custos.alimzen.com',\n\t\t\tscope,\n\t\t\tresponseType: config.responseType || 'code',\n\t\t\tstate: config.state || generateState(),\n\t\t\tusePKCE: config.usePKCE !== false, // Default to true\n\t\t\tcodeChallengeMethod: config.codeChallengeMethod || 'S256',\n\t\t\tgrantType: config.grantType || 'authorization_code',\n\t\t\tuseSessionStorage: useSessionStorage || false\n\t\t};\n\n\t\tthis.storage = new Storage(useSessionStorage); // Use sessionStorage for better security\n\t\tthis.api = new ApiClient(this.config.apiUrl);\n\t\tthis.listeners = new Map();\n\n\t\t// Handle callback automatically\n\t\tif (typeof window !== 'undefined') {\n\t\t\tthis.handleCallback();\n\t\t\tthis.setupTokenExpiryMonitoring();\n\t\t}\n\t}\n\n\t// ==================== Authentication Methods ====================\n\n\tasync login(additionalParams?: Record<string, string>): Promise<void> {\n\t\tconst state = this.config.state;\n\t\tthis.storage.setState('oauth_state', state);\n\n\t\tconst params: Record<string, string> = {\n\t\t\tresponse_type: this.config.responseType,\n\t\t\tclient_id: this.config.clientId,\n\t\t\tredirect_uri: this.config.redirectUri,\n\t\t\tscope: Array.isArray(this.config.scope) ? this.config.scope.join(' ') : this.config.scope,\n\t\t\tstate,\n\t\t\t...additionalParams,\n\t\t};\n\n\t\t// Add PKCE if enabled\n\t\tif (this.config.usePKCE) {\n\t\t\tconst codeVerifier = generateCodeVerifier();\n\t\t\tconst codeChallenge = await generateCodeChallenge(codeVerifier);\n\n\t\t\tthis.storage.setCodeVerifier(codeVerifier);\n\t\t\tthis.storage.setCodeChallenge(codeChallenge);\n\n\t\t\tparams.code_challenge = codeChallenge;\n\t\t\tparams.code_challenge_method = this.config.codeChallengeMethod;\n\t\t}\n\n\t\tconst authUrl = `${this.config.apiUrl}/v1/auth/authorize?${new URLSearchParams(params)}`;\n\t\twindow.location.href = authUrl;\n\t}\n\n\tasync logout(): Promise<void> {\n\t\tconst tokens = this.storage.getTokens();\n\n\t\tif (tokens?.accessToken) {\n\t\t\ttry {\n\t\t\t\tawait this.api.logout(tokens.accessToken);\n\t\t\t} catch (error) {\n\t\t\t\tthrow new Error(`Logout error: ${error}`);\n\t\t\t}\n\t\t}\n\n\t\tthis.clearTokenExpiryTimer();\n\t\tthis.storage.clear();\n\t\tthis.emit('logout', null);\n\t}\n\n\tasync handleCallback(): Promise<void> {\n\t\tconst params = parseQueryString(window.location.href);\n\n\t\t// Check for errors\n\t\tconst error = params.error;\n\t\tif (error) {\n\t\t\tconst errorDescription = params.error_description || error;\n\t\t\tthis.emit('error', { error, error_description: errorDescription });\n\t\t\tthrow new Error(errorDescription);\n\t\t}\n\n\t\t// Check for authorization code\n\t\tconst code = params.code;\n\t\tif (!code) return;\n\n\t\t// Validate state\n\t\tconst state = params.state;\n\t\tconst savedState = this.storage.getState('oauth_state');\n\t\tif (state !== savedState) {\n\t\t\tthis.emit('error', { error: 'invalid_state', error_description: 'State parameter mismatch' });\n\t\t\tthrow new Error('Invalid state parameter');\n\t\t}\n\n\t\tthis.storage.removeState('oauth_state');\n\n\t\ttry {\n\t\t\t// Get code_verifier if using PKCE\n\t\t\tconst codeVerifier = this.config.usePKCE ? this.storage.getCodeVerifier() || undefined : undefined;\n\n\t\t\t// Exchange code for tokens\n\t\t\tconst tokens = await this.api.exchangeCodeForTokens(\n\t\t\t\tcode,\n\t\t\t\tthis.config.clientId,\n\t\t\t\tthis.config.redirectUri,\n\t\t\t\tcodeVerifier,\n\t\t\t\tthis.config.clientSecret\n\t\t\t);\n\n\t\t\tthis.storage.setTokens(tokens);\n\n\t\t\t// Get user info\n\t\t\tconst user = await this.api.getUserInfo(tokens.accessToken);\n\t\t\tthis.storage.setUser(user);\n\n\t\t\t// Clean up PKCE data\n\t\t\tif (this.config.usePKCE) {\n\t\t\t\tthis.storage.removeCodeVerifier();\n\t\t\t\tthis.storage.removeCodeChallenge();\n\t\t\t}\n\n\t\t\t// Setup token expiry monitoring\n\t\t\tthis.setupTokenExpiryMonitoring();\n\n\t\t\tthis.emit('login', { user, tokens });\n\n\t\t\t// Clean URL (remove query params)\n\t\t\twindow.history.replaceState({}, document.title, window.location.pathname + window.location.hash);\n\t\t} catch (error) {\n\t\t\tthis.emit('error', error);\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t// ==================== User Methods ====================\n\n\tgetUser(): User | null {\n\t\treturn this.storage.getUser();\n\t}\n\n\tgetAccessToken(): string | null {\n\t\treturn this.storage.getTokens()?.accessToken || null;\n\t}\n\n\tgetRefreshToken(): string | null {\n\t\treturn this.storage.getTokens()?.refreshToken || null;\n\t}\n\n\tisAuthenticated(): boolean {\n\t\treturn this.storage.hasValidToken() && !!this.storage.getUser();\n\t}\n\n\tgetState(): AuthState {\n\t\treturn {\n\t\t\tisAuthenticated: this.isAuthenticated(),\n\t\t\tuser: this.getUser(),\n\t\t\ttokens: this.storage.getTokens(),\n\t\t};\n\t}\n\n\tasync validateToken(): Promise<boolean> {\n\t\tconst accessToken = this.getAccessToken();\n\t\tif (!accessToken) return false;\n\n\t\ttry {\n\t\t\treturn await this.api.validateToken(accessToken);\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t// ==================== Token Refresh ====================\n\n\tprivate setupTokenExpiryMonitoring(): void {\n\t\tthis.clearTokenExpiryTimer();\n\n\t\tconst tokens = this.storage.getTokens();\n\t\tconst issuedAt = this.storage.getTokenIssuedAt();\n\n\t\tif (!tokens || !issuedAt) return;\n\n\t\t// Refresh 5 minutes before expiry\n\t\tconst timeUntilRefresh = (tokens.expiresIn - 300) * 1000; // 5 min buffer\n\n\t\tif (timeUntilRefresh > 0) {\n\t\t\tthis.tokenExpiryTimer = setTimeout(async () => {\n\t\t\t\ttry {\n\t\t\t\t\tawait this.refreshToken();\n\t\t\t\t} catch (error) {\n\t\t\t\t\tthis.emit('token-expired', error);\n\t\t\t\t\tawait this.logout();\n\t\t\t\t}\n\t\t\t}, timeUntilRefresh);\n\t\t}\n\t}\n\n\tprivate clearTokenExpiryTimer(): void {\n\t\tif (this.tokenExpiryTimer) {\n\t\t\tclearTimeout(this.tokenExpiryTimer);\n\t\t\tthis.tokenExpiryTimer = null;\n\t\t}\n\t}\n\n\tasync refreshToken(): Promise<void> {\n\t\tconst tokens = this.storage.getTokens();\n\t\tif (!tokens?.refreshToken) {\n\t\t\tthrow new Error('No refresh token available');\n\t\t}\n\n\t\ttry {\n\t\t\tconst newTokens = await this.api.refreshAccessToken(\n\t\t\t\ttokens.refreshToken,\n\t\t\t\tthis.config.clientId,\n\t\t\t\tthis.config.clientSecret\n\t\t\t);\n\n\t\t\tthis.storage.setTokens(newTokens);\n\t\t\tthis.setupTokenExpiryMonitoring();\n\t\t\tthis.emit('token-refresh', newTokens);\n\t\t} catch (error) {\n\t\t\tthis.emit('error', error);\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t// ==================== Event Handling ====================\n\n\ton(event: AuthEventType, callback: (event: AuthEvent) => void): void {\n\t\tif (!this.listeners.has(event)) {\n\t\t\tthis.listeners.set(event, new Set());\n\t\t}\n\t\tthis.listeners.get(event)!.add(callback);\n\t}\n\n\toff(event: AuthEventType, callback: (event: AuthEvent) => void): void {\n\t\tthis.listeners.get(event)?.delete(callback);\n\t}\n\n\tprivate emit(type: AuthEventType, data?: any): void {\n\t\tconst event: AuthEvent = { type, data };\n\t\tthis.listeners.get(type)?.forEach((callback) => callback(event));\n\t}\n\n\t// ==================== Utility Methods ====================\n\n\tclearStorage(): void {\n\t\tthis.storage.clear();\n\t}\n\n\tdestroy(): void {\n\t\tthis.clearTokenExpiryTimer();\n\t\tthis.listeners.clear();\n\t}\n}\n"],"names":["STORAGE_PREFIX","Storage","constructor","useSessionStorage","this","storage","sessionStorage","localStorage","console","log","setTokens","tokens","setItem","JSON","stringify","Date","now","toString","getTokens","data","getItem","parse","getTokenIssuedAt","parseInt","setUser","user","getUser","setState","key","value","getState","removeState","removeItem","setCodeVerifier","codeVerifier","getCodeVerifier","removeCodeVerifier","setCodeChallenge","codeChallenge","getCodeChallenge","removeCodeChallenge","clear","hasValidToken","issuedAt","expiresIn","ApiClient","baseUrl","exchangeCodeForTokens","code","clientId","redirectUri","clientSecret","body","grant_type","client_id","redirect_uri","code_verifier","client_secret","response","fetch","method","headers","URLSearchParams","ok","errorData","json","catch","error","error_description","Error","result","accessToken","access_token","refreshToken","refresh_token","expires_in","tokenType","token_type","getUserInfo","Authorization","refreshAccessToken","logout","validateToken","generateState","array","Uint8Array","crypto","getRandomValues","Array","from","byte","padStart","join","async","generateCodeChallenge","TextEncoder","encode","digest","bytes","binary","String","fromCharCode","btoa","replace","base64UrlEncode","subtle","config","tokenExpiryTimer","scope","isArray","split","normalizeScope","apiUrl","responseType","state","usePKCE","codeChallengeMethod","grantType","api","listeners","Map","window","handleCallback","setupTokenExpiryMonitoring","login","additionalParams","params","response_type","charset","length","Math","floor","random","verifier","i","charAt","generateCodeVerifier","code_challenge","code_challenge_method","authUrl","location","href","clearTokenExpiryTimer","emit","url","URL","searchParams","forEach","parseQueryString","errorDescription","undefined","history","replaceState","document","title","pathname","hash","getAccessToken","getRefreshToken","isAuthenticated","timeUntilRefresh","setTimeout","clearTimeout","newTokens","on","event","callback","has","set","Set","get","add","off","delete","type","clearStorage","destroy"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.umd.js","sources":["../src/storage.ts","../src/api.ts","../src/utils.ts","../src/Custos.ts"],"sourcesContent":["import { AuthTokens, User } from './types';\n\nconst STORAGE_PREFIX = 'custos_';\n\nexport class Storage {\n\tprivate storage: globalThis.Storage;\n\n\tconstructor(useSessionStorage = false) {\n\t\tthis.storage = useSessionStorage ? sessionStorage : localStorage;\n\t}\n\n\t// Tokens\n\tsetTokens(tokens: AuthTokens): void {\n\t\tthis.storage.setItem(`${STORAGE_PREFIX}tokens`, JSON.stringify(tokens));\n\t\tthis.storage.setItem(`${STORAGE_PREFIX}token_issued_at`, Date.now().toString());\n\t}\n\n\tgetTokens(): AuthTokens | null {\n\t\tconst data = this.storage.getItem(`${STORAGE_PREFIX}tokens`);\n\t\treturn data ? JSON.parse(data) : null;\n\t}\n\n\tgetTokenIssuedAt(): number | null {\n\t\tconst data = this.storage.getItem(`${STORAGE_PREFIX}token_issued_at`);\n\t\treturn data ? parseInt(data, 10) : null;\n\t}\n\n\t// User\n\tsetUser(user: User): void {\n\t\tthis.storage.setItem(`${STORAGE_PREFIX}user`, JSON.stringify(user));\n\t}\n\n\tgetUser(): User | null {\n\t\tconst data = this.storage.getItem(`${STORAGE_PREFIX}user`);\n\t\treturn data ? JSON.parse(data) : null;\n\t}\n\n\t// State & PKCE\n\tsetState(key: string, value: string): void {\n\t\tthis.storage.setItem(`${STORAGE_PREFIX}${key}`, value);\n\t}\n\n\tgetState(key: string): string | null {\n\t\treturn this.storage.getItem(`${STORAGE_PREFIX}${key}`);\n\t}\n\n\tremoveState(key: string): void {\n\t\tthis.storage.removeItem(`${STORAGE_PREFIX}${key}`);\n\t}\n\n\t// PKCE specific\n\tsetCodeVerifier(codeVerifier: string): void {\n\t\tthis.setState('code_verifier', codeVerifier);\n\t}\n\n\tgetCodeVerifier(): string | null {\n\t\treturn this.getState('code_verifier');\n\t}\n\n\tremoveCodeVerifier(): void {\n\t\tthis.removeState('code_verifier');\n\t}\n\n\tsetCodeChallenge(codeChallenge: string): void {\n\t\tthis.setState('code_challenge', codeChallenge);\n\t}\n\n\tgetCodeChallenge(): string | null {\n\t\treturn this.getState('code_challenge');\n\t}\n\n\tremoveCodeChallenge(): void {\n\t\tthis.removeState('code_challenge');\n\t}\n\n\t// Clear all\n\tclear(): void {\n\t\tthis.storage.removeItem(`${STORAGE_PREFIX}tokens`);\n\t\tthis.storage.removeItem(`${STORAGE_PREFIX}token_issued_at`);\n\t\tthis.storage.removeItem(`${STORAGE_PREFIX}user`);\n\t\tthis.removeState('oauth_state');\n\t\tthis.removeCodeVerifier();\n\t\tthis.removeCodeChallenge();\n\t}\n\n\t// Validation\n\thasValidToken(): boolean {\n\t\tconst tokens = this.getTokens();\n\t\tconst issuedAt = this.getTokenIssuedAt();\n\n\t\tif (!tokens || !issuedAt) return false;\n\n\t\tconst now = Date.now();\n\t\tconst expirationTime = issuedAt + tokens.expiresIn * 1000;\n\n\t\treturn now < expirationTime;\n\t}\n}\n","import { AuthTokens, User } from './types';\n\nexport class ApiClient {\n\tprivate baseUrl: string;\n\n\tconstructor(baseUrl: string) {\n\t\tthis.baseUrl = baseUrl;\n\t}\n\n\tasync exchangeCodeForTokens(\n\t\tcode: string,\n\t\tclientId: string,\n\t\tredirectUri: string,\n\t\tcodeVerifier?: string,\n\t\tclientSecret?: string\n\t): Promise<AuthTokens> {\n\t\tconst body: Record<string, string> = {\n\t\t\tgrant_type: 'authorization_code',\n\t\t\tcode,\n\t\t\tclient_id: clientId,\n\t\t\tredirect_uri: redirectUri,\n\t\t};\n\n\t\t// Add PKCE code_verifier if present\n\t\tif (codeVerifier) {\n\t\t\tbody.code_verifier = codeVerifier;\n\t\t}\n\n\t\t// Add client_secret if present (for confidential clients)\n\t\tif (clientSecret) {\n\t\t\tbody.client_secret = clientSecret;\n\t\t}\n\n\t\tconst response = await fetch(`${this.baseUrl}/api/v1/auth/token`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: {\n\t\t\t\t'Content-Type': 'application/x-www-form-urlencoded',\n\t\t\t},\n\t\t\tbody: new URLSearchParams(body).toString(),\n\t\t});\n\n\t\tif (!response.ok) {\n\t\t\tconst errorData = await response.json().catch(() => ({\n\t\t\t\terror: 'unknown_error',\n\t\t\t\terror_description: 'Failed to exchange code for tokens'\n\t\t\t}));\n\n\t\t\tthrow new Error(errorData.error_description || errorData.error || 'Token exchange failed');\n\t\t}\n\n\t\tconst result = await response.json();\n\t\tconst data = result.data || result; // Support both {data: {...}} and direct response\n\n\t\treturn {\n\t\t\taccessToken: data.access_token,\n\t\t\trefreshToken: data.refresh_token,\n\t\t\texpiresIn: data.expires_in,\n\t\t\ttokenType: data.token_type || 'Bearer',\n\t\t};\n\t}\n\n\tasync getUserInfo(accessToken: string): Promise<User> {\n\t\tconst response = await fetch(`${this.baseUrl}/api/v1/system/users/profile`, {\n\t\t\theaders: {\n\t\t\t\tAuthorization: `Bearer ${accessToken}`,\n\t\t\t},\n\t\t});\n\n\t\tif (!response.ok) {\n\t\t\tthrow new Error('Failed to get user info');\n\t\t}\n\n\t\tconst result = await response.json();\n\t\treturn result.data || result;\n\t}\n\n\tasync refreshAccessToken(\n\t\trefreshToken: string,\n\t\tclientId: string,\n\t\tclientSecret?: string\n\t): Promise<AuthTokens> {\n\t\tconst body: Record<string, string> = {\n\t\t\tgrant_type: 'refresh_token',\n\t\t\trefresh_token: refreshToken,\n\t\t\tclient_id: clientId,\n\t\t};\n\n\t\tif (clientSecret) {\n\t\t\tbody.client_secret = clientSecret;\n\t\t}\n\n\t\tconst response = await fetch(`${this.baseUrl}/api/v1/auth/token`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: {\n\t\t\t\t'Content-Type': 'application/x-www-form-urlencoded',\n\t\t\t},\n\t\t\tbody: new URLSearchParams(body).toString(),\n\t\t});\n\n\t\tif (!response.ok) {\n\t\t\tthrow new Error('Failed to refresh token');\n\t\t}\n\n\t\tconst result = await response.json();\n\t\tconst data = result.data || result;\n\n\t\treturn {\n\t\t\taccessToken: data.access_token,\n\t\t\trefreshToken: data.refresh_token || refreshToken, // Keep old refresh token if not provided\n\t\t\texpiresIn: data.expires_in,\n\t\t\ttokenType: data.token_type || 'Bearer',\n\t\t};\n\t}\n\n\tasync logout(accessToken: string): Promise<void> {\n\t\tawait fetch(`${this.baseUrl}/api/v1/auth/revoke`, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: {\n\t\t\t\tAuthorization: `Bearer ${accessToken}`,\n\t\t\t},\n\t\t});\n\t}\n\n\tasync validateToken(accessToken: string): Promise<boolean> {\n\t\ttry {\n\t\t\tconst response = await fetch(`${this.baseUrl}/api/v1/auth/validate`, {\n\t\t\t\theaders: {\n\t\t\t\t\tAuthorization: `Bearer ${accessToken}`,\n\t\t\t\t},\n\t\t\t});\n\t\t\treturn response.ok;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n}\n","export function generateState(): string {\n\tconst array = new Uint8Array(32);\n\tcrypto.getRandomValues(array);\n\treturn Array.from(array, (byte) => byte.toString(16).padStart(2, '0')).join('');\n}\n\nexport function parseQueryString(url: string): Record<string, string> {\n\tconst params: Record<string, string> = {};\n\tconst searchParams = new URL(url).searchParams;\n\tsearchParams.forEach((value, key) => {\n\t\tparams[key] = value;\n\t});\n\treturn params;\n}\n\nexport function isTokenExpired(expiresIn: number, issuedAt: number): boolean {\n\tconst now = Date.now();\n\tconst expirationTime = issuedAt + expiresIn * 1000;\n\t// Refresh 5 minutes before expiration\n\treturn now >= expirationTime - 5 * 60 * 1000;\n}\n\n// PKCE utilities\nexport function generateCodeVerifier(): string {\n\tconst charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~';\n\tconst length = Math.floor(Math.random() * 86) + 43; // 43-128 characters\n\tlet verifier = '';\n\tfor (let i = 0; i < length; i++) {\n\t\tverifier += charset.charAt(Math.floor(Math.random() * charset.length));\n\t}\n\treturn verifier;\n}\n\nexport async function generateCodeChallenge(codeVerifier: string): Promise<string> {\n\tconst encoder = new TextEncoder();\n\tconst data = encoder.encode(codeVerifier);\n\tconst digest = await crypto.subtle.digest('SHA-256', data);\n\treturn base64UrlEncode(digest);\n}\n\nfunction base64UrlEncode(digest: ArrayBuffer): string {\n\tconst bytes = new Uint8Array(digest);\n\tconst binary = Array.from(bytes, (byte) => String.fromCharCode(byte)).join('');\n\treturn btoa(binary)\n\t\t.replace(/\\+/g, '-')\n\t\t.replace(/\\//g, '_')\n\t\t.replace(/=/g, '');\n}\n\nexport function normalizeScope(scope?: string | string[]): string[] {\n\tif (!scope) return ['openid', 'profile', 'email'];\n\tif (Array.isArray(scope)) return scope;\n\tif (typeof scope === 'string') return scope.split(' ');\n\treturn ['openid', 'profile', 'email'];\n}\n","import { CustosConfig, User, AuthState, AuthEvent, AuthEventType } from './types';\nimport { Storage } from './storage';\nimport { ApiClient } from './api';\nimport {\n\tgenerateState,\n\tparseQueryString,\n\tgenerateCodeVerifier,\n\tgenerateCodeChallenge,\n\tnormalizeScope\n} from './utils';\n\nexport class Custos {\n\tprivate config: Required<CustosConfig>;\n\tprivate storage: Storage;\n\tprivate api: ApiClient;\n\tprivate listeners: Map<AuthEventType, Set<(event: AuthEvent) => void>>;\n\tprivate tokenExpiryTimer: any = null;\n\n\tconstructor({ useSessionStorage, ...config }: CustosConfig) {\n\t\t// Normalize scope\n\t\tconst scope = normalizeScope(config.scope);\n\n\t\tthis.config = {\n\t\t\tclientId: config.clientId,\n\t\t\tclientSecret: config.clientSecret || '',\n\t\t\tredirectUri: config.redirectUri,\n\t\t\tapiUrl: config.apiUrl || 'https://custos.alimzen.com',\n\t\t\tscope,\n\t\t\tresponseType: config.responseType || 'code',\n\t\t\tstate: config.state || generateState(),\n\t\t\tusePKCE: config.usePKCE !== false, // Default to true\n\t\t\tcodeChallengeMethod: config.codeChallengeMethod || 'S256',\n\t\t\tgrantType: config.grantType || 'authorization_code',\n\t\t\tuseSessionStorage: useSessionStorage || false\n\t\t};\n\n\t\t// π₯ FIX: Para apps nativas o casos donde se pierde sessionStorage,\n\t\t// usar localStorage para state y PKCE\n\t\tconst storageForState = useSessionStorage ? sessionStorage : localStorage;\n\t\tthis.storage = new Storage(false); // β
SIEMPRE usar localStorage para persistencia\n\t\tthis.api = new ApiClient(this.config.apiUrl);\n\t\tthis.listeners = new Map();\n\n\t\tconsole.log('π§ Custos SDK initialized with localStorage for state persistence');\n\n\t\t// Handle callback automatically\n\t\tif (typeof window !== 'undefined') {\n\t\t\tthis.handleCallback();\n\t\t\tthis.setupTokenExpiryMonitoring();\n\t\t}\n\t}\n\n\t// ==================== Authentication Methods ====================\n\n\tasync login(additionalParams?: Record<string, string>): Promise<void> {\n\t\tconst state = this.config.state;\n\t\t\n\t\tconsole.log('π Starting login flow');\n\t\tconsole.log('π Saving state:', state);\n\t\t\n\t\tthis.storage.setState('oauth_state', state);\n\t\t\n\t\t// Verificar que se guardΓ³ correctamente\n\t\tconst savedState = this.storage.getState('oauth_state');\n\t\tconsole.log('β
State saved successfully:', savedState === state);\n\n\t\tconst params: Record<string, string> = {\n\t\t\tresponse_type: this.config.responseType,\n\t\t\tclient_id: this.config.clientId,\n\t\t\tredirect_uri: this.config.redirectUri,\n\t\t\tscope: Array.isArray(this.config.scope) ? this.config.scope.join(' ') : this.config.scope,\n\t\t\tstate,\n\t\t\t...additionalParams,\n\t\t};\n\n\t\t// Add PKCE if enabled\n\t\tif (this.config.usePKCE) {\n\t\t\tconst codeVerifier = generateCodeVerifier();\n\t\t\tconst codeChallenge = await generateCodeChallenge(codeVerifier);\n\n\t\t\tconsole.log('π PKCE enabled');\n\t\t\tconsole.log('π Saving code_verifier');\n\t\t\t\n\t\t\tthis.storage.setCodeVerifier(codeVerifier);\n\t\t\tthis.storage.setCodeChallenge(codeChallenge);\n\t\t\t\n\t\t\t// Verificar que se guardΓ³\n\t\t\tconst savedVerifier = this.storage.getCodeVerifier();\n\t\t\tconsole.log('β
Code verifier saved:', !!savedVerifier);\n\n\t\t\tparams.code_challenge = codeChallenge;\n\t\t\tparams.code_challenge_method = this.config.codeChallengeMethod;\n\t\t}\n\n\t\tconst authUrl = `${this.config.apiUrl}/v1/auth/authorize?${new URLSearchParams(params)}`;\n\t\tconsole.log('π Redirecting to:', authUrl);\n\t\t\n\t\twindow.location.href = authUrl;\n\t}\n\n\tasync logout(): Promise<void> {\n\t\tconst tokens = this.storage.getTokens();\n\n\t\tif (tokens?.accessToken) {\n\t\t\ttry {\n\t\t\t\tawait this.api.logout(tokens.accessToken);\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error('Logout error:', error);\n\t\t\t}\n\t\t}\n\n\t\tthis.clearTokenExpiryTimer();\n\t\tthis.storage.clear();\n\t\tthis.emit('logout', null);\n\t}\n\n\tasync handleCallback(): Promise<void> {\n\t\tconst params = parseQueryString(window.location.href);\n\n\t\tconsole.log('π Checking for callback params:', {\n\t\t\thasCode: !!params.code,\n\t\t\thasError: !!params.error,\n\t\t\thasState: !!params.state\n\t\t});\n\n\t\t// Check for errors\n\t\tconst error = params.error;\n\t\tif (error) {\n\t\t\tconst errorDescription = params.error_description || error;\n\t\t\tconsole.error('β OAuth error:', error, errorDescription);\n\t\t\tthis.emit('error', { error, error_description: errorDescription });\n\t\t\tthrow new Error(errorDescription);\n\t\t}\n\n\t\t// Check for authorization code\n\t\tconst code = params.code;\n\t\tif (!code) {\n\t\t\tconsole.log('βΉοΈ No authorization code found, skipping callback handling');\n\t\t\treturn;\n\t\t}\n\n\t\tconsole.log('β
Authorization code found');\n\n\t\t// Validate state\n\t\tconst state = params.state;\n\t\tconst savedState = this.storage.getState('oauth_state');\n\t\t\n\t\tconsole.log('π State validation:');\n\t\tconsole.log(' Received state:', state);\n\t\tconsole.log(' Saved state:', savedState);\n\t\tconsole.log(' Match:', state === savedState);\n\n\t\tif (state !== savedState) {\n\t\t\tconsole.error('β State mismatch!');\n\t\t\tconsole.error(' Expected:', savedState);\n\t\t\tconsole.error(' Received:', state);\n\t\t\t\n\t\t\t// π₯ FIX: Si no hay savedState, es probable que se perdiΓ³\n\t\t\tif (!savedState) {\n\t\t\t\tconsole.warn('β οΈ No saved state found. This might be due to:');\n\t\t\t\tconsole.warn(' - App opened in new tab/window');\n\t\t\t\tconsole.warn(' - sessionStorage was cleared');\n\t\t\t\tconsole.warn(' - App was restarted');\n\t\t\t\tconsole.warn('π§ Attempting recovery...');\n\t\t\t\t\n\t\t\t\t// Intentar continuar de todas formas si el code es vΓ‘lido\n\t\t\t\t// (esto es menos seguro pero permite que funcione en apps nativas)\n\t\t\t} else {\n\t\t\t\t// Si hay savedState pero no coincide, es un error de seguridad real\n\t\t\t\tthis.emit('error', { error: 'invalid_state', error_description: 'State parameter mismatch' });\n\t\t\t\tthrow new Error('Invalid state parameter');\n\t\t\t}\n\t\t}\n\n\t\tthis.storage.removeState('oauth_state');\n\n\t\ttry {\n\t\t\t// Get code_verifier if using PKCE\n\t\t\tconst codeVerifier = this.config.usePKCE ? this.storage.getCodeVerifier() || undefined : undefined;\n\t\t\t\n\t\t\tconsole.log('π PKCE code_verifier:', !!codeVerifier);\n\t\t\t\n\t\t\tif (this.config.usePKCE && !codeVerifier) {\n\t\t\t\tconsole.error('β PKCE enabled but no code_verifier found!');\n\t\t\t\tthrow new Error('Code verifier not found. Authentication cannot continue.');\n\t\t\t}\n\n\t\t\tconsole.log('π Exchanging code for tokens...');\n\n\t\t\t// Exchange code for tokens\n\t\t\tconst tokens = await this.api.exchangeCodeForTokens(\n\t\t\t\tcode,\n\t\t\t\tthis.config.clientId,\n\t\t\t\tthis.config.redirectUri,\n\t\t\t\tcodeVerifier,\n\t\t\t\tthis.config.clientSecret\n\t\t\t);\n\n\t\t\tconsole.log('β
Tokens received');\n\t\t\tthis.storage.setTokens(tokens);\n\n\t\t\t// Get user info\n\t\t\tconsole.log('π€ Fetching user info...');\n\t\t\tconst user = await this.api.getUserInfo(tokens.accessToken);\n\t\t\tconsole.log('β
User info received:', user.email);\n\t\t\t\n\t\t\tthis.storage.setUser(user);\n\n\t\t\t// Clean up PKCE data\n\t\t\tif (this.config.usePKCE) {\n\t\t\t\tthis.storage.removeCodeVerifier();\n\t\t\t\tthis.storage.removeCodeChallenge();\n\t\t\t\tconsole.log('π§Ή PKCE data cleaned up');\n\t\t\t}\n\n\t\t\t// Setup token expiry monitoring\n\t\t\tthis.setupTokenExpiryMonitoring();\n\n\t\t\tthis.emit('login', { user, tokens });\n\t\t\tconsole.log('π Login successful!');\n\n\t\t\t// Clean URL (remove query params)\n\t\t\twindow.history.replaceState({}, document.title, window.location.pathname + window.location.hash);\n\t\t} catch (error) {\n\t\t\tconsole.error('β Callback handling error:', error);\n\t\t\tthis.emit('error', error);\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t// ==================== User Methods ====================\n\n\tgetUser(): User | null {\n\t\treturn this.storage.getUser();\n\t}\n\n\tgetAccessToken(): string | null {\n\t\treturn this.storage.getTokens()?.accessToken || null;\n\t}\n\n\tgetRefreshToken(): string | null {\n\t\treturn this.storage.getTokens()?.refreshToken || null;\n\t}\n\n\tisAuthenticated(): boolean {\n\t\treturn this.storage.hasValidToken() && !!this.storage.getUser();\n\t}\n\n\tgetState(): AuthState {\n\t\treturn {\n\t\t\tisAuthenticated: this.isAuthenticated(),\n\t\t\tuser: this.getUser(),\n\t\t\ttokens: this.storage.getTokens(),\n\t\t};\n\t}\n\n\tasync validateToken(): Promise<boolean> {\n\t\tconst accessToken = this.getAccessToken();\n\t\tif (!accessToken) return false;\n\n\t\ttry {\n\t\t\treturn await this.api.validateToken(accessToken);\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t// ==================== Token Refresh ====================\n\n\tprivate setupTokenExpiryMonitoring(): void {\n\t\tthis.clearTokenExpiryTimer();\n\n\t\tconst tokens = this.storage.getTokens();\n\t\tconst issuedAt = this.storage.getTokenIssuedAt();\n\n\t\tif (!tokens || !issuedAt) return;\n\n\t\t// Refresh 5 minutes before expiry\n\t\tconst timeUntilRefresh = (tokens.expiresIn - 300) * 1000; // 5 min buffer\n\n\t\tif (timeUntilRefresh > 0) {\n\t\t\tthis.tokenExpiryTimer = setTimeout(async () => {\n\t\t\t\ttry {\n\t\t\t\t\tawait this.refreshToken();\n\t\t\t\t} catch (error) {\n\t\t\t\t\tthis.emit('token-expired', error);\n\t\t\t\t\tawait this.logout();\n\t\t\t\t}\n\t\t\t}, timeUntilRefresh);\n\t\t}\n\t}\n\n\tprivate clearTokenExpiryTimer(): void {\n\t\tif (this.tokenExpiryTimer) {\n\t\t\tclearTimeout(this.tokenExpiryTimer);\n\t\t\tthis.tokenExpiryTimer = null;\n\t\t}\n\t}\n\n\tasync refreshToken(): Promise<void> {\n\t\tconst tokens = this.storage.getTokens();\n\t\tif (!tokens?.refreshToken) {\n\t\t\tthrow new Error('No refresh token available');\n\t\t}\n\n\t\ttry {\n\t\t\tconst newTokens = await this.api.refreshAccessToken(\n\t\t\t\ttokens.refreshToken,\n\t\t\t\tthis.config.clientId,\n\t\t\t\tthis.config.clientSecret\n\t\t\t);\n\n\t\t\tthis.storage.setTokens(newTokens);\n\t\t\tthis.setupTokenExpiryMonitoring();\n\t\t\tthis.emit('token-refresh', newTokens);\n\t\t} catch (error) {\n\t\t\tthis.emit('error', error);\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t// ==================== Event Handling ====================\n\n\ton(event: AuthEventType, callback: (event: AuthEvent) => void): void {\n\t\tif (!this.listeners.has(event)) {\n\t\t\tthis.listeners.set(event, new Set());\n\t\t}\n\t\tthis.listeners.get(event)!.add(callback);\n\t}\n\n\toff(event: AuthEventType, callback: (event: AuthEvent) => void): void {\n\t\tthis.listeners.get(event)?.delete(callback);\n\t}\n\n\tprivate emit(type: AuthEventType, data?: any): void {\n\t\tconst event: AuthEvent = { type, data };\n\t\tthis.listeners.get(type)?.forEach((callback) => callback(event));\n\t}\n\n\t// ==================== Utility Methods ====================\n\n\tclearStorage(): void {\n\t\tthis.storage.clear();\n\t}\n\n\tdestroy(): void {\n\t\tthis.clearTokenExpiryTimer();\n\t\tthis.listeners.clear();\n\t}\n}"],"names":["STORAGE_PREFIX","Storage","constructor","useSessionStorage","this","storage","sessionStorage","localStorage","setTokens","tokens","setItem","JSON","stringify","Date","now","toString","getTokens","data","getItem","parse","getTokenIssuedAt","parseInt","setUser","user","getUser","setState","key","value","getState","removeState","removeItem","setCodeVerifier","codeVerifier","getCodeVerifier","removeCodeVerifier","setCodeChallenge","codeChallenge","getCodeChallenge","removeCodeChallenge","clear","hasValidToken","issuedAt","expiresIn","ApiClient","baseUrl","exchangeCodeForTokens","code","clientId","redirectUri","clientSecret","body","grant_type","client_id","redirect_uri","code_verifier","client_secret","response","fetch","method","headers","URLSearchParams","ok","errorData","json","catch","error","error_description","Error","result","accessToken","access_token","refreshToken","refresh_token","expires_in","tokenType","token_type","getUserInfo","Authorization","refreshAccessToken","logout","validateToken","generateState","array","Uint8Array","crypto","getRandomValues","Array","from","byte","padStart","join","async","generateCodeChallenge","TextEncoder","encode","digest","bytes","binary","String","fromCharCode","btoa","replace","base64UrlEncode","subtle","config","tokenExpiryTimer","scope","isArray","split","normalizeScope","apiUrl","responseType","state","usePKCE","codeChallengeMethod","grantType","api","listeners","Map","console","log","window","handleCallback","setupTokenExpiryMonitoring","login","additionalParams","savedState","params","response_type","charset","length","Math","floor","random","verifier","i","charAt","generateCodeVerifier","savedVerifier","code_challenge","code_challenge_method","authUrl","location","href","clearTokenExpiryTimer","emit","url","URL","searchParams","forEach","parseQueryString","hasCode","hasError","hasState","errorDescription","warn","undefined","email","history","replaceState","document","title","pathname","hash","getAccessToken","getRefreshToken","isAuthenticated","timeUntilRefresh","setTimeout","clearTimeout","newTokens","on","event","callback","has","set","Set","get","add","off","delete","type","clearStorage","destroy"],"mappings":"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"}
|
package/dist/storage.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAI3C,qBAAa,OAAO;IACnB,OAAO,CAAC,OAAO,CAAqB;gBAExB,iBAAiB,UAAQ;
|
|
1
|
+
{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAI3C,qBAAa,OAAO;IACnB,OAAO,CAAC,OAAO,CAAqB;gBAExB,iBAAiB,UAAQ;IAKrC,SAAS,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;IAKnC,SAAS,IAAI,UAAU,GAAG,IAAI;IAK9B,gBAAgB,IAAI,MAAM,GAAG,IAAI;IAMjC,OAAO,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI;IAIzB,OAAO,IAAI,IAAI,GAAG,IAAI;IAMtB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAI1C,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAIpC,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAK9B,eAAe,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI;IAI3C,eAAe,IAAI,MAAM,GAAG,IAAI;IAIhC,kBAAkB,IAAI,IAAI;IAI1B,gBAAgB,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI;IAI7C,gBAAgB,IAAI,MAAM,GAAG,IAAI;IAIjC,mBAAmB,IAAI,IAAI;IAK3B,KAAK,IAAI,IAAI;IAUb,aAAa,IAAI,OAAO;CAWxB"}
|