@isaias_pv/custos-sdk 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Custos.d.ts +8 -4
- package/dist/Custos.d.ts.map +1 -1
- package/dist/api.d.ts +2 -1
- package/dist/api.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 +9 -1
- package/dist/storage.d.ts.map +1 -1
- package/dist/types.d.ts +11 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/utils.d.ts +3 -0
- package/dist/utils.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/Custos.d.ts
CHANGED
|
@@ -4,20 +4,24 @@ export declare class Custos {
|
|
|
4
4
|
private storage;
|
|
5
5
|
private api;
|
|
6
6
|
private listeners;
|
|
7
|
-
private
|
|
7
|
+
private tokenExpiryTimer;
|
|
8
8
|
constructor(config: CustosConfig);
|
|
9
|
-
login(): Promise<void>;
|
|
9
|
+
login(additionalParams?: Record<string, string>): Promise<void>;
|
|
10
10
|
logout(): Promise<void>;
|
|
11
11
|
handleCallback(): Promise<void>;
|
|
12
12
|
getUser(): User | null;
|
|
13
13
|
getAccessToken(): string | null;
|
|
14
|
+
getRefreshToken(): string | null;
|
|
14
15
|
isAuthenticated(): boolean;
|
|
15
16
|
getState(): AuthState;
|
|
16
|
-
|
|
17
|
-
private
|
|
17
|
+
validateToken(): Promise<boolean>;
|
|
18
|
+
private setupTokenExpiryMonitoring;
|
|
19
|
+
private clearTokenExpiryTimer;
|
|
18
20
|
refreshToken(): Promise<void>;
|
|
19
21
|
on(event: AuthEventType, callback: (event: AuthEvent) => void): void;
|
|
20
22
|
off(event: AuthEventType, callback: (event: AuthEvent) => void): void;
|
|
21
23
|
private emit;
|
|
24
|
+
clearStorage(): void;
|
|
25
|
+
destroy(): void;
|
|
22
26
|
}
|
|
23
27
|
//# sourceMappingURL=Custos.d.ts.map
|
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;
|
|
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,MAAM,EAAE,YAAY;IA8B1B,KAAK,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IA6B/D,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAgBvB,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IAiErC,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/api.d.ts
CHANGED
|
@@ -2,9 +2,10 @@ import { AuthTokens, User } from './types';
|
|
|
2
2
|
export declare class ApiClient {
|
|
3
3
|
private baseUrl;
|
|
4
4
|
constructor(baseUrl: string);
|
|
5
|
-
exchangeCodeForTokens(code: string, clientId: string, redirectUri: string, clientSecret?: string): Promise<AuthTokens>;
|
|
5
|
+
exchangeCodeForTokens(code: string, clientId: string, redirectUri: string, codeVerifier?: string, clientSecret?: string): Promise<AuthTokens>;
|
|
6
6
|
getUserInfo(accessToken: string): Promise<User>;
|
|
7
7
|
refreshAccessToken(refreshToken: string, clientId: string, clientSecret?: string): Promise<AuthTokens>;
|
|
8
8
|
logout(accessToken: string): Promise<void>;
|
|
9
|
+
validateToken(accessToken: string): Promise<boolean>;
|
|
9
10
|
}
|
|
10
11
|
//# sourceMappingURL=api.d.ts.map
|
package/dist/api.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAE3C,qBAAa,SAAS;IACrB,OAAO,CAAC,OAAO,CAAS;gBAEZ,OAAO,EAAE,MAAM;IAIrB,qBAAqB,CAC1B,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EACnB,YAAY,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAE3C,qBAAa,SAAS;IACrB,OAAO,CAAC,OAAO,CAAS;gBAEZ,OAAO,EAAE,MAAM;IAIrB,qBAAqB,CAC1B,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EACnB,YAAY,CAAC,EAAE,MAAM,EACrB,YAAY,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,UAAU,CAAC;IA8ChB,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAe/C,kBAAkB,CACvB,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,MAAM,EAChB,YAAY,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,UAAU,CAAC;IAkChB,MAAM,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAS1C,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAY1D"}
|
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}setTokens(t){this.storage.setItem(`${e}tokens`,JSON.stringify(t))}getTokens(){const t=this.storage.getItem(`${e}tokens`);return t?JSON.parse(t):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}
|
|
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,r){this.storage.setItem(`${e}${t}`,r)}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 r{constructor(e){this.baseUrl=e}async exchangeCodeForTokens(e,t,r,s,o){const i={grant_type:"authorization_code",code:e,client_id:t,redirect_uri:r};s&&(i.code_verifier=s),o&&(i.client_secret=o);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/auth/userinfo`,{headers:{Authorization:`Bearer ${e}`}});if(!t.ok)throw new Error("Failed to get user info");const r=await t.json();return r.data||r}async refreshAccessToken(e,t,r){const s={grant_type:"refresh_token",refresh_token:e,client_id:t};r&&(s.client_secret=r);const o=await fetch(`${this.baseUrl}/api/v1/auth/token`,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams(s).toString()});if(!o.ok)throw new Error("Failed to refresh token");const i=await o.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 o(e){const t=(new TextEncoder).encode(e);return function(e){const t=new Uint8Array(e),r=Array.from(t,e=>String.fromCharCode(e)).join("");return btoa(r).replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}(await crypto.subtle.digest("SHA-256",t))}exports.Custos=class{constructor(e){this.tokenExpiryTimer=null;const o=function(e){return e?Array.isArray(e)?e:"string"==typeof e?e.split(" "):["openid","profile","email"]:["openid","profile","email"]}(e.scope);this.config={clientId:e.clientId,clientSecret:e.clientSecret||"",redirectUri:e.redirectUri,apiUrl:e.apiUrl||"https://custos.alimzen.com",scope:o,responseType:e.responseType||"code",state:e.state||s(),usePKCE:!1!==e.usePKCE,codeChallengeMethod:e.codeChallengeMethod||"S256",grantType:e.grantType||"authorization_code"},this.storage=new t,this.api=new r(this.config.apiUrl),this.listeners=new Map,"undefined"!=typeof window&&(this.handleCallback(),this.setupTokenExpiryMonitoring())}async login(e){const t=this.config.state;this.storage.setState("oauth_state",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 r="";for(let s=0;s<t;s++)r+=e.charAt(Math.floor(66*Math.random()));return r}(),t=await o(e);this.storage.setCodeVerifier(e),this.storage.setCodeChallenge(t),r.code_challenge=t,r.code_challenge_method=this.config.codeChallengeMethod}const s=`${this.config.apiUrl}/v1/auth/authorize?${new URLSearchParams(r)}`;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,r)=>{t[r]=e}),t}(window.location.href),t=e.error;if(t){const r=e.error_description||t;throw this.emit("error",{error:t,error_description:r}),new Error(r)}const r=e.code;if(!r)return;if(e.state!==this.storage.getState("oauth_state"))throw this.emit("error",{error:"invalid_state",error_description:"State parameter mismatch"}),new Error("Invalid state parameter");this.storage.removeState("oauth_state");try{const e=this.config.usePKCE&&this.storage.getCodeVerifier()||void 0,t=await this.api.exchangeCodeForTokens(r,this.config.clientId,this.config.redirectUri,e,this.config.clientSecret);this.storage.setTokens(t);const s=await this.api.getUserInfo(t.accessToken);this.storage.setUser(s),this.config.usePKCE&&(this.storage.removeCodeVerifier(),this.storage.removeCodeChallenge()),this.setupTokenExpiryMonitoring(),this.emit("login",{user:s,tokens:t}),window.history.replaceState({},document.title,window.location.pathname+window.location.hash)}catch(t){throw 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 r=1e3*(e.expiresIn-300);r>0&&(this.tokenExpiryTimer=setTimeout(async()=>{try{await this.refreshToken()}catch(e){this.emit("token-expired",e),await this.logout()}},r))}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 r={type:e,data:t};this.listeners.get(e)?.forEach(e=>e(r))}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/Custos.ts","../src/utils.ts"],"sourcesContent":["import { AuthTokens, User } from './types';\r\n\r\nconst STORAGE_PREFIX = 'custos_';\r\n\r\nexport class Storage {\r\n\tprivate storage: globalThis.Storage;\r\n\r\n\tconstructor(useSessionStorage = false) {\r\n\t\tthis.storage = useSessionStorage ? sessionStorage : localStorage;\r\n\t}\r\n\r\n\tsetTokens(tokens: AuthTokens): void {\r\n\t\tthis.storage.setItem(`${STORAGE_PREFIX}tokens`, JSON.stringify(tokens));\r\n\t}\r\n\r\n\tgetTokens(): AuthTokens | null {\r\n\t\tconst data = this.storage.getItem(`${STORAGE_PREFIX}tokens`);\r\n\t\treturn data ? JSON.parse(data) : null;\r\n\t}\r\n\r\n\tsetUser(user: User): void {\r\n\t\tthis.storage.setItem(`${STORAGE_PREFIX}user`, JSON.stringify(user));\r\n\t}\r\n\r\n\tgetUser(): User | null {\r\n\t\tconst data = this.storage.getItem(`${STORAGE_PREFIX}user`);\r\n\t\treturn data ? JSON.parse(data) : null;\r\n\t}\r\n\r\n\tclear(): void {\r\n\t\tthis.storage.removeItem(`${STORAGE_PREFIX}tokens`);\r\n\t\tthis.storage.removeItem(`${STORAGE_PREFIX}user`);\r\n\t}\r\n\r\n\tsetState(key: string, value: string): void {\r\n\t\tthis.storage.setItem(`${STORAGE_PREFIX}${key}`, value);\r\n\t}\r\n\r\n\tgetState(key: string): string | null {\r\n\t\treturn this.storage.getItem(`${STORAGE_PREFIX}${key}`);\r\n\t}\r\n\r\n\tremoveState(key: string): void {\r\n\t\tthis.storage.removeItem(`${STORAGE_PREFIX}${key}`);\r\n\t}\r\n}","import { AuthTokens, User } from './types';\r\n\r\nexport class ApiClient {\r\n\tprivate baseUrl: string;\r\n\r\n\tconstructor(baseUrl: string) {\r\n\t\tthis.baseUrl = baseUrl;\r\n\t}\r\n\r\n\tasync exchangeCodeForTokens(\r\n\t\tcode: string,\r\n\t\tclientId: string,\r\n\t\tredirectUri: string,\r\n\t\tclientSecret?: string\r\n\t): Promise<AuthTokens> {\r\n\t\tconst response = await fetch(`${this.baseUrl}/oauth/token`, {\r\n\t\t\tmethod: 'POST',\r\n\t\t\theaders: {\r\n\t\t\t\t'Content-Type': 'application/json',\r\n\t\t\t},\r\n\t\t\tbody: JSON.stringify({\r\n\t\t\t\tgrant_type: 'authorization_code',\r\n\t\t\t\tcode,\r\n\t\t\t\tclient_id: clientId,\r\n\t\t\t\tclient_secret: clientSecret,\r\n\t\t\t\tredirect_uri: redirectUri,\r\n\t\t\t}),\r\n\t\t});\r\n\r\n\t\tif (!response.ok) {\r\n\t\t\tthrow new Error('Failed to exchange code for tokens');\r\n\t\t}\r\n\r\n\t\tconst data = await response.json();\r\n\t\treturn {\r\n\t\t\taccessToken: data.access_token,\r\n\t\t\trefreshToken: data.refresh_token,\r\n\t\t\texpiresIn: data.expires_in,\r\n\t\t\ttokenType: data.token_type,\r\n\t\t};\r\n\t}\r\n\r\n\tasync getUserInfo(accessToken: string): Promise<User> {\r\n\t\tconst response = await fetch(`${this.baseUrl}/oauth/userinfo`, {\r\n\t\t\theaders: {\r\n\t\t\t\tAuthorization: `Bearer ${accessToken}`,\r\n\t\t\t},\r\n\t\t});\r\n\r\n\t\tif (!response.ok) {\r\n\t\t\tthrow new Error('Failed to get user info');\r\n\t\t}\r\n\r\n\t\treturn response.json();\r\n\t}\r\n\r\n\tasync refreshAccessToken(\r\n\t\trefreshToken: string,\r\n\t\tclientId: string,\r\n\t\tclientSecret?: string\r\n\t): Promise<AuthTokens> {\r\n\t\tconst response = await fetch(`${this.baseUrl}/oauth/token`, {\r\n\t\t\tmethod: 'POST',\r\n\t\t\theaders: {\r\n\t\t\t\t'Content-Type': 'application/json',\r\n\t\t\t},\r\n\t\t\tbody: JSON.stringify({\r\n\t\t\t\tgrant_type: 'refresh_token',\r\n\t\t\t\trefresh_token: refreshToken,\r\n\t\t\t\tclient_id: clientId,\r\n\t\t\t\tclient_secret: clientSecret,\r\n\t\t\t}),\r\n\t\t});\r\n\r\n\t\tif (!response.ok) {\r\n\t\t\tthrow new Error('Failed to refresh token');\r\n\t\t}\r\n\r\n\t\tconst data = await response.json();\r\n\t\treturn {\r\n\t\t\taccessToken: data.access_token,\r\n\t\t\trefreshToken: data.refresh_token,\r\n\t\t\texpiresIn: data.expires_in,\r\n\t\t\ttokenType: data.token_type,\r\n\t\t};\r\n\t}\r\n\r\n\tasync logout(accessToken: string): Promise<void> {\r\n\t\tawait fetch(`${this.baseUrl}/oauth/revoke`, {\r\n\t\t\tmethod: 'POST',\r\n\t\t\theaders: {\r\n\t\t\t\tAuthorization: `Bearer ${accessToken}`,\r\n\t\t\t},\r\n\t\t});\r\n\t}\r\n}","import { CustosConfig, User, AuthState, AuthEvent, AuthEventType } from './types';\r\nimport { Storage } from './storage';\r\nimport { ApiClient } from './api';\r\nimport { generateState, parseQueryString, isTokenExpired } from './utils';\r\n\r\nexport class Custos {\r\n\tprivate config: Required<CustosConfig>;\r\n\tprivate storage: Storage;\r\n\tprivate api: ApiClient;\r\n\tprivate listeners: Map<AuthEventType, Set<(event: AuthEvent) => void>>;\r\n\tprivate tokenIssuedAt: number | null = null;\r\n\r\n\tconstructor(config: CustosConfig) {\r\n\t\tthis.config = {\r\n\t\t\tclientId: config.clientId,\r\n\t\t\tclientSecret: config.clientSecret || '',\r\n\t\t\tredirectUri: config.redirectUri,\r\n\t\t\tapiUrl: config.apiUrl || 'https://custos.alimzen.com',\r\n\t\t\tscope: config.scope || ['openid', 'profile', 'email'],\r\n\t\t};\r\n\r\n\t\tthis.storage = new Storage();\r\n\t\tthis.api = new ApiClient(this.config.apiUrl);\r\n\t\tthis.listeners = new Map();\r\n\r\n\t\t// Handle callback automatically\r\n\t\tthis.handleCallback();\r\n\r\n\t\t// Setup token refresh\r\n\t\tthis.setupTokenRefresh();\r\n\t}\r\n\r\n\t// Authentication Methods\r\n\tasync login(): Promise<void> {\r\n\t\tconst state = generateState();\r\n\t\tthis.storage.setState('oauth_state', state);\r\n\r\n\t\tconst params = new URLSearchParams({\r\n\t\t\tresponse_type: 'code',\r\n\t\t\tclient_id: this.config.clientId,\r\n\t\t\tredirect_uri: this.config.redirectUri,\r\n\t\t\tscope: this.config.scope.join(' '),\r\n\t\t\tstate,\r\n\t\t});\r\n\r\n\t\twindow.location.href = `${this.config.apiUrl}/oauth/authorize?${params}`;\r\n\t}\r\n\r\n\tasync logout(): Promise<void> {\r\n\t\tconst tokens = this.storage.getTokens();\r\n\r\n\t\tif (tokens?.accessToken) {\r\n\t\t\ttry {\r\n\t\t\t\tawait this.api.logout(tokens.accessToken);\r\n\t\t\t} catch (error) {\r\n\t\t\t\tconsole.error('Logout error:', error);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tthis.storage.clear();\r\n\t\tthis.emit('logout', null);\r\n\t}\r\n\r\n\tasync handleCallback(): Promise<void> {\r\n\t\tconst params = parseQueryString(window.location.href);\r\n\t\tconst code = params.code;\r\n\t\tconst state = params.state;\r\n\r\n\t\tif (!code) return;\r\n\r\n\t\tconst savedState = this.storage.getState('oauth_state');\r\n\t\tif (state !== savedState) {\r\n\t\t\tthrow new Error('Invalid state parameter');\r\n\t\t}\r\n\r\n\t\tthis.storage.removeState('oauth_state');\r\n\r\n\t\ttry {\r\n\t\t\tconst tokens = await this.api.exchangeCodeForTokens(\r\n\t\t\t\tcode,\r\n\t\t\t\tthis.config.clientId,\r\n\t\t\t\tthis.config.redirectUri,\r\n\t\t\t\tthis.config.clientSecret\r\n\t\t\t);\r\n\r\n\t\t\tthis.tokenIssuedAt = Date.now();\r\n\t\t\tthis.storage.setTokens(tokens);\r\n\r\n\t\t\tconst user = await this.api.getUserInfo(tokens.accessToken);\r\n\t\t\tthis.storage.setUser(user);\r\n\r\n\t\t\tthis.emit('login', { user, tokens });\r\n\r\n\t\t\t// Clean URL\r\n\t\t\twindow.history.replaceState({}, document.title, window.location.pathname);\r\n\t\t} catch (error) {\r\n\t\t\tthis.emit('error', error);\r\n\t\t\tthrow error;\r\n\t\t}\r\n\t}\r\n\r\n\t// User Methods\r\n\tgetUser(): User | null {\r\n\t\treturn this.storage.getUser();\r\n\t}\r\n\r\n\tgetAccessToken(): string | null {\r\n\t\treturn this.storage.getTokens()?.accessToken || null;\r\n\t}\r\n\r\n\tisAuthenticated(): boolean {\r\n\t\treturn !!this.storage.getTokens() && !!this.storage.getUser();\r\n\t}\r\n\r\n\tgetState(): AuthState {\r\n\t\treturn {\r\n\t\t\tisAuthenticated: this.isAuthenticated(),\r\n\t\t\tuser: this.getUser(),\r\n\t\t\ttokens: this.storage.getTokens(),\r\n\t\t};\r\n\t}\r\n\r\n\t// Token Refresh\r\n\tprivate setupTokenRefresh(): void {\r\n\t\tsetInterval(async () => {\r\n\t\t\tif (this.shouldRefreshToken()) {\r\n\t\t\t\tawait this.refreshToken();\r\n\t\t\t}\r\n\t\t}, 60000); // Check every minute\r\n\t}\r\n\r\n\tprivate shouldRefreshToken(): boolean {\r\n\t\tconst tokens = this.storage.getTokens();\r\n\t\tif (!tokens || !this.tokenIssuedAt) return false;\r\n\r\n\t\treturn isTokenExpired(tokens.expiresIn, this.tokenIssuedAt);\r\n\t}\r\n\r\n\tasync refreshToken(): Promise<void> {\r\n\t\tconst tokens = this.storage.getTokens();\r\n\t\tif (!tokens?.refreshToken) {\r\n\t\t\tthrow new Error('No refresh token available');\r\n\t\t}\r\n\r\n\t\ttry {\r\n\t\t\tconst newTokens = await this.api.refreshAccessToken(\r\n\t\t\t\ttokens.refreshToken,\r\n\t\t\t\tthis.config.clientId,\r\n\t\t\t\tthis.config.clientSecret\r\n\t\t\t);\r\n\r\n\t\t\tthis.tokenIssuedAt = Date.now();\r\n\t\t\tthis.storage.setTokens(newTokens);\r\n\r\n\t\t\tthis.emit('token-refresh', newTokens);\r\n\t\t} catch (error) {\r\n\t\t\tthis.emit('error', error);\r\n\t\t\t// If refresh fails, logout\r\n\t\t\tawait this.logout();\r\n\t\t\tthrow error;\r\n\t\t}\r\n\t}\r\n\r\n\t// Event Handling\r\n\ton(event: AuthEventType, callback: (event: AuthEvent) => void): void {\r\n\t\tif (!this.listeners.has(event)) {\r\n\t\t\tthis.listeners.set(event, new Set());\r\n\t\t}\r\n\t\tthis.listeners.get(event)!.add(callback);\r\n\t}\r\n\r\n\toff(event: AuthEventType, callback: (event: AuthEvent) => void): void {\r\n\t\tthis.listeners.get(event)?.delete(callback);\r\n\t}\r\n\r\n\tprivate emit(type: AuthEventType, data?: any): void {\r\n\t\tconst event: AuthEvent = { type, data };\r\n\t\tthis.listeners.get(type)?.forEach((callback) => callback(event));\r\n\t}\r\n}","export function generateState(): string {\r\n\tconst array = new Uint8Array(32);\r\n\tcrypto.getRandomValues(array);\r\n\treturn Array.from(array, (byte) => byte.toString(16).padStart(2, '0')).join('');\r\n}\r\n\r\nexport function parseQueryString(url: string): Record<string, string> {\r\n\tconst params: Record<string, string> = {};\r\n\tconst searchParams = new URL(url).searchParams;\r\n\tsearchParams.forEach((value, key) => {\r\n\t\tparams[key] = value;\r\n\t});\r\n\treturn params;\r\n}\r\n\r\nexport function isTokenExpired(expiresIn: number, issuedAt: number): boolean {\r\n\tconst now = Date.now();\r\n\tconst expirationTime = issuedAt + expiresIn * 1000;\r\n\t// Refresh 5 minutes before expiration\r\n\treturn now >= expirationTime - 5 * 60 * 1000;\r\n}"],"names":["STORAGE_PREFIX","Storage","constructor","useSessionStorage","this","storage","sessionStorage","localStorage","setTokens","tokens","setItem","JSON","stringify","getTokens","data","getItem","parse","setUser","user","getUser","clear","removeItem","setState","key","value","getState","removeState","ApiClient","baseUrl","exchangeCodeForTokens","code","clientId","redirectUri","clientSecret","response","fetch","method","headers","body","grant_type","client_id","client_secret","redirect_uri","ok","Error","json","accessToken","access_token","refreshToken","refresh_token","expiresIn","expires_in","tokenType","token_type","getUserInfo","Authorization","refreshAccessToken","logout","config","tokenIssuedAt","apiUrl","scope","api","listeners","Map","handleCallback","setupTokenRefresh","login","state","array","Uint8Array","crypto","getRandomValues","Array","from","byte","toString","padStart","join","generateState","params","URLSearchParams","response_type","window","location","href","error","console","emit","url","URL","searchParams","forEach","parseQueryString","Date","now","history","replaceState","document","title","pathname","getAccessToken","isAuthenticated","setInterval","async","shouldRefreshToken","issuedAt","newTokens","on","event","callback","has","set","Set","get","add","off","delete","type"],"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/auth/userinfo`, {\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(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};\n\n\t\tthis.storage = new Storage();\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\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\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","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","console","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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"}
|
package/dist/index.esm.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e="custos_";class t{constructor(e=!1){this.storage=e?sessionStorage:localStorage}setTokens(t){this.storage.setItem(`${e}tokens`,JSON.stringify(t))}getTokens(){const t=this.storage.getItem(`${e}tokens`);return t?JSON.parse(t):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}
|
|
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,r){this.storage.setItem(`${e}${t}`,r)}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 r{constructor(e){this.baseUrl=e}async exchangeCodeForTokens(e,t,r,s,o){const i={grant_type:"authorization_code",code:e,client_id:t,redirect_uri:r};s&&(i.code_verifier=s),o&&(i.client_secret=o);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/auth/userinfo`,{headers:{Authorization:`Bearer ${e}`}});if(!t.ok)throw new Error("Failed to get user info");const r=await t.json();return r.data||r}async refreshAccessToken(e,t,r){const s={grant_type:"refresh_token",refresh_token:e,client_id:t};r&&(s.client_secret=r);const o=await fetch(`${this.baseUrl}/api/v1/auth/token`,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams(s).toString()});if(!o.ok)throw new Error("Failed to refresh token");const i=await o.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 o(e){const t=(new TextEncoder).encode(e);return function(e){const t=new Uint8Array(e),r=Array.from(t,e=>String.fromCharCode(e)).join("");return btoa(r).replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}(await crypto.subtle.digest("SHA-256",t))}class i{constructor(e){this.tokenExpiryTimer=null;const o=function(e){return e?Array.isArray(e)?e:"string"==typeof e?e.split(" "):["openid","profile","email"]:["openid","profile","email"]}(e.scope);this.config={clientId:e.clientId,clientSecret:e.clientSecret||"",redirectUri:e.redirectUri,apiUrl:e.apiUrl||"https://custos.alimzen.com",scope:o,responseType:e.responseType||"code",state:e.state||s(),usePKCE:!1!==e.usePKCE,codeChallengeMethod:e.codeChallengeMethod||"S256",grantType:e.grantType||"authorization_code"},this.storage=new t,this.api=new r(this.config.apiUrl),this.listeners=new Map,"undefined"!=typeof window&&(this.handleCallback(),this.setupTokenExpiryMonitoring())}async login(e){const t=this.config.state;this.storage.setState("oauth_state",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 r="";for(let s=0;s<t;s++)r+=e.charAt(Math.floor(66*Math.random()));return r}(),t=await o(e);this.storage.setCodeVerifier(e),this.storage.setCodeChallenge(t),r.code_challenge=t,r.code_challenge_method=this.config.codeChallengeMethod}const s=`${this.config.apiUrl}/v1/auth/authorize?${new URLSearchParams(r)}`;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,r)=>{t[r]=e}),t}(window.location.href),t=e.error;if(t){const r=e.error_description||t;throw this.emit("error",{error:t,error_description:r}),new Error(r)}const r=e.code;if(!r)return;if(e.state!==this.storage.getState("oauth_state"))throw this.emit("error",{error:"invalid_state",error_description:"State parameter mismatch"}),new Error("Invalid state parameter");this.storage.removeState("oauth_state");try{const e=this.config.usePKCE&&this.storage.getCodeVerifier()||void 0,t=await this.api.exchangeCodeForTokens(r,this.config.clientId,this.config.redirectUri,e,this.config.clientSecret);this.storage.setTokens(t);const s=await this.api.getUserInfo(t.accessToken);this.storage.setUser(s),this.config.usePKCE&&(this.storage.removeCodeVerifier(),this.storage.removeCodeChallenge()),this.setupTokenExpiryMonitoring(),this.emit("login",{user:s,tokens:t}),window.history.replaceState({},document.title,window.location.pathname+window.location.hash)}catch(t){throw 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 r=1e3*(e.expiresIn-300);r>0&&(this.tokenExpiryTimer=setTimeout(async()=>{try{await this.refreshToken()}catch(e){this.emit("token-expired",e),await this.logout()}},r))}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 r={type:e,data:t};this.listeners.get(e)?.forEach(e=>e(r))}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/Custos.ts","../src/utils.ts"],"sourcesContent":["import { AuthTokens, User } from './types';\r\n\r\nconst STORAGE_PREFIX = 'custos_';\r\n\r\nexport class Storage {\r\n\tprivate storage: globalThis.Storage;\r\n\r\n\tconstructor(useSessionStorage = false) {\r\n\t\tthis.storage = useSessionStorage ? sessionStorage : localStorage;\r\n\t}\r\n\r\n\tsetTokens(tokens: AuthTokens): void {\r\n\t\tthis.storage.setItem(`${STORAGE_PREFIX}tokens`, JSON.stringify(tokens));\r\n\t}\r\n\r\n\tgetTokens(): AuthTokens | null {\r\n\t\tconst data = this.storage.getItem(`${STORAGE_PREFIX}tokens`);\r\n\t\treturn data ? JSON.parse(data) : null;\r\n\t}\r\n\r\n\tsetUser(user: User): void {\r\n\t\tthis.storage.setItem(`${STORAGE_PREFIX}user`, JSON.stringify(user));\r\n\t}\r\n\r\n\tgetUser(): User | null {\r\n\t\tconst data = this.storage.getItem(`${STORAGE_PREFIX}user`);\r\n\t\treturn data ? JSON.parse(data) : null;\r\n\t}\r\n\r\n\tclear(): void {\r\n\t\tthis.storage.removeItem(`${STORAGE_PREFIX}tokens`);\r\n\t\tthis.storage.removeItem(`${STORAGE_PREFIX}user`);\r\n\t}\r\n\r\n\tsetState(key: string, value: string): void {\r\n\t\tthis.storage.setItem(`${STORAGE_PREFIX}${key}`, value);\r\n\t}\r\n\r\n\tgetState(key: string): string | null {\r\n\t\treturn this.storage.getItem(`${STORAGE_PREFIX}${key}`);\r\n\t}\r\n\r\n\tremoveState(key: string): void {\r\n\t\tthis.storage.removeItem(`${STORAGE_PREFIX}${key}`);\r\n\t}\r\n}","import { AuthTokens, User } from './types';\r\n\r\nexport class ApiClient {\r\n\tprivate baseUrl: string;\r\n\r\n\tconstructor(baseUrl: string) {\r\n\t\tthis.baseUrl = baseUrl;\r\n\t}\r\n\r\n\tasync exchangeCodeForTokens(\r\n\t\tcode: string,\r\n\t\tclientId: string,\r\n\t\tredirectUri: string,\r\n\t\tclientSecret?: string\r\n\t): Promise<AuthTokens> {\r\n\t\tconst response = await fetch(`${this.baseUrl}/oauth/token`, {\r\n\t\t\tmethod: 'POST',\r\n\t\t\theaders: {\r\n\t\t\t\t'Content-Type': 'application/json',\r\n\t\t\t},\r\n\t\t\tbody: JSON.stringify({\r\n\t\t\t\tgrant_type: 'authorization_code',\r\n\t\t\t\tcode,\r\n\t\t\t\tclient_id: clientId,\r\n\t\t\t\tclient_secret: clientSecret,\r\n\t\t\t\tredirect_uri: redirectUri,\r\n\t\t\t}),\r\n\t\t});\r\n\r\n\t\tif (!response.ok) {\r\n\t\t\tthrow new Error('Failed to exchange code for tokens');\r\n\t\t}\r\n\r\n\t\tconst data = await response.json();\r\n\t\treturn {\r\n\t\t\taccessToken: data.access_token,\r\n\t\t\trefreshToken: data.refresh_token,\r\n\t\t\texpiresIn: data.expires_in,\r\n\t\t\ttokenType: data.token_type,\r\n\t\t};\r\n\t}\r\n\r\n\tasync getUserInfo(accessToken: string): Promise<User> {\r\n\t\tconst response = await fetch(`${this.baseUrl}/oauth/userinfo`, {\r\n\t\t\theaders: {\r\n\t\t\t\tAuthorization: `Bearer ${accessToken}`,\r\n\t\t\t},\r\n\t\t});\r\n\r\n\t\tif (!response.ok) {\r\n\t\t\tthrow new Error('Failed to get user info');\r\n\t\t}\r\n\r\n\t\treturn response.json();\r\n\t}\r\n\r\n\tasync refreshAccessToken(\r\n\t\trefreshToken: string,\r\n\t\tclientId: string,\r\n\t\tclientSecret?: string\r\n\t): Promise<AuthTokens> {\r\n\t\tconst response = await fetch(`${this.baseUrl}/oauth/token`, {\r\n\t\t\tmethod: 'POST',\r\n\t\t\theaders: {\r\n\t\t\t\t'Content-Type': 'application/json',\r\n\t\t\t},\r\n\t\t\tbody: JSON.stringify({\r\n\t\t\t\tgrant_type: 'refresh_token',\r\n\t\t\t\trefresh_token: refreshToken,\r\n\t\t\t\tclient_id: clientId,\r\n\t\t\t\tclient_secret: clientSecret,\r\n\t\t\t}),\r\n\t\t});\r\n\r\n\t\tif (!response.ok) {\r\n\t\t\tthrow new Error('Failed to refresh token');\r\n\t\t}\r\n\r\n\t\tconst data = await response.json();\r\n\t\treturn {\r\n\t\t\taccessToken: data.access_token,\r\n\t\t\trefreshToken: data.refresh_token,\r\n\t\t\texpiresIn: data.expires_in,\r\n\t\t\ttokenType: data.token_type,\r\n\t\t};\r\n\t}\r\n\r\n\tasync logout(accessToken: string): Promise<void> {\r\n\t\tawait fetch(`${this.baseUrl}/oauth/revoke`, {\r\n\t\t\tmethod: 'POST',\r\n\t\t\theaders: {\r\n\t\t\t\tAuthorization: `Bearer ${accessToken}`,\r\n\t\t\t},\r\n\t\t});\r\n\t}\r\n}","import { CustosConfig, User, AuthState, AuthEvent, AuthEventType } from './types';\r\nimport { Storage } from './storage';\r\nimport { ApiClient } from './api';\r\nimport { generateState, parseQueryString, isTokenExpired } from './utils';\r\n\r\nexport class Custos {\r\n\tprivate config: Required<CustosConfig>;\r\n\tprivate storage: Storage;\r\n\tprivate api: ApiClient;\r\n\tprivate listeners: Map<AuthEventType, Set<(event: AuthEvent) => void>>;\r\n\tprivate tokenIssuedAt: number | null = null;\r\n\r\n\tconstructor(config: CustosConfig) {\r\n\t\tthis.config = {\r\n\t\t\tclientId: config.clientId,\r\n\t\t\tclientSecret: config.clientSecret || '',\r\n\t\t\tredirectUri: config.redirectUri,\r\n\t\t\tapiUrl: config.apiUrl || 'https://custos.alimzen.com',\r\n\t\t\tscope: config.scope || ['openid', 'profile', 'email'],\r\n\t\t};\r\n\r\n\t\tthis.storage = new Storage();\r\n\t\tthis.api = new ApiClient(this.config.apiUrl);\r\n\t\tthis.listeners = new Map();\r\n\r\n\t\t// Handle callback automatically\r\n\t\tthis.handleCallback();\r\n\r\n\t\t// Setup token refresh\r\n\t\tthis.setupTokenRefresh();\r\n\t}\r\n\r\n\t// Authentication Methods\r\n\tasync login(): Promise<void> {\r\n\t\tconst state = generateState();\r\n\t\tthis.storage.setState('oauth_state', state);\r\n\r\n\t\tconst params = new URLSearchParams({\r\n\t\t\tresponse_type: 'code',\r\n\t\t\tclient_id: this.config.clientId,\r\n\t\t\tredirect_uri: this.config.redirectUri,\r\n\t\t\tscope: this.config.scope.join(' '),\r\n\t\t\tstate,\r\n\t\t});\r\n\r\n\t\twindow.location.href = `${this.config.apiUrl}/oauth/authorize?${params}`;\r\n\t}\r\n\r\n\tasync logout(): Promise<void> {\r\n\t\tconst tokens = this.storage.getTokens();\r\n\r\n\t\tif (tokens?.accessToken) {\r\n\t\t\ttry {\r\n\t\t\t\tawait this.api.logout(tokens.accessToken);\r\n\t\t\t} catch (error) {\r\n\t\t\t\tconsole.error('Logout error:', error);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tthis.storage.clear();\r\n\t\tthis.emit('logout', null);\r\n\t}\r\n\r\n\tasync handleCallback(): Promise<void> {\r\n\t\tconst params = parseQueryString(window.location.href);\r\n\t\tconst code = params.code;\r\n\t\tconst state = params.state;\r\n\r\n\t\tif (!code) return;\r\n\r\n\t\tconst savedState = this.storage.getState('oauth_state');\r\n\t\tif (state !== savedState) {\r\n\t\t\tthrow new Error('Invalid state parameter');\r\n\t\t}\r\n\r\n\t\tthis.storage.removeState('oauth_state');\r\n\r\n\t\ttry {\r\n\t\t\tconst tokens = await this.api.exchangeCodeForTokens(\r\n\t\t\t\tcode,\r\n\t\t\t\tthis.config.clientId,\r\n\t\t\t\tthis.config.redirectUri,\r\n\t\t\t\tthis.config.clientSecret\r\n\t\t\t);\r\n\r\n\t\t\tthis.tokenIssuedAt = Date.now();\r\n\t\t\tthis.storage.setTokens(tokens);\r\n\r\n\t\t\tconst user = await this.api.getUserInfo(tokens.accessToken);\r\n\t\t\tthis.storage.setUser(user);\r\n\r\n\t\t\tthis.emit('login', { user, tokens });\r\n\r\n\t\t\t// Clean URL\r\n\t\t\twindow.history.replaceState({}, document.title, window.location.pathname);\r\n\t\t} catch (error) {\r\n\t\t\tthis.emit('error', error);\r\n\t\t\tthrow error;\r\n\t\t}\r\n\t}\r\n\r\n\t// User Methods\r\n\tgetUser(): User | null {\r\n\t\treturn this.storage.getUser();\r\n\t}\r\n\r\n\tgetAccessToken(): string | null {\r\n\t\treturn this.storage.getTokens()?.accessToken || null;\r\n\t}\r\n\r\n\tisAuthenticated(): boolean {\r\n\t\treturn !!this.storage.getTokens() && !!this.storage.getUser();\r\n\t}\r\n\r\n\tgetState(): AuthState {\r\n\t\treturn {\r\n\t\t\tisAuthenticated: this.isAuthenticated(),\r\n\t\t\tuser: this.getUser(),\r\n\t\t\ttokens: this.storage.getTokens(),\r\n\t\t};\r\n\t}\r\n\r\n\t// Token Refresh\r\n\tprivate setupTokenRefresh(): void {\r\n\t\tsetInterval(async () => {\r\n\t\t\tif (this.shouldRefreshToken()) {\r\n\t\t\t\tawait this.refreshToken();\r\n\t\t\t}\r\n\t\t}, 60000); // Check every minute\r\n\t}\r\n\r\n\tprivate shouldRefreshToken(): boolean {\r\n\t\tconst tokens = this.storage.getTokens();\r\n\t\tif (!tokens || !this.tokenIssuedAt) return false;\r\n\r\n\t\treturn isTokenExpired(tokens.expiresIn, this.tokenIssuedAt);\r\n\t}\r\n\r\n\tasync refreshToken(): Promise<void> {\r\n\t\tconst tokens = this.storage.getTokens();\r\n\t\tif (!tokens?.refreshToken) {\r\n\t\t\tthrow new Error('No refresh token available');\r\n\t\t}\r\n\r\n\t\ttry {\r\n\t\t\tconst newTokens = await this.api.refreshAccessToken(\r\n\t\t\t\ttokens.refreshToken,\r\n\t\t\t\tthis.config.clientId,\r\n\t\t\t\tthis.config.clientSecret\r\n\t\t\t);\r\n\r\n\t\t\tthis.tokenIssuedAt = Date.now();\r\n\t\t\tthis.storage.setTokens(newTokens);\r\n\r\n\t\t\tthis.emit('token-refresh', newTokens);\r\n\t\t} catch (error) {\r\n\t\t\tthis.emit('error', error);\r\n\t\t\t// If refresh fails, logout\r\n\t\t\tawait this.logout();\r\n\t\t\tthrow error;\r\n\t\t}\r\n\t}\r\n\r\n\t// Event Handling\r\n\ton(event: AuthEventType, callback: (event: AuthEvent) => void): void {\r\n\t\tif (!this.listeners.has(event)) {\r\n\t\t\tthis.listeners.set(event, new Set());\r\n\t\t}\r\n\t\tthis.listeners.get(event)!.add(callback);\r\n\t}\r\n\r\n\toff(event: AuthEventType, callback: (event: AuthEvent) => void): void {\r\n\t\tthis.listeners.get(event)?.delete(callback);\r\n\t}\r\n\r\n\tprivate emit(type: AuthEventType, data?: any): void {\r\n\t\tconst event: AuthEvent = { type, data };\r\n\t\tthis.listeners.get(type)?.forEach((callback) => callback(event));\r\n\t}\r\n}","export function generateState(): string {\r\n\tconst array = new Uint8Array(32);\r\n\tcrypto.getRandomValues(array);\r\n\treturn Array.from(array, (byte) => byte.toString(16).padStart(2, '0')).join('');\r\n}\r\n\r\nexport function parseQueryString(url: string): Record<string, string> {\r\n\tconst params: Record<string, string> = {};\r\n\tconst searchParams = new URL(url).searchParams;\r\n\tsearchParams.forEach((value, key) => {\r\n\t\tparams[key] = value;\r\n\t});\r\n\treturn params;\r\n}\r\n\r\nexport function isTokenExpired(expiresIn: number, issuedAt: number): boolean {\r\n\tconst now = Date.now();\r\n\tconst expirationTime = issuedAt + expiresIn * 1000;\r\n\t// Refresh 5 minutes before expiration\r\n\treturn now >= expirationTime - 5 * 60 * 1000;\r\n}"],"names":["STORAGE_PREFIX","Storage","constructor","useSessionStorage","this","storage","sessionStorage","localStorage","setTokens","tokens","setItem","JSON","stringify","getTokens","data","getItem","parse","setUser","user","getUser","clear","removeItem","setState","key","value","getState","removeState","ApiClient","baseUrl","exchangeCodeForTokens","code","clientId","redirectUri","clientSecret","response","fetch","method","headers","body","grant_type","client_id","client_secret","redirect_uri","ok","Error","json","accessToken","access_token","refreshToken","refresh_token","expiresIn","expires_in","tokenType","token_type","getUserInfo","Authorization","refreshAccessToken","logout","Custos","config","tokenIssuedAt","apiUrl","scope","api","listeners","Map","handleCallback","setupTokenRefresh","login","state","array","Uint8Array","crypto","getRandomValues","Array","from","byte","toString","padStart","join","generateState","params","URLSearchParams","response_type","window","location","href","error","console","emit","url","URL","searchParams","forEach","parseQueryString","Date","now","history","replaceState","document","title","pathname","getAccessToken","isAuthenticated","setInterval","async","shouldRefreshToken","issuedAt","newTokens","on","event","callback","has","set","Set","get","add","off","delete","type"],"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/auth/userinfo`, {\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(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};\n\n\t\tthis.storage = new Storage();\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\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\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","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","console","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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"}
|
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 r{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,r){this.storage.setItem(`${t}${e}`,r)}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 s{constructor(e){this.baseUrl=e}async exchangeCodeForTokens(e,t,r,s,o){const i={grant_type:"authorization_code",code:e,client_id:t,redirect_uri:r};s&&(i.code_verifier=s),o&&(i.client_secret=o);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/auth/userinfo`,{headers:{Authorization:`Bearer ${e}`}});if(!t.ok)throw new Error("Failed to get user info");const r=await t.json();return r.data||r}async refreshAccessToken(e,t,r){const s={grant_type:"refresh_token",refresh_token:e,client_id:t};r&&(s.client_secret=r);const o=await fetch(`${this.baseUrl}/api/v1/auth/token`,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams(s).toString()});if(!o.ok)throw new Error("Failed to refresh token");const i=await o.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 o(){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),r=Array.from(t,e=>String.fromCharCode(e)).join("");return btoa(r).replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}(await crypto.subtle.digest("SHA-256",t))}e.Custos=class{constructor(e){this.tokenExpiryTimer=null;const t=function(e){return e?Array.isArray(e)?e:"string"==typeof e?e.split(" "):["openid","profile","email"]:["openid","profile","email"]}(e.scope);this.config={clientId:e.clientId,clientSecret:e.clientSecret||"",redirectUri:e.redirectUri,apiUrl:e.apiUrl||"https://custos.alimzen.com",scope:t,responseType:e.responseType||"code",state:e.state||o(),usePKCE:!1!==e.usePKCE,codeChallengeMethod:e.codeChallengeMethod||"S256",grantType:e.grantType||"authorization_code"},this.storage=new r,this.api=new s(this.config.apiUrl),this.listeners=new Map,"undefined"!=typeof window&&(this.handleCallback(),this.setupTokenExpiryMonitoring())}async login(e){const t=this.config.state;this.storage.setState("oauth_state",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 r="";for(let s=0;s<t;s++)r+=e.charAt(Math.floor(66*Math.random()));return r}(),t=await i(e);this.storage.setCodeVerifier(e),this.storage.setCodeChallenge(t),r.code_challenge=t,r.code_challenge_method=this.config.codeChallengeMethod}const s=`${this.config.apiUrl}/v1/auth/authorize?${new URLSearchParams(r)}`;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,r)=>{t[r]=e}),t}(window.location.href),t=e.error;if(t){const r=e.error_description||t;throw this.emit("error",{error:t,error_description:r}),new Error(r)}const r=e.code;if(!r)return;if(e.state!==this.storage.getState("oauth_state"))throw this.emit("error",{error:"invalid_state",error_description:"State parameter mismatch"}),new Error("Invalid state parameter");this.storage.removeState("oauth_state");try{const e=this.config.usePKCE&&this.storage.getCodeVerifier()||void 0,t=await this.api.exchangeCodeForTokens(r,this.config.clientId,this.config.redirectUri,e,this.config.clientSecret);this.storage.setTokens(t);const s=await this.api.getUserInfo(t.accessToken);this.storage.setUser(s),this.config.usePKCE&&(this.storage.removeCodeVerifier(),this.storage.removeCodeChallenge()),this.setupTokenExpiryMonitoring(),this.emit("login",{user:s,tokens:t}),window.history.replaceState({},document.title,window.location.pathname+window.location.hash)}catch(t){throw 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 r=1e3*(e.expiresIn-300);r>0&&(this.tokenExpiryTimer=setTimeout(async()=>{try{await this.refreshToken()}catch(e){this.emit("token-expired",e),await this.logout()}},r))}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 r={type:e,data:t};this.listeners.get(e)?.forEach(e=>e(r))}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/Custos.ts","../src/utils.ts"],"sourcesContent":["import { AuthTokens, User } from './types';\r\n\r\nconst STORAGE_PREFIX = 'custos_';\r\n\r\nexport class Storage {\r\n\tprivate storage: globalThis.Storage;\r\n\r\n\tconstructor(useSessionStorage = false) {\r\n\t\tthis.storage = useSessionStorage ? sessionStorage : localStorage;\r\n\t}\r\n\r\n\tsetTokens(tokens: AuthTokens): void {\r\n\t\tthis.storage.setItem(`${STORAGE_PREFIX}tokens`, JSON.stringify(tokens));\r\n\t}\r\n\r\n\tgetTokens(): AuthTokens | null {\r\n\t\tconst data = this.storage.getItem(`${STORAGE_PREFIX}tokens`);\r\n\t\treturn data ? JSON.parse(data) : null;\r\n\t}\r\n\r\n\tsetUser(user: User): void {\r\n\t\tthis.storage.setItem(`${STORAGE_PREFIX}user`, JSON.stringify(user));\r\n\t}\r\n\r\n\tgetUser(): User | null {\r\n\t\tconst data = this.storage.getItem(`${STORAGE_PREFIX}user`);\r\n\t\treturn data ? JSON.parse(data) : null;\r\n\t}\r\n\r\n\tclear(): void {\r\n\t\tthis.storage.removeItem(`${STORAGE_PREFIX}tokens`);\r\n\t\tthis.storage.removeItem(`${STORAGE_PREFIX}user`);\r\n\t}\r\n\r\n\tsetState(key: string, value: string): void {\r\n\t\tthis.storage.setItem(`${STORAGE_PREFIX}${key}`, value);\r\n\t}\r\n\r\n\tgetState(key: string): string | null {\r\n\t\treturn this.storage.getItem(`${STORAGE_PREFIX}${key}`);\r\n\t}\r\n\r\n\tremoveState(key: string): void {\r\n\t\tthis.storage.removeItem(`${STORAGE_PREFIX}${key}`);\r\n\t}\r\n}","import { AuthTokens, User } from './types';\r\n\r\nexport class ApiClient {\r\n\tprivate baseUrl: string;\r\n\r\n\tconstructor(baseUrl: string) {\r\n\t\tthis.baseUrl = baseUrl;\r\n\t}\r\n\r\n\tasync exchangeCodeForTokens(\r\n\t\tcode: string,\r\n\t\tclientId: string,\r\n\t\tredirectUri: string,\r\n\t\tclientSecret?: string\r\n\t): Promise<AuthTokens> {\r\n\t\tconst response = await fetch(`${this.baseUrl}/oauth/token`, {\r\n\t\t\tmethod: 'POST',\r\n\t\t\theaders: {\r\n\t\t\t\t'Content-Type': 'application/json',\r\n\t\t\t},\r\n\t\t\tbody: JSON.stringify({\r\n\t\t\t\tgrant_type: 'authorization_code',\r\n\t\t\t\tcode,\r\n\t\t\t\tclient_id: clientId,\r\n\t\t\t\tclient_secret: clientSecret,\r\n\t\t\t\tredirect_uri: redirectUri,\r\n\t\t\t}),\r\n\t\t});\r\n\r\n\t\tif (!response.ok) {\r\n\t\t\tthrow new Error('Failed to exchange code for tokens');\r\n\t\t}\r\n\r\n\t\tconst data = await response.json();\r\n\t\treturn {\r\n\t\t\taccessToken: data.access_token,\r\n\t\t\trefreshToken: data.refresh_token,\r\n\t\t\texpiresIn: data.expires_in,\r\n\t\t\ttokenType: data.token_type,\r\n\t\t};\r\n\t}\r\n\r\n\tasync getUserInfo(accessToken: string): Promise<User> {\r\n\t\tconst response = await fetch(`${this.baseUrl}/oauth/userinfo`, {\r\n\t\t\theaders: {\r\n\t\t\t\tAuthorization: `Bearer ${accessToken}`,\r\n\t\t\t},\r\n\t\t});\r\n\r\n\t\tif (!response.ok) {\r\n\t\t\tthrow new Error('Failed to get user info');\r\n\t\t}\r\n\r\n\t\treturn response.json();\r\n\t}\r\n\r\n\tasync refreshAccessToken(\r\n\t\trefreshToken: string,\r\n\t\tclientId: string,\r\n\t\tclientSecret?: string\r\n\t): Promise<AuthTokens> {\r\n\t\tconst response = await fetch(`${this.baseUrl}/oauth/token`, {\r\n\t\t\tmethod: 'POST',\r\n\t\t\theaders: {\r\n\t\t\t\t'Content-Type': 'application/json',\r\n\t\t\t},\r\n\t\t\tbody: JSON.stringify({\r\n\t\t\t\tgrant_type: 'refresh_token',\r\n\t\t\t\trefresh_token: refreshToken,\r\n\t\t\t\tclient_id: clientId,\r\n\t\t\t\tclient_secret: clientSecret,\r\n\t\t\t}),\r\n\t\t});\r\n\r\n\t\tif (!response.ok) {\r\n\t\t\tthrow new Error('Failed to refresh token');\r\n\t\t}\r\n\r\n\t\tconst data = await response.json();\r\n\t\treturn {\r\n\t\t\taccessToken: data.access_token,\r\n\t\t\trefreshToken: data.refresh_token,\r\n\t\t\texpiresIn: data.expires_in,\r\n\t\t\ttokenType: data.token_type,\r\n\t\t};\r\n\t}\r\n\r\n\tasync logout(accessToken: string): Promise<void> {\r\n\t\tawait fetch(`${this.baseUrl}/oauth/revoke`, {\r\n\t\t\tmethod: 'POST',\r\n\t\t\theaders: {\r\n\t\t\t\tAuthorization: `Bearer ${accessToken}`,\r\n\t\t\t},\r\n\t\t});\r\n\t}\r\n}","import { CustosConfig, User, AuthState, AuthEvent, AuthEventType } from './types';\r\nimport { Storage } from './storage';\r\nimport { ApiClient } from './api';\r\nimport { generateState, parseQueryString, isTokenExpired } from './utils';\r\n\r\nexport class Custos {\r\n\tprivate config: Required<CustosConfig>;\r\n\tprivate storage: Storage;\r\n\tprivate api: ApiClient;\r\n\tprivate listeners: Map<AuthEventType, Set<(event: AuthEvent) => void>>;\r\n\tprivate tokenIssuedAt: number | null = null;\r\n\r\n\tconstructor(config: CustosConfig) {\r\n\t\tthis.config = {\r\n\t\t\tclientId: config.clientId,\r\n\t\t\tclientSecret: config.clientSecret || '',\r\n\t\t\tredirectUri: config.redirectUri,\r\n\t\t\tapiUrl: config.apiUrl || 'https://custos.alimzen.com',\r\n\t\t\tscope: config.scope || ['openid', 'profile', 'email'],\r\n\t\t};\r\n\r\n\t\tthis.storage = new Storage();\r\n\t\tthis.api = new ApiClient(this.config.apiUrl);\r\n\t\tthis.listeners = new Map();\r\n\r\n\t\t// Handle callback automatically\r\n\t\tthis.handleCallback();\r\n\r\n\t\t// Setup token refresh\r\n\t\tthis.setupTokenRefresh();\r\n\t}\r\n\r\n\t// Authentication Methods\r\n\tasync login(): Promise<void> {\r\n\t\tconst state = generateState();\r\n\t\tthis.storage.setState('oauth_state', state);\r\n\r\n\t\tconst params = new URLSearchParams({\r\n\t\t\tresponse_type: 'code',\r\n\t\t\tclient_id: this.config.clientId,\r\n\t\t\tredirect_uri: this.config.redirectUri,\r\n\t\t\tscope: this.config.scope.join(' '),\r\n\t\t\tstate,\r\n\t\t});\r\n\r\n\t\twindow.location.href = `${this.config.apiUrl}/oauth/authorize?${params}`;\r\n\t}\r\n\r\n\tasync logout(): Promise<void> {\r\n\t\tconst tokens = this.storage.getTokens();\r\n\r\n\t\tif (tokens?.accessToken) {\r\n\t\t\ttry {\r\n\t\t\t\tawait this.api.logout(tokens.accessToken);\r\n\t\t\t} catch (error) {\r\n\t\t\t\tconsole.error('Logout error:', error);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tthis.storage.clear();\r\n\t\tthis.emit('logout', null);\r\n\t}\r\n\r\n\tasync handleCallback(): Promise<void> {\r\n\t\tconst params = parseQueryString(window.location.href);\r\n\t\tconst code = params.code;\r\n\t\tconst state = params.state;\r\n\r\n\t\tif (!code) return;\r\n\r\n\t\tconst savedState = this.storage.getState('oauth_state');\r\n\t\tif (state !== savedState) {\r\n\t\t\tthrow new Error('Invalid state parameter');\r\n\t\t}\r\n\r\n\t\tthis.storage.removeState('oauth_state');\r\n\r\n\t\ttry {\r\n\t\t\tconst tokens = await this.api.exchangeCodeForTokens(\r\n\t\t\t\tcode,\r\n\t\t\t\tthis.config.clientId,\r\n\t\t\t\tthis.config.redirectUri,\r\n\t\t\t\tthis.config.clientSecret\r\n\t\t\t);\r\n\r\n\t\t\tthis.tokenIssuedAt = Date.now();\r\n\t\t\tthis.storage.setTokens(tokens);\r\n\r\n\t\t\tconst user = await this.api.getUserInfo(tokens.accessToken);\r\n\t\t\tthis.storage.setUser(user);\r\n\r\n\t\t\tthis.emit('login', { user, tokens });\r\n\r\n\t\t\t// Clean URL\r\n\t\t\twindow.history.replaceState({}, document.title, window.location.pathname);\r\n\t\t} catch (error) {\r\n\t\t\tthis.emit('error', error);\r\n\t\t\tthrow error;\r\n\t\t}\r\n\t}\r\n\r\n\t// User Methods\r\n\tgetUser(): User | null {\r\n\t\treturn this.storage.getUser();\r\n\t}\r\n\r\n\tgetAccessToken(): string | null {\r\n\t\treturn this.storage.getTokens()?.accessToken || null;\r\n\t}\r\n\r\n\tisAuthenticated(): boolean {\r\n\t\treturn !!this.storage.getTokens() && !!this.storage.getUser();\r\n\t}\r\n\r\n\tgetState(): AuthState {\r\n\t\treturn {\r\n\t\t\tisAuthenticated: this.isAuthenticated(),\r\n\t\t\tuser: this.getUser(),\r\n\t\t\ttokens: this.storage.getTokens(),\r\n\t\t};\r\n\t}\r\n\r\n\t// Token Refresh\r\n\tprivate setupTokenRefresh(): void {\r\n\t\tsetInterval(async () => {\r\n\t\t\tif (this.shouldRefreshToken()) {\r\n\t\t\t\tawait this.refreshToken();\r\n\t\t\t}\r\n\t\t}, 60000); // Check every minute\r\n\t}\r\n\r\n\tprivate shouldRefreshToken(): boolean {\r\n\t\tconst tokens = this.storage.getTokens();\r\n\t\tif (!tokens || !this.tokenIssuedAt) return false;\r\n\r\n\t\treturn isTokenExpired(tokens.expiresIn, this.tokenIssuedAt);\r\n\t}\r\n\r\n\tasync refreshToken(): Promise<void> {\r\n\t\tconst tokens = this.storage.getTokens();\r\n\t\tif (!tokens?.refreshToken) {\r\n\t\t\tthrow new Error('No refresh token available');\r\n\t\t}\r\n\r\n\t\ttry {\r\n\t\t\tconst newTokens = await this.api.refreshAccessToken(\r\n\t\t\t\ttokens.refreshToken,\r\n\t\t\t\tthis.config.clientId,\r\n\t\t\t\tthis.config.clientSecret\r\n\t\t\t);\r\n\r\n\t\t\tthis.tokenIssuedAt = Date.now();\r\n\t\t\tthis.storage.setTokens(newTokens);\r\n\r\n\t\t\tthis.emit('token-refresh', newTokens);\r\n\t\t} catch (error) {\r\n\t\t\tthis.emit('error', error);\r\n\t\t\t// If refresh fails, logout\r\n\t\t\tawait this.logout();\r\n\t\t\tthrow error;\r\n\t\t}\r\n\t}\r\n\r\n\t// Event Handling\r\n\ton(event: AuthEventType, callback: (event: AuthEvent) => void): void {\r\n\t\tif (!this.listeners.has(event)) {\r\n\t\t\tthis.listeners.set(event, new Set());\r\n\t\t}\r\n\t\tthis.listeners.get(event)!.add(callback);\r\n\t}\r\n\r\n\toff(event: AuthEventType, callback: (event: AuthEvent) => void): void {\r\n\t\tthis.listeners.get(event)?.delete(callback);\r\n\t}\r\n\r\n\tprivate emit(type: AuthEventType, data?: any): void {\r\n\t\tconst event: AuthEvent = { type, data };\r\n\t\tthis.listeners.get(type)?.forEach((callback) => callback(event));\r\n\t}\r\n}","export function generateState(): string {\r\n\tconst array = new Uint8Array(32);\r\n\tcrypto.getRandomValues(array);\r\n\treturn Array.from(array, (byte) => byte.toString(16).padStart(2, '0')).join('');\r\n}\r\n\r\nexport function parseQueryString(url: string): Record<string, string> {\r\n\tconst params: Record<string, string> = {};\r\n\tconst searchParams = new URL(url).searchParams;\r\n\tsearchParams.forEach((value, key) => {\r\n\t\tparams[key] = value;\r\n\t});\r\n\treturn params;\r\n}\r\n\r\nexport function isTokenExpired(expiresIn: number, issuedAt: number): boolean {\r\n\tconst now = Date.now();\r\n\tconst expirationTime = issuedAt + expiresIn * 1000;\r\n\t// Refresh 5 minutes before expiration\r\n\treturn now >= expirationTime - 5 * 60 * 1000;\r\n}"],"names":["STORAGE_PREFIX","Storage","constructor","useSessionStorage","this","storage","sessionStorage","localStorage","setTokens","tokens","setItem","JSON","stringify","getTokens","data","getItem","parse","setUser","user","getUser","clear","removeItem","setState","key","value","getState","removeState","ApiClient","baseUrl","exchangeCodeForTokens","code","clientId","redirectUri","clientSecret","response","fetch","method","headers","body","grant_type","client_id","client_secret","redirect_uri","ok","Error","json","accessToken","access_token","refreshToken","refresh_token","expiresIn","expires_in","tokenType","token_type","getUserInfo","Authorization","refreshAccessToken","logout","config","tokenIssuedAt","apiUrl","scope","api","listeners","Map","handleCallback","setupTokenRefresh","login","state","array","Uint8Array","crypto","getRandomValues","Array","from","byte","toString","padStart","join","generateState","params","URLSearchParams","response_type","window","location","href","error","console","emit","url","URL","searchParams","forEach","parseQueryString","Date","now","history","replaceState","document","title","pathname","getAccessToken","isAuthenticated","setInterval","async","shouldRefreshToken","issuedAt","newTokens","on","event","callback","has","set","Set","get","add","off","delete","type"],"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/auth/userinfo`, {\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(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};\n\n\t\tthis.storage = new Storage();\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\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\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","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","console","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":"6OAEA,MAAMA,EAAiB,gBAEVC,EAGZ,WAAAC,CAAYC,GAAoB,GAC/BC,KAAKC,QAAUF,EAAoBG,eAAiBC,YACpD,CAGD,SAAAC,CAAUC,GACTL,KAAKC,QAAQK,QAAQ,GAAGV,UAAwBW,KAAKC,UAAUH,IAC/DL,KAAKC,QAAQK,QAAQ,GAAGV,mBAAiCa,KAAKC,MAAMC,WACpE,CAED,SAAAC,GACC,MAAMC,EAAOb,KAAKC,QAAQa,QAAQ,GAAGlB,WACrC,OAAOiB,EAAON,KAAKQ,MAAMF,GAAQ,IACjC,CAED,gBAAAG,GACC,MAAMH,EAAOb,KAAKC,QAAQa,QAAQ,GAAGlB,oBACrC,OAAOiB,EAAOI,SAASJ,EAAM,IAAM,IACnC,CAGD,OAAAK,CAAQC,GACPnB,KAAKC,QAAQK,QAAQ,GAAGV,QAAsBW,KAAKC,UAAUW,GAC7D,CAED,OAAAC,GACC,MAAMP,EAAOb,KAAKC,QAAQa,QAAQ,GAAGlB,SACrC,OAAOiB,EAAON,KAAKQ,MAAMF,GAAQ,IACjC,CAGD,QAAAQ,CAASC,EAAaC,GACrBvB,KAAKC,QAAQK,QAAQ,GAAGV,IAAiB0B,IAAOC,EAChD,CAED,QAAAC,CAASF,GACR,OAAOtB,KAAKC,QAAQa,QAAQ,GAAGlB,IAAiB0B,IAChD,CAED,WAAAG,CAAYH,GACXtB,KAAKC,QAAQyB,WAAW,GAAG9B,IAAiB0B,IAC5C,CAGD,eAAAK,CAAgBC,GACf5B,KAAKqB,SAAS,gBAAiBO,EAC/B,CAED,eAAAC,GACC,OAAO7B,KAAKwB,SAAS,gBACrB,CAED,kBAAAM,GACC9B,KAAKyB,YAAY,gBACjB,CAED,gBAAAM,CAAiBC,GAChBhC,KAAKqB,SAAS,iBAAkBW,EAChC,CAED,gBAAAC,GACC,OAAOjC,KAAKwB,SAAS,iBACrB,CAED,mBAAAU,GACClC,KAAKyB,YAAY,iBACjB,CAGD,KAAAU,GACCnC,KAAKC,QAAQyB,WAAW,GAAG9B,WAC3BI,KAAKC,QAAQyB,WAAW,GAAG9B,oBAC3BI,KAAKC,QAAQyB,WAAW,GAAG9B,SAC3BI,KAAKyB,YAAY,eACjBzB,KAAK8B,qBACL9B,KAAKkC,qBACL,CAGD,aAAAE,GACC,MAAM/B,EAASL,KAAKY,YACdyB,EAAWrC,KAAKgB,mBAEtB,IAAKX,IAAWgC,EAAU,OAAO,EAKjC,OAHY5B,KAAKC,MACM2B,EAA8B,IAAnBhC,EAAOiC,SAGzC,QC9FWC,EAGZ,WAAAzC,CAAY0C,GACXxC,KAAKwC,QAAUA,CACf,CAED,2BAAMC,CACLC,EACAC,EACAC,EACAhB,EACAiB,GAEA,MAAMC,EAA+B,CACpCC,WAAY,qBACZL,OACAM,UAAWL,EACXM,aAAcL,GAIXhB,IACHkB,EAAKI,cAAgBtB,GAIlBiB,IACHC,EAAKK,cAAgBN,GAGtB,MAAMO,QAAiBC,MAAM,GAAGrD,KAAKwC,4BAA6B,CACjEc,OAAQ,OACRC,QAAS,CACR,eAAgB,qCAEjBT,KAAM,IAAIU,gBAAgBV,GAAMnC,aAGjC,IAAKyC,EAASK,GAAI,CACjB,MAAMC,QAAkBN,EAASO,OAAOC,MAAM,KAAO,CACpDC,MAAO,gBACPC,kBAAmB,wCAGpB,MAAM,IAAIC,MAAML,EAAUI,mBAAqBJ,EAAUG,OAAS,wBAClE,CAED,MAAMG,QAAeZ,EAASO,OACxB9C,EAAOmD,EAAOnD,MAAQmD,EAE5B,MAAO,CACNC,YAAapD,EAAKqD,aAClBC,aAActD,EAAKuD,cACnB9B,UAAWzB,EAAKwD,WAChBC,UAAWzD,EAAK0D,YAAc,SAE/B,CAED,iBAAMC,CAAYP,GACjB,MAAMb,QAAiBC,MAAM,GAAGrD,KAAKwC,+BAAgC,CACpEe,QAAS,CACRkB,cAAe,UAAUR,OAI3B,IAAKb,EAASK,GACb,MAAM,IAAIM,MAAM,2BAGjB,MAAMC,QAAeZ,EAASO,OAC9B,OAAOK,EAAOnD,MAAQmD,CACtB,CAED,wBAAMU,CACLP,EACAxB,EACAE,GAEA,MAAMC,EAA+B,CACpCC,WAAY,gBACZqB,cAAeD,EACfnB,UAAWL,GAGRE,IACHC,EAAKK,cAAgBN,GAGtB,MAAMO,QAAiBC,MAAM,GAAGrD,KAAKwC,4BAA6B,CACjEc,OAAQ,OACRC,QAAS,CACR,eAAgB,qCAEjBT,KAAM,IAAIU,gBAAgBV,GAAMnC,aAGjC,IAAKyC,EAASK,GACb,MAAM,IAAIM,MAAM,2BAGjB,MAAMC,QAAeZ,EAASO,OACxB9C,EAAOmD,EAAOnD,MAAQmD,EAE5B,MAAO,CACNC,YAAapD,EAAKqD,aAClBC,aAActD,EAAKuD,eAAiBD,EACpC7B,UAAWzB,EAAKwD,WAChBC,UAAWzD,EAAK0D,YAAc,SAE/B,CAED,YAAMI,CAAOV,SACNZ,MAAM,GAAGrD,KAAKwC,6BAA8B,CACjDc,OAAQ,OACRC,QAAS,CACRkB,cAAe,UAAUR,MAG3B,CAED,mBAAMW,CAAcX,GACnB,IAMC,aALuBZ,MAAM,GAAGrD,KAAKwC,+BAAgC,CACpEe,QAAS,CACRkB,cAAe,UAAUR,QAGXR,EAChB,CAAC,MACD,OAAO,CACP,CACD,WCtIcoB,IACf,MAAMC,EAAQ,IAAIC,WAAW,IAE7B,OADAC,OAAOC,gBAAgBH,GAChBI,MAAMC,KAAKL,EAAQM,GAASA,EAAKzE,SAAS,IAAI0E,SAAS,EAAG,MAAMC,KAAK,GAC7E,CA6BOC,eAAeC,EAAsB5D,GAC3C,MACMf,GADU,IAAI4E,aACCC,OAAO9D,GAE5B,OAGD,SAAyB+D,GACxB,MAAMC,EAAQ,IAAIb,WAAWY,GACvBE,EAASX,MAAMC,KAAKS,EAAQR,GAASU,OAAOC,aAAaX,IAAOE,KAAK,IAC3E,OAAOU,KAAKH,GACVI,QAAQ,MAAO,KACfA,QAAQ,MAAO,KACfA,QAAQ,KAAM,GACjB,CAVQC,OADclB,OAAOmB,OAAOR,OAAO,UAAW9E,GAEtD,gBCpBC,WAAAf,CAAYsG,GAFJpG,KAAgBqG,iBAAQ,KAI/B,MAAMC,ED6BF,SAAyBA,GAC9B,OAAKA,EACDpB,MAAMqB,QAAQD,GAAeA,EACZ,iBAAVA,EAA2BA,EAAME,MAAM,KAC3C,CAAC,SAAU,UAAW,SAHV,CAAC,SAAU,UAAW,QAI1C,CClCgBC,CAAeL,EAAOE,OAEpCtG,KAAKoG,OAAS,CACbzD,SAAUyD,EAAOzD,SACjBE,aAAcuD,EAAOvD,cAAgB,GACrCD,YAAawD,EAAOxD,YACpB8D,OAAQN,EAAOM,QAAU,6BACzBJ,QACAK,aAAcP,EAAOO,cAAgB,OACrCC,MAAOR,EAAOQ,OAAS/B,IACvBgC,SAA4B,IAAnBT,EAAOS,QAChBC,oBAAqBV,EAAOU,qBAAuB,OACnDC,UAAWX,EAAOW,WAAa,sBAGhC/G,KAAKC,QAAU,IAAIJ,EACnBG,KAAKgH,IAAM,IAAIzE,EAAUvC,KAAKoG,OAAOM,QACrC1G,KAAKiH,UAAY,IAAIC,IAGC,oBAAXC,SACVnH,KAAKoH,iBACLpH,KAAKqH,6BAEN,CAID,WAAMC,CAAMC,GACX,MAAMX,EAAQ5G,KAAKoG,OAAOQ,MAC1B5G,KAAKC,QAAQoB,SAAS,cAAeuF,GAErC,MAAMY,EAAiC,CACtCC,cAAezH,KAAKoG,OAAOO,aAC3B3D,UAAWhD,KAAKoG,OAAOzD,SACvBM,aAAcjD,KAAKoG,OAAOxD,YAC1B0D,MAAOpB,MAAMqB,QAAQvG,KAAKoG,OAAOE,OAAStG,KAAKoG,OAAOE,MAAMhB,KAAK,KAAOtF,KAAKoG,OAAOE,MACpFM,WACGW,GAIJ,GAAIvH,KAAKoG,OAAOS,QAAS,CACxB,MAAMjF,aDvCR,MAAM8F,EAAU,qEACVC,EAASC,KAAKC,MAAsB,GAAhBD,KAAKE,UAAiB,GAChD,IAAIC,EAAW,GACf,IAAK,IAAIC,EAAI,EAAGA,EAAIL,EAAQK,IAC3BD,GAAYL,EAAQO,OAAOL,KAAKC,MAAsBH,GAAhBE,KAAKE,WAE5C,OAAOC,CACR,CCgCwBG,GACflG,QAAsBwD,EAAsB5D,GAElD5B,KAAKC,QAAQ0B,gBAAgBC,GAC7B5B,KAAKC,QAAQ8B,iBAAiBC,GAE9BwF,EAAOW,eAAiBnG,EACxBwF,EAAOY,sBAAwBpI,KAAKoG,OAAOU,mBAC3C,CAED,MAAMuB,EAAU,GAAGrI,KAAKoG,OAAOM,4BAA4B,IAAIlD,gBAAgBgE,KAC/EL,OAAOmB,SAASC,KAAOF,CACvB,CAED,YAAM1D,GACL,MAAMtE,EAASL,KAAKC,QAAQW,YAE5B,GAAIP,GAAQ4D,YACX,UACOjE,KAAKgH,IAAIrC,OAAOtE,EAAO4D,YAC7B,CAAC,MAAOJ,GACR2E,QAAQ3E,MAAM,gBAAiBA,EAC/B,CAGF7D,KAAKyI,wBACLzI,KAAKC,QAAQkC,QACbnC,KAAK0I,KAAK,SAAU,KACpB,CAED,oBAAMtB,GACL,MAAMI,EDxFF,SAA2BmB,GAChC,MAAMnB,EAAiC,CAAA,EAKvC,OAJqB,IAAIoB,IAAID,GAAKE,aACrBC,QAAQ,CAACvH,EAAOD,KAC5BkG,EAAOlG,GAAOC,IAERiG,CACR,CCiFiBuB,CAAiB5B,OAAOmB,SAASC,MAG1C1E,EAAQ2D,EAAO3D,MACrB,GAAIA,EAAO,CACV,MAAMmF,EAAmBxB,EAAO1D,mBAAqBD,EAErD,MADA7D,KAAK0I,KAAK,QAAS,CAAE7E,QAAOC,kBAAmBkF,IACzC,IAAIjF,MAAMiF,EAChB,CAGD,MAAMtG,EAAO8E,EAAO9E,KACpB,IAAKA,EAAM,OAKX,GAFc8E,EAAOZ,QACF5G,KAAKC,QAAQuB,SAAS,eAGxC,MADAxB,KAAK0I,KAAK,QAAS,CAAE7E,MAAO,gBAAiBC,kBAAmB,6BAC1D,IAAIC,MAAM,2BAGjB/D,KAAKC,QAAQwB,YAAY,eAEzB,IAEC,MAAMG,EAAe5B,KAAKoG,OAAOS,SAAU7G,KAAKC,QAAQ4B,wBAAiCoH,EAGnF5I,QAAeL,KAAKgH,IAAIvE,sBAC7BC,EACA1C,KAAKoG,OAAOzD,SACZ3C,KAAKoG,OAAOxD,YACZhB,EACA5B,KAAKoG,OAAOvD,cAGb7C,KAAKC,QAAQG,UAAUC,GAGvB,MAAMc,QAAanB,KAAKgH,IAAIxC,YAAYnE,EAAO4D,aAC/CjE,KAAKC,QAAQiB,QAAQC,GAGjBnB,KAAKoG,OAAOS,UACf7G,KAAKC,QAAQ6B,qBACb9B,KAAKC,QAAQiC,uBAIdlC,KAAKqH,6BAELrH,KAAK0I,KAAK,QAAS,CAAEvH,OAAMd,WAG3B8G,OAAO+B,QAAQC,aAAa,CAAE,EAAEC,SAASC,MAAOlC,OAAOmB,SAASgB,SAAWnC,OAAOmB,SAASiB,KAC3F,CAAC,MAAO1F,GAER,MADA7D,KAAK0I,KAAK,QAAS7E,GACbA,CACN,CACD,CAID,OAAAzC,GACC,OAAOpB,KAAKC,QAAQmB,SACpB,CAED,cAAAoI,GACC,OAAOxJ,KAAKC,QAAQW,aAAaqD,aAAe,IAChD,CAED,eAAAwF,GACC,OAAOzJ,KAAKC,QAAQW,aAAauD,cAAgB,IACjD,CAED,eAAAuF,GACC,OAAO1J,KAAKC,QAAQmC,mBAAqBpC,KAAKC,QAAQmB,SACtD,CAED,QAAAI,GACC,MAAO,CACNkI,gBAAiB1J,KAAK0J,kBACtBvI,KAAMnB,KAAKoB,UACXf,OAAQL,KAAKC,QAAQW,YAEtB,CAED,mBAAMgE,GACL,MAAMX,EAAcjE,KAAKwJ,iBACzB,IAAKvF,EAAa,OAAO,EAEzB,IACC,aAAajE,KAAKgH,IAAIpC,cAAcX,EACpC,CAAC,MACD,OAAO,CACP,CACD,CAIO,0BAAAoD,GACPrH,KAAKyI,wBAEL,MAAMpI,EAASL,KAAKC,QAAQW,YACtByB,EAAWrC,KAAKC,QAAQe,mBAE9B,IAAKX,IAAWgC,EAAU,OAG1B,MAAMsH,EAA8C,KAA1BtJ,EAAOiC,UAAY,KAEzCqH,EAAmB,IACtB3J,KAAKqG,iBAAmBuD,WAAWrE,UAClC,UACOvF,KAAKmE,cACX,CAAC,MAAON,GACR7D,KAAK0I,KAAK,gBAAiB7E,SACrB7D,KAAK2E,QACX,GACCgF,GAEJ,CAEO,qBAAAlB,GACHzI,KAAKqG,mBACRwD,aAAa7J,KAAKqG,kBAClBrG,KAAKqG,iBAAmB,KAEzB,CAED,kBAAMlC,GACL,MAAM9D,EAASL,KAAKC,QAAQW,YAC5B,IAAKP,GAAQ8D,aACZ,MAAM,IAAIJ,MAAM,8BAGjB,IACC,MAAM+F,QAAkB9J,KAAKgH,IAAItC,mBAChCrE,EAAO8D,aACPnE,KAAKoG,OAAOzD,SACZ3C,KAAKoG,OAAOvD,cAGb7C,KAAKC,QAAQG,UAAU0J,GACvB9J,KAAKqH,6BACLrH,KAAK0I,KAAK,gBAAiBoB,EAC3B,CAAC,MAAOjG,GAER,MADA7D,KAAK0I,KAAK,QAAS7E,GACbA,CACN,CACD,CAID,EAAAkG,CAAGC,EAAsBC,GACnBjK,KAAKiH,UAAUiD,IAAIF,IACvBhK,KAAKiH,UAAUkD,IAAIH,EAAO,IAAII,KAE/BpK,KAAKiH,UAAUoD,IAAIL,GAAQM,IAAIL,EAC/B,CAED,GAAAM,CAAIP,EAAsBC,GACzBjK,KAAKiH,UAAUoD,IAAIL,IAAQQ,OAAOP,EAClC,CAEO,IAAAvB,CAAK+B,EAAqB5J,GACjC,MAAMmJ,EAAmB,CAAES,OAAM5J,QACjCb,KAAKiH,UAAUoD,IAAII,IAAO3B,QAASmB,GAAaA,EAASD,GACzD,CAID,YAAAU,GACC1K,KAAKC,QAAQkC,OACb,CAED,OAAAwI,GACC3K,KAAKyI,wBACLzI,KAAKiH,UAAU9E,OACf"}
|
package/dist/storage.d.ts
CHANGED
|
@@ -4,11 +4,19 @@ export declare class Storage {
|
|
|
4
4
|
constructor(useSessionStorage?: boolean);
|
|
5
5
|
setTokens(tokens: AuthTokens): void;
|
|
6
6
|
getTokens(): AuthTokens | null;
|
|
7
|
+
getTokenIssuedAt(): number | null;
|
|
7
8
|
setUser(user: User): void;
|
|
8
9
|
getUser(): User | null;
|
|
9
|
-
clear(): void;
|
|
10
10
|
setState(key: string, value: string): void;
|
|
11
11
|
getState(key: string): string | null;
|
|
12
12
|
removeState(key: string): void;
|
|
13
|
+
setCodeVerifier(codeVerifier: string): void;
|
|
14
|
+
getCodeVerifier(): string | null;
|
|
15
|
+
removeCodeVerifier(): void;
|
|
16
|
+
setCodeChallenge(codeChallenge: string): void;
|
|
17
|
+
getCodeChallenge(): string | null;
|
|
18
|
+
removeCodeChallenge(): void;
|
|
19
|
+
clear(): void;
|
|
20
|
+
hasValidToken(): boolean;
|
|
13
21
|
}
|
|
14
22
|
//# sourceMappingURL=storage.d.ts.map
|
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"}
|
package/dist/types.d.ts
CHANGED
|
@@ -3,7 +3,12 @@ export interface CustosConfig {
|
|
|
3
3
|
clientSecret?: string;
|
|
4
4
|
redirectUri: string;
|
|
5
5
|
apiUrl?: string;
|
|
6
|
-
scope?: string[];
|
|
6
|
+
scope?: string | string[];
|
|
7
|
+
responseType?: 'code' | 'token';
|
|
8
|
+
state?: string;
|
|
9
|
+
usePKCE?: boolean;
|
|
10
|
+
codeChallengeMethod?: 'S256' | 'plain';
|
|
11
|
+
grantType?: string;
|
|
7
12
|
}
|
|
8
13
|
export interface User {
|
|
9
14
|
id: string;
|
|
@@ -24,9 +29,13 @@ export interface AuthState {
|
|
|
24
29
|
user: User | null;
|
|
25
30
|
tokens: AuthTokens | null;
|
|
26
31
|
}
|
|
27
|
-
export type AuthEventType = 'login' | 'logout' | 'token-refresh' | 'error';
|
|
32
|
+
export type AuthEventType = 'login' | 'logout' | 'token-refresh' | 'token-expired' | 'error';
|
|
28
33
|
export interface AuthEvent {
|
|
29
34
|
type: AuthEventType;
|
|
30
35
|
data?: any;
|
|
31
36
|
}
|
|
37
|
+
export interface PKCETokens {
|
|
38
|
+
code_verifier: string;
|
|
39
|
+
code_challenge: string;
|
|
40
|
+
}
|
|
32
41
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,YAAY;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,YAAY;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC1B,YAAY,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,mBAAmB,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACvC,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,IAAI;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC/B;AAED,MAAM,WAAW,UAAU;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,SAAS;IACzB,eAAe,EAAE,OAAO,CAAC;IACzB,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;IAClB,MAAM,EAAE,UAAU,GAAG,IAAI,CAAC;CAC1B;AAED,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,QAAQ,GAAG,eAAe,GAAG,eAAe,GAAG,OAAO,CAAC;AAE7F,MAAM,WAAW,SAAS;IACzB,IAAI,EAAE,aAAa,CAAC;IACpB,IAAI,CAAC,EAAE,GAAG,CAAC;CACX;AAED,MAAM,WAAW,UAAU;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;CACvB"}
|
package/dist/utils.d.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
export declare function generateState(): string;
|
|
2
2
|
export declare function parseQueryString(url: string): Record<string, string>;
|
|
3
3
|
export declare function isTokenExpired(expiresIn: number, issuedAt: number): boolean;
|
|
4
|
+
export declare function generateCodeVerifier(): string;
|
|
5
|
+
export declare function generateCodeChallenge(codeVerifier: string): Promise<string>;
|
|
6
|
+
export declare function normalizeScope(scope?: string | string[]): string[];
|
|
4
7
|
//# sourceMappingURL=utils.d.ts.map
|
package/dist/utils.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,wBAAgB,aAAa,IAAI,MAAM,CAItC;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAOpE;AAED,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAK3E"}
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,wBAAgB,aAAa,IAAI,MAAM,CAItC;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAOpE;AAED,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAK3E;AAGD,wBAAgB,oBAAoB,IAAI,MAAM,CAQ7C;AAED,wBAAsB,qBAAqB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAKjF;AAWD,wBAAgB,cAAc,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,MAAM,EAAE,CAKlE"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@isaias_pv/custos-sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Official JavaScript SDK for Custos authentication",
|
|
5
5
|
"main": "dist/index.cjs.js",
|
|
6
6
|
"module": "dist/index.esm.js",
|
|
@@ -60,4 +60,4 @@
|
|
|
60
60
|
"dependencies": {
|
|
61
61
|
"tslib": "^2.8.1"
|
|
62
62
|
}
|
|
63
|
-
}
|
|
63
|
+
}
|