@alien_org/sso-sdk-core 1.0.18 → 1.0.20

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/index.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";var d=(a,o,s)=>new Promise((n,r)=>{var t=i=>{try{h(s.next(i))}catch(g){r(g)}},c=i=>{try{h(s.throw(i))}catch(g){r(g)}},h=i=>i.done?n(i.value):Promise.resolve(i.value).then(t,c);h((s=s.apply(a,o)).next())});Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("zod/v4-mini"),j=require("js-sha256"),S=e.z.object({code_challenge:e.z.string(),code_challenge_method:e.z.literal("S256")}),f=e.z.object({deep_link:e.z.string(),polling_code:e.z.string(),expired_at:e.z.number()}),_=e.z.object({polling_code:e.z.string()}),v=["pending","authorized","rejected","expired"],T=e.z.enum(v),m=e.z.object({status:T,authorization_code:e.z.optional(e.z.string())}),z=e.z.object({authorization_code:e.z.string(),code_verifier:e.z.string()}),y=e.z.object({access_token:e.z.string()}),R=e.z.object({access_token:e.z.string()}),k=e.z.object({is_valid:e.z.boolean(),access_token:e.z.optional(e.z.string())}),b=e.z.object({app_callback_session_address:e.z.string(),expired_at:e.z.number(),issued_at:e.z.number()});function w(a){return btoa(a).replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}function u(a){let o=a.replace(/-/g,"+").replace(/_/g,"/");for(;o.length%4;)o+="=";return atob(o)}const C="https://sso.alien.com",E=5e3,l="alien-sso_",p=(a,o)=>new URL(o,a).toString(),A=e.z.object({ssoBaseUrl:e.z.url(),providerAddress:e.z.string(),pollingInterval:e.z.optional(e.z.number())});class P{constructor(o){this.config=A.parse(o),this.ssoBaseUrl=this.config.ssoBaseUrl||C,this.providerAddress=this.config.providerAddress,this.pollingInterval=this.config.pollingInterval||E}generateCodeVerifier(o=128){let s;const n=typeof window!="undefined"&&window.crypto;if(n&&n.getRandomValues)s=new Uint8Array(o),n.getRandomValues(s);else{s=new Uint8Array(o);for(let t=0;t<o;t++)s[t]=Math.floor(Math.random()*256)}let r="";for(let t=0;t<s.length;t++)r+=String.fromCharCode(s[t]);return w(r)}generateCodeChallenge(o){return j.sha256(o)}generateDeeplink(){return d(this,null,function*(){const o=this.generateCodeVerifier(),s=this.generateCodeChallenge(o);sessionStorage.setItem(l+"code_verifier",o);const n=`${this.config.ssoBaseUrl}/sso/authorize`,r={code_challenge:s,code_challenge_method:"S256"};S.parse(r);const c=yield(yield fetch(n,{method:"POST",headers:{"Content-Type":"application/json","X-PROVIDER-ADDRESS":this.providerAddress},body:JSON.stringify(r)})).json();return f.parse(c)})}pollAuth(o){return d(this,null,function*(){const s={polling_code:o};_.parse(s);const n=yield fetch(p(this.config.ssoBaseUrl,"/sso/poll"),{method:"POST",headers:{"Content-Type":"application/json","X-PROVIDER-ADDRESS":this.providerAddress},body:JSON.stringify(s)});if(!n.ok)throw new Error(`Poll failed: ${n.statusText}`);const r=yield n.json();return m.parse(r)})}exchangeToken(o){return d(this,null,function*(){const s=sessionStorage.getItem(l+"code_verifier");if(!s)throw new Error("Missing code verifier.");const n={authorization_code:o,code_verifier:s};z.parse(n);const r=yield fetch(p(this.config.ssoBaseUrl,"/sso/access_token/exchange"),{method:"POST",headers:{"Content-Type":"application/json","X-PROVIDER-ADDRESS":this.providerAddress},body:JSON.stringify(n)});if(!r.ok)throw new Error(`ExchangeCode failed: ${r.statusText}`);const t=yield r.json(),c=y.parse(t);if(c.access_token)return localStorage.setItem(l+"access_token",c.access_token),c.access_token;throw new Error("Exchange failed")})}verifyAuth(){return d(this,null,function*(){const o=this.getAccessToken();if(!o)return!1;const s={access_token:o};R.parse(s);const n=yield fetch(p(this.config.ssoBaseUrl,"/sso/access_token/verify"),{method:"POST",headers:{"Content-Type":"application/json","X-PROVIDER-ADDRESS":this.providerAddress},body:JSON.stringify(s)});if(!n.ok)return!1;const r=yield n.json(),t=k.parse(r);return t.access_token&&localStorage.setItem(l+"access_token",t.access_token),t.is_valid})}getAccessToken(){return localStorage.getItem(l+"access_token")}getAuthData(){const o=this.getAccessToken();if(!o)return null;const s=o.split(".");if(s.length!==3)return null;let n;try{const t=u(s[0]);n=JSON.parse(t)}catch(t){return null}if(n.alg!=="HS256"||n.typ!=="JWT")return null;let r;try{const t=JSON.parse(u(s[1]));r=b.parse(t)}catch(t){return null}return r}logout(){localStorage.removeItem(l+"access_token"),sessionStorage.removeItem(l+"code_verifier")}}exports.AlienSsoClient=P;exports.AlienSsoClientSchema=A;exports.AuthorizeRequestSchema=S;exports.AuthorizeResponseSchema=f;exports.ExchangeCodeRequestSchema=z;exports.ExchangeCodeResponseSchema=y;exports.PollRequestSchema=_;exports.PollResponseSchema=m;exports.TokenInfoSchema=b;exports.VerifyTokenRequestSchema=R;exports.VerifyTokenResponseSchema=k;
1
+ "use strict";var h=(d,e,o)=>new Promise((r,s)=>{var a=l=>{try{c(o.next(l))}catch(S){s(S)}},n=l=>{try{c(o.throw(l))}catch(S){s(S)}},c=l=>l.done?r(l.value):Promise.resolve(l.value).then(a,n);c((o=o.apply(d,e)).next())});Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("zod/v4-mini"),I=require("js-sha256"),y=t.z.object({deep_link:t.z.string(),polling_code:t.z.string(),expired_at:t.z.number()}),T=t.z.object({polling_code:t.z.string()}),j=["pending","authorized","rejected","expired"],x=t.z.enum(j),z=t.z.object({status:x,authorization_code:t.z.optional(t.z.string())}),m=t.z.object({access_token:t.z.string(),token_type:t.z.string(),expires_in:t.z.number(),id_token:t.z.string(),refresh_token:t.z.string()}),R=t.z.object({sub:t.z.string()}),A=t.z.object({iss:t.z.string(),sub:t.z.string(),aud:t.z.union([t.z.string(),t.z.array(t.z.string())]),exp:t.z.number(),iat:t.z.number(),nonce:t.z.optional(t.z.string()),auth_time:t.z.optional(t.z.number())}),E=m;function w(d){return btoa(d).replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}function _(d){let e=d.replace(/-/g,"+").replace(/_/g,"/");for(;e.length%4;)e+="=";return atob(e)}const v="https://sso.alien.com",P=5e3,i="alien-sso_",g=i+"refresh_token",p=i+"token_expiry",f=(d,e)=>new URL(e,d).toString(),b=t.z.object({ssoBaseUrl:t.z.url(),providerAddress:t.z.string(),pollingInterval:t.z.optional(t.z.number())}),u=class u{constructor(e){this.config=b.parse(e),this.ssoBaseUrl=this.config.ssoBaseUrl||v,this.providerAddress=this.config.providerAddress,this.pollingInterval=this.config.pollingInterval||P}generateCodeVerifier(e=128){let o;const r=typeof window!="undefined"&&window.crypto;if(r&&r.getRandomValues)o=new Uint8Array(e),r.getRandomValues(o);else{o=new Uint8Array(e);for(let a=0;a<e;a++)o[a]=Math.floor(Math.random()*256)}let s="";for(let a=0;a<o.length;a++)s+=String.fromCharCode(o[a]);return w(s)}generateCodeChallenge(e){const o=I.sha256.array(e),r=String.fromCharCode(...o);return w(r)}generateDeeplink(){return h(this,null,function*(){const e=this.generateCodeVerifier(),o=this.generateCodeChallenge(e);sessionStorage.setItem(i+"code_verifier",e);const r=new URLSearchParams({response_type:"code",response_mode:"json",client_id:this.providerAddress,scope:"openid",code_challenge:o,code_challenge_method:"S256"}),s=`${this.config.ssoBaseUrl}/oauth/authorize?${r.toString()}`,a=yield fetch(s,{method:"GET"});if(!a.ok){const c=yield a.json().catch(()=>({error:a.statusText}));throw new Error(`Authorize failed: ${c.error_description||c.error||a.statusText}`)}const n=yield a.json();return y.parse(n)})}pollAuth(e){return h(this,null,function*(){const o={polling_code:e};T.parse(o);const r=yield fetch(f(this.config.ssoBaseUrl,"/oauth/poll"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(o)});if(!r.ok)throw new Error(`Poll failed: ${r.statusText}`);const s=yield r.json();return z.parse(s)})}exchangeToken(e){return h(this,null,function*(){const o=sessionStorage.getItem(i+"code_verifier");if(!o)throw new Error("Missing code verifier.");const r=new URLSearchParams({grant_type:"authorization_code",code:e,client_id:this.providerAddress,code_verifier:o}),s=yield fetch(f(this.config.ssoBaseUrl,"/oauth/token"),{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:r.toString()});if(!s.ok){const l=yield s.json().catch(()=>({error:s.statusText}));throw new Error(`Token exchange failed: ${l.error_description||l.error||s.statusText}`)}const a=yield s.json(),n=m.parse(a);localStorage.setItem(i+"access_token",n.access_token),localStorage.setItem(i+"id_token",n.id_token),localStorage.setItem(g,n.refresh_token);const c=Date.now()+n.expires_in*1e3;return localStorage.setItem(p,c.toString()),sessionStorage.removeItem(i+"code_verifier"),n})}verifyAuth(){return h(this,null,function*(){return this.withAutoRefresh(()=>h(this,null,function*(){const e=this.getAccessToken();if(!e)return null;const o=yield fetch(f(this.config.ssoBaseUrl,"/oauth/userinfo"),{method:"GET",headers:{Authorization:`Bearer ${e}`}});if(!o.ok){if(o.status===401){const s=new Error("Unauthorized");throw s.response={status:401},s}return null}const r=yield o.json();return R.parse(r)}))})}getAccessToken(){return localStorage.getItem(i+"access_token")}getIdToken(){return localStorage.getItem(i+"id_token")}getAuthData(){const e=this.getIdToken()||this.getAccessToken();if(!e)return null;const o=e.split(".");if(o.length!==3)return null;let r;try{const n=_(o[0]);r=JSON.parse(n)}catch(n){return null}if(r.alg!=="RS256"||r.typ!=="JWT")return null;let s;try{const n=JSON.parse(_(o[1]));s=A.parse(n)}catch(n){return null}return(Array.isArray(s.aud)?s.aud:[s.aud]).includes(this.providerAddress)?s:null}getSubject(){const e=this.getAuthData();return(e==null?void 0:e.sub)||null}isTokenExpired(){const e=this.getAuthData();return e?Date.now()/1e3>e.exp:!0}logout(){localStorage.removeItem(i+"access_token"),localStorage.removeItem(i+"id_token"),localStorage.removeItem(g),localStorage.removeItem(p),sessionStorage.removeItem(i+"code_verifier")}getRefreshToken(){return localStorage.getItem(g)}hasRefreshToken(){return!!this.getRefreshToken()}isAccessTokenExpired(){const e=localStorage.getItem(p);if(!e)return!0;const o=parseInt(e,10),r=Date.now(),s=300*1e3;return r>=o-s}refreshAccessToken(){return h(this,null,function*(){return u.refreshPromise||(u.refreshPromise=this.doRefreshAccessToken().finally(()=>{u.refreshPromise=null})),u.refreshPromise})}doRefreshAccessToken(){return h(this,null,function*(){const e=this.getRefreshToken();if(!e)throw new Error("No refresh token available");const o=new URLSearchParams({grant_type:"refresh_token",refresh_token:e,client_id:this.providerAddress}),r=yield fetch(f(this.config.ssoBaseUrl,"/oauth/token"),{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:o.toString()});if(!r.ok){const c=yield r.json().catch(()=>({error:r.statusText}));throw this.logout(),new Error(`Token refresh failed: ${c.error_description||c.error||r.statusText}`)}const s=yield r.json(),a=m.parse(s);localStorage.setItem(i+"access_token",a.access_token),localStorage.setItem(i+"id_token",a.id_token),localStorage.setItem(g,a.refresh_token);const n=Date.now()+a.expires_in*1e3;return localStorage.setItem(p,n.toString()),a})}withAutoRefresh(e,o=1){return h(this,null,function*(){var r,s,a;try{return yield e()}catch(n){if((((r=n==null?void 0:n.response)==null?void 0:r.status)===401||((s=n==null?void 0:n.message)==null?void 0:s.includes("401"))||((a=n==null?void 0:n.message)==null?void 0:a.includes("Unauthorized")))&&o>0&&this.hasRefreshToken())try{return yield this.refreshAccessToken(),yield e()}catch(l){throw n}throw n}})}};u.refreshPromise=null;let k=u;exports.AlienSsoClient=k;exports.AlienSsoClientSchema=b;exports.AuthorizeResponseSchema=y;exports.ExchangeCodeResponseSchema=E;exports.PollRequestSchema=T;exports.PollResponseSchema=z;exports.TokenInfoSchema=A;exports.TokenResponseSchema=m;exports.UserInfoResponseSchema=R;
package/dist/index.d.ts CHANGED
@@ -5,16 +5,84 @@ export declare class AlienSsoClient {
5
5
  readonly pollingInterval: number;
6
6
  readonly ssoBaseUrl: string;
7
7
  readonly providerAddress: string;
8
+ private static refreshPromise;
8
9
  constructor(config: AlienSsoClientConfig);
9
10
  private generateCodeVerifier;
10
11
  private generateCodeChallenge;
12
+ /**
13
+ * Initiates OAuth2 authorization flow with response_mode=json for SPA
14
+ * GET /oauth/authorize?response_type=code&response_mode=json&...
15
+ */
11
16
  generateDeeplink(): Promise<AuthorizeResponse>;
17
+ /**
18
+ * Polls for authorization completion
19
+ * POST /oauth/poll
20
+ */
12
21
  pollAuth(pollingCode: string): Promise<PollResponse>;
13
- exchangeToken(authorizationCode: string): Promise<string>;
14
- verifyAuth(): Promise<boolean>;
22
+ /**
23
+ * Exchanges authorization code for tokens
24
+ * POST /oauth/token (application/x-www-form-urlencoded)
25
+ * Returns both access_token and id_token
26
+ */
27
+ exchangeToken(authorizationCode: string): Promise<TokenResponse>;
28
+ /**
29
+ * Verifies authentication by calling userinfo endpoint
30
+ * GET /oauth/userinfo
31
+ * Automatically refreshes token on 401 if refresh token is available
32
+ */
33
+ verifyAuth(): Promise<UserInfoResponse | null>;
34
+ /**
35
+ * Gets stored access token
36
+ */
15
37
  getAccessToken(): string | null;
38
+ /**
39
+ * Gets stored ID token
40
+ */
41
+ getIdToken(): string | null;
42
+ /**
43
+ * Decodes and validates JWT token to extract claims
44
+ * Works with both access_token and id_token (EdDSA signed)
45
+ */
16
46
  getAuthData(): TokenInfo | null;
47
+ /**
48
+ * Gets the subject (user identifier) from the token
49
+ */
50
+ getSubject(): string | null;
51
+ /**
52
+ * Checks if the current token is expired
53
+ */
54
+ isTokenExpired(): boolean;
55
+ /**
56
+ * Clears all stored authentication data
57
+ */
17
58
  logout(): void;
59
+ /**
60
+ * Gets stored refresh token
61
+ */
62
+ getRefreshToken(): string | null;
63
+ /**
64
+ * Checks if a refresh token is available
65
+ */
66
+ hasRefreshToken(): boolean;
67
+ /**
68
+ * Checks if the access token is expired or will expire soon (within 5 minutes)
69
+ */
70
+ isAccessTokenExpired(): boolean;
71
+ /**
72
+ * Refreshes the access token using the stored refresh token
73
+ * POST /oauth/token with grant_type=refresh_token
74
+ * Uses singleton pattern to prevent concurrent refresh requests (race condition)
75
+ */
76
+ refreshAccessToken(): Promise<TokenResponse>;
77
+ /**
78
+ * Internal method that performs the actual token refresh
79
+ */
80
+ private doRefreshAccessToken;
81
+ /**
82
+ * Executes a function that makes an authenticated request
83
+ * Automatically refreshes token and retries on 401 error
84
+ */
85
+ withAutoRefresh<T>(requestFn: () => Promise<T>, maxRetries?: number): Promise<T>;
18
86
  }
19
87
 
20
88
  export declare type AlienSsoClientConfig = z.infer<typeof AlienSsoClientSchema>;
@@ -25,49 +93,39 @@ export declare const AlienSsoClientSchema: z.ZodMiniObject<{
25
93
  pollingInterval: z.ZodMiniOptional<z.ZodMiniNumber<number>>;
26
94
  }, z.core.$strip>;
27
95
 
28
- export declare type AuthorizeRequest = z.infer<typeof AuthorizeRequestSchema>;
96
+ export declare type AuthorizeResponse = z.infer<typeof AuthorizeResponseSchema>;
29
97
 
30
98
  /**
31
- * Authorize request/response schema
99
+ * Authorize response schema (for response_mode=json)
100
+ * GET /oauth/authorize?response_mode=json&...
32
101
  */
33
- export declare const AuthorizeRequestSchema: z.ZodMiniObject<{
34
- code_challenge: z.ZodMiniString<string>;
35
- code_challenge_method: z.ZodMiniLiteral<"S256">;
36
- }, z.core.$strip>;
37
-
38
- export declare type AuthorizeResponse = z.infer<typeof AuthorizeResponseSchema>;
39
-
40
102
  export declare const AuthorizeResponseSchema: z.ZodMiniObject<{
41
103
  deep_link: z.ZodMiniString<string>;
42
104
  polling_code: z.ZodMiniString<string>;
43
105
  expired_at: z.ZodMiniNumber<number>;
44
106
  }, z.core.$strip>;
45
107
 
46
- export declare type ExchangeCodeRequest = z.infer<typeof ExchangeCodeRequestSchema>;
47
-
48
- /**
49
- * ExchangeCode request/response schema
50
- */
51
- export declare const ExchangeCodeRequestSchema: z.ZodMiniObject<{
52
- authorization_code: z.ZodMiniString<string>;
53
- code_verifier: z.ZodMiniString<string>;
54
- }, z.core.$strip>;
55
-
56
- export declare type ExchangeCodeResponse = z.infer<typeof ExchangeCodeResponseSchema>;
108
+ export declare type ExchangeCodeResponse = TokenResponse;
57
109
 
58
110
  export declare const ExchangeCodeResponseSchema: z.ZodMiniObject<{
59
111
  access_token: z.ZodMiniString<string>;
112
+ token_type: z.ZodMiniString<string>;
113
+ expires_in: z.ZodMiniNumber<number>;
114
+ id_token: z.ZodMiniString<string>;
115
+ refresh_token: z.ZodMiniString<string>;
60
116
  }, z.core.$strip>;
61
117
 
62
118
  export declare interface JWTHeader {
63
119
  alg: string;
64
120
  typ: string;
121
+ kid?: string;
65
122
  }
66
123
 
67
124
  export declare type PollRequest = z.infer<typeof PollRequestSchema>;
68
125
 
69
126
  /**
70
127
  * Poll request/response schema
128
+ * POST /oauth/poll
71
129
  */
72
130
  export declare const PollRequestSchema: z.ZodMiniObject<{
73
131
  polling_code: z.ZodMiniString<string>;
@@ -88,28 +146,41 @@ export declare const PollResponseSchema: z.ZodMiniObject<{
88
146
  export declare type TokenInfo = z.infer<typeof TokenInfoSchema>;
89
147
 
90
148
  /**
91
- * Token info schema
149
+ * Token info schema (parsed from JWT)
150
+ * Standard OIDC claims
92
151
  */
93
152
  export declare const TokenInfoSchema: z.ZodMiniObject<{
94
- app_callback_session_address: z.ZodMiniString<string>;
95
- expired_at: z.ZodMiniNumber<number>;
96
- issued_at: z.ZodMiniNumber<number>;
153
+ iss: z.ZodMiniString<string>;
154
+ sub: z.ZodMiniString<string>;
155
+ aud: z.ZodMiniUnion<readonly [z.ZodMiniString<string>, z.ZodMiniArray<z.ZodMiniString<string>>]>;
156
+ exp: z.ZodMiniNumber<number>;
157
+ iat: z.ZodMiniNumber<number>;
158
+ nonce: z.ZodMiniOptional<z.ZodMiniString<string>>;
159
+ auth_time: z.ZodMiniOptional<z.ZodMiniNumber<number>>;
97
160
  }, z.core.$strip>;
98
161
 
99
- export declare type VerifyTokenRequest = z.infer<typeof VerifyTokenRequestSchema>;
162
+ export declare type TokenResponse = z.infer<typeof TokenResponseSchema>;
100
163
 
101
164
  /**
102
- * VerifyToken request/response schema
165
+ * Token exchange response schema (OAuth2 standard)
166
+ * POST /oauth/token
103
167
  */
104
- export declare const VerifyTokenRequestSchema: z.ZodMiniObject<{
168
+ export declare const TokenResponseSchema: z.ZodMiniObject<{
105
169
  access_token: z.ZodMiniString<string>;
170
+ token_type: z.ZodMiniString<string>;
171
+ expires_in: z.ZodMiniNumber<number>;
172
+ id_token: z.ZodMiniString<string>;
173
+ refresh_token: z.ZodMiniString<string>;
106
174
  }, z.core.$strip>;
107
175
 
108
- export declare type VerifyTokenResponse = z.infer<typeof VerifyTokenResponseSchema>;
176
+ export declare type UserInfoResponse = z.infer<typeof UserInfoResponseSchema>;
109
177
 
110
- export declare const VerifyTokenResponseSchema: z.ZodMiniObject<{
111
- is_valid: z.ZodMiniBoolean<boolean>;
112
- access_token: z.ZodMiniOptional<z.ZodMiniString<string>>;
178
+ /**
179
+ * UserInfo response schema
180
+ * GET /oauth/userinfo
181
+ */
182
+ export declare const UserInfoResponseSchema: z.ZodMiniObject<{
183
+ sub: z.ZodMiniString<string>;
113
184
  }, z.core.$strip>;
114
185
 
115
186
  export { }
package/dist/index.esm.js CHANGED
@@ -1,225 +1,354 @@
1
- var d = (a, o, s) => new Promise((n, r) => {
2
- var t = (i) => {
1
+ var h = (u, e, r) => new Promise((o, s) => {
2
+ var a = (l) => {
3
3
  try {
4
- h(s.next(i));
5
- } catch (p) {
6
- r(p);
4
+ c(r.next(l));
5
+ } catch (m) {
6
+ s(m);
7
7
  }
8
- }, c = (i) => {
8
+ }, n = (l) => {
9
9
  try {
10
- h(s.throw(i));
11
- } catch (p) {
12
- r(p);
10
+ c(r.throw(l));
11
+ } catch (m) {
12
+ s(m);
13
13
  }
14
- }, h = (i) => i.done ? n(i.value) : Promise.resolve(i.value).then(t, c);
15
- h((s = s.apply(a, o)).next());
14
+ }, c = (l) => l.done ? o(l.value) : Promise.resolve(l.value).then(a, n);
15
+ c((r = r.apply(u, e)).next());
16
16
  });
17
- import { z as e } from "zod/v4-mini";
18
- import { sha256 as f } from "js-sha256";
19
- const _ = e.object({
20
- code_challenge: e.string(),
21
- code_challenge_method: e.literal("S256")
22
- }), S = e.object({
23
- deep_link: e.string(),
24
- polling_code: e.string(),
25
- expired_at: e.number()
26
- }), m = e.object({
27
- polling_code: e.string()
28
- }), y = ["pending", "authorized", "rejected", "expired"], k = e.enum(y), b = e.object({
29
- status: k,
30
- authorization_code: e.optional(e.string())
31
- }), R = e.object({
32
- authorization_code: e.string(),
33
- code_verifier: e.string()
34
- }), A = e.object({
35
- access_token: e.string()
36
- }), j = e.object({
37
- access_token: e.string()
38
- }), v = e.object({
39
- is_valid: e.boolean(),
40
- access_token: e.optional(e.string())
41
- }), w = e.object({
42
- app_callback_session_address: e.string(),
43
- expired_at: e.number(),
44
- issued_at: e.number()
45
- });
46
- function E(a) {
47
- return btoa(a).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
17
+ import { z as t } from "zod/v4-mini";
18
+ import { sha256 as y } from "js-sha256";
19
+ const T = t.object({
20
+ deep_link: t.string(),
21
+ polling_code: t.string(),
22
+ expired_at: t.number()
23
+ }), b = t.object({
24
+ polling_code: t.string()
25
+ }), A = ["pending", "authorized", "rejected", "expired"], I = t.enum(A), R = t.object({
26
+ status: I,
27
+ authorization_code: t.optional(t.string())
28
+ }), k = t.object({
29
+ access_token: t.string(),
30
+ token_type: t.string(),
31
+ expires_in: t.number(),
32
+ id_token: t.string(),
33
+ refresh_token: t.string()
34
+ }), x = t.object({
35
+ sub: t.string()
36
+ }), j = t.object({
37
+ iss: t.string(),
38
+ sub: t.string(),
39
+ aud: t.union([t.string(), t.array(t.string())]),
40
+ exp: t.number(),
41
+ iat: t.number(),
42
+ nonce: t.optional(t.string()),
43
+ auth_time: t.optional(t.number())
44
+ }), z = k;
45
+ function w(u) {
46
+ return btoa(u).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
48
47
  }
49
- function u(a) {
50
- let o = a.replace(/-/g, "+").replace(/_/g, "/");
51
- for (; o.length % 4; )
52
- o += "=";
53
- return atob(o);
48
+ function _(u) {
49
+ let e = u.replace(/-/g, "+").replace(/_/g, "/");
50
+ for (; e.length % 4; )
51
+ e += "=";
52
+ return atob(e);
54
53
  }
55
- const T = "https://sso.alien.com", C = 5e3, l = "alien-sso_", g = (a, o) => new URL(o, a).toString(), O = e.object({
56
- ssoBaseUrl: e.url(),
57
- providerAddress: e.string(),
58
- pollingInterval: e.optional(e.number())
59
- });
60
- class x {
61
- constructor(o) {
62
- this.config = O.parse(o), this.ssoBaseUrl = this.config.ssoBaseUrl || T, this.providerAddress = this.config.providerAddress, this.pollingInterval = this.config.pollingInterval || C;
54
+ const E = "https://sso.alien.com", v = 5e3, i = "alien-sso_", g = i + "refresh_token", p = i + "token_expiry", f = (u, e) => new URL(e, u).toString(), U = t.object({
55
+ ssoBaseUrl: t.url(),
56
+ providerAddress: t.string(),
57
+ pollingInterval: t.optional(t.number())
58
+ }), d = class d {
59
+ constructor(e) {
60
+ this.config = U.parse(e), this.ssoBaseUrl = this.config.ssoBaseUrl || E, this.providerAddress = this.config.providerAddress, this.pollingInterval = this.config.pollingInterval || v;
63
61
  }
64
- generateCodeVerifier(o = 128) {
65
- let s;
66
- const n = typeof window != "undefined" && window.crypto;
67
- if (n && n.getRandomValues)
68
- s = new Uint8Array(o), n.getRandomValues(s);
62
+ generateCodeVerifier(e = 128) {
63
+ let r;
64
+ const o = typeof window != "undefined" && window.crypto;
65
+ if (o && o.getRandomValues)
66
+ r = new Uint8Array(e), o.getRandomValues(r);
69
67
  else {
70
- s = new Uint8Array(o);
71
- for (let t = 0; t < o; t++)
72
- s[t] = Math.floor(Math.random() * 256);
68
+ r = new Uint8Array(e);
69
+ for (let a = 0; a < e; a++)
70
+ r[a] = Math.floor(Math.random() * 256);
73
71
  }
74
- let r = "";
75
- for (let t = 0; t < s.length; t++)
76
- r += String.fromCharCode(s[t]);
77
- return E(r);
72
+ let s = "";
73
+ for (let a = 0; a < r.length; a++)
74
+ s += String.fromCharCode(r[a]);
75
+ return w(s);
78
76
  }
79
- generateCodeChallenge(o) {
80
- return f(o);
77
+ generateCodeChallenge(e) {
78
+ const r = y.array(e), o = String.fromCharCode(...r);
79
+ return w(o);
81
80
  }
81
+ /**
82
+ * Initiates OAuth2 authorization flow with response_mode=json for SPA
83
+ * GET /oauth/authorize?response_type=code&response_mode=json&...
84
+ */
82
85
  generateDeeplink() {
83
- return d(this, null, function* () {
84
- const o = this.generateCodeVerifier(), s = this.generateCodeChallenge(o);
85
- sessionStorage.setItem(l + "code_verifier", o);
86
- const n = `${this.config.ssoBaseUrl}/sso/authorize`, r = {
87
- code_challenge: s,
86
+ return h(this, null, function* () {
87
+ const e = this.generateCodeVerifier(), r = this.generateCodeChallenge(e);
88
+ sessionStorage.setItem(i + "code_verifier", e);
89
+ const o = new URLSearchParams({
90
+ response_type: "code",
91
+ response_mode: "json",
92
+ client_id: this.providerAddress,
93
+ scope: "openid",
94
+ code_challenge: r,
88
95
  code_challenge_method: "S256"
89
- };
90
- _.parse(r);
91
- const c = yield (yield fetch(n, {
92
- method: "POST",
93
- headers: {
94
- "Content-Type": "application/json",
95
- "X-PROVIDER-ADDRESS": this.providerAddress
96
- },
97
- body: JSON.stringify(r)
98
- })).json();
99
- return S.parse(c);
96
+ }), s = `${this.config.ssoBaseUrl}/oauth/authorize?${o.toString()}`, a = yield fetch(s, {
97
+ method: "GET"
98
+ });
99
+ if (!a.ok) {
100
+ const c = yield a.json().catch(() => ({ error: a.statusText }));
101
+ throw new Error(`Authorize failed: ${c.error_description || c.error || a.statusText}`);
102
+ }
103
+ const n = yield a.json();
104
+ return T.parse(n);
100
105
  });
101
106
  }
102
- pollAuth(o) {
103
- return d(this, null, function* () {
104
- const s = {
105
- polling_code: o
107
+ /**
108
+ * Polls for authorization completion
109
+ * POST /oauth/poll
110
+ */
111
+ pollAuth(e) {
112
+ return h(this, null, function* () {
113
+ const r = {
114
+ polling_code: e
106
115
  };
107
- m.parse(s);
108
- const n = yield fetch(g(this.config.ssoBaseUrl, "/sso/poll"), {
116
+ b.parse(r);
117
+ const o = yield fetch(f(this.config.ssoBaseUrl, "/oauth/poll"), {
109
118
  method: "POST",
110
119
  headers: {
111
- "Content-Type": "application/json",
112
- "X-PROVIDER-ADDRESS": this.providerAddress
120
+ "Content-Type": "application/json"
113
121
  },
114
- body: JSON.stringify(s)
122
+ body: JSON.stringify(r)
115
123
  });
116
- if (!n.ok)
117
- throw new Error(`Poll failed: ${n.statusText}`);
118
- const r = yield n.json();
119
- return b.parse(r);
124
+ if (!o.ok)
125
+ throw new Error(`Poll failed: ${o.statusText}`);
126
+ const s = yield o.json();
127
+ return R.parse(s);
120
128
  });
121
129
  }
122
- exchangeToken(o) {
123
- return d(this, null, function* () {
124
- const s = sessionStorage.getItem(l + "code_verifier");
125
- if (!s) throw new Error("Missing code verifier.");
126
- const n = {
127
- authorization_code: o,
128
- code_verifier: s
129
- };
130
- R.parse(n);
131
- const r = yield fetch(
132
- g(this.config.ssoBaseUrl, "/sso/access_token/exchange"),
130
+ /**
131
+ * Exchanges authorization code for tokens
132
+ * POST /oauth/token (application/x-www-form-urlencoded)
133
+ * Returns both access_token and id_token
134
+ */
135
+ exchangeToken(e) {
136
+ return h(this, null, function* () {
137
+ const r = sessionStorage.getItem(i + "code_verifier");
138
+ if (!r) throw new Error("Missing code verifier.");
139
+ const o = new URLSearchParams({
140
+ grant_type: "authorization_code",
141
+ code: e,
142
+ client_id: this.providerAddress,
143
+ code_verifier: r
144
+ }), s = yield fetch(
145
+ f(this.config.ssoBaseUrl, "/oauth/token"),
133
146
  {
134
147
  method: "POST",
135
148
  headers: {
136
- "Content-Type": "application/json",
137
- "X-PROVIDER-ADDRESS": this.providerAddress
149
+ "Content-Type": "application/x-www-form-urlencoded"
138
150
  },
139
- body: JSON.stringify(n)
151
+ body: o.toString()
140
152
  }
141
153
  );
142
- if (!r.ok)
143
- throw new Error(`ExchangeCode failed: ${r.statusText}`);
144
- const t = yield r.json(), c = A.parse(t);
145
- if (c.access_token)
146
- return localStorage.setItem(
147
- l + "access_token",
148
- c.access_token
149
- ), c.access_token;
150
- throw new Error("Exchange failed");
154
+ if (!s.ok) {
155
+ const l = yield s.json().catch(() => ({ error: s.statusText }));
156
+ throw new Error(`Token exchange failed: ${l.error_description || l.error || s.statusText}`);
157
+ }
158
+ const a = yield s.json(), n = k.parse(a);
159
+ localStorage.setItem(i + "access_token", n.access_token), localStorage.setItem(i + "id_token", n.id_token), localStorage.setItem(g, n.refresh_token);
160
+ const c = Date.now() + n.expires_in * 1e3;
161
+ return localStorage.setItem(p, c.toString()), sessionStorage.removeItem(i + "code_verifier"), n;
151
162
  });
152
163
  }
164
+ /**
165
+ * Verifies authentication by calling userinfo endpoint
166
+ * GET /oauth/userinfo
167
+ * Automatically refreshes token on 401 if refresh token is available
168
+ */
153
169
  verifyAuth() {
154
- return d(this, null, function* () {
155
- const o = this.getAccessToken();
156
- if (!o)
157
- return !1;
158
- const s = {
159
- access_token: o
160
- };
161
- j.parse(s);
162
- const n = yield fetch(
163
- g(this.config.ssoBaseUrl, "/sso/access_token/verify"),
164
- {
165
- method: "POST",
166
- headers: {
167
- "Content-Type": "application/json",
168
- "X-PROVIDER-ADDRESS": this.providerAddress
169
- },
170
- body: JSON.stringify(s)
170
+ return h(this, null, function* () {
171
+ return this.withAutoRefresh(() => h(this, null, function* () {
172
+ const e = this.getAccessToken();
173
+ if (!e)
174
+ return null;
175
+ const r = yield fetch(
176
+ f(this.config.ssoBaseUrl, "/oauth/userinfo"),
177
+ {
178
+ method: "GET",
179
+ headers: {
180
+ Authorization: `Bearer ${e}`
181
+ }
182
+ }
183
+ );
184
+ if (!r.ok) {
185
+ if (r.status === 401) {
186
+ const s = new Error("Unauthorized");
187
+ throw s.response = { status: 401 }, s;
188
+ }
189
+ return null;
171
190
  }
172
- );
173
- if (!n.ok)
174
- return !1;
175
- const r = yield n.json(), t = v.parse(r);
176
- return t.access_token && localStorage.setItem(
177
- l + "access_token",
178
- t.access_token
179
- ), t.is_valid;
191
+ const o = yield r.json();
192
+ return x.parse(o);
193
+ }));
180
194
  });
181
195
  }
196
+ /**
197
+ * Gets stored access token
198
+ */
182
199
  getAccessToken() {
183
- return localStorage.getItem(l + "access_token");
200
+ return localStorage.getItem(i + "access_token");
184
201
  }
202
+ /**
203
+ * Gets stored ID token
204
+ */
205
+ getIdToken() {
206
+ return localStorage.getItem(i + "id_token");
207
+ }
208
+ /**
209
+ * Decodes and validates JWT token to extract claims
210
+ * Works with both access_token and id_token (EdDSA signed)
211
+ */
185
212
  getAuthData() {
186
- const o = this.getAccessToken();
187
- if (!o) return null;
188
- const s = o.split(".");
189
- if (s.length !== 3)
213
+ const e = this.getIdToken() || this.getAccessToken();
214
+ if (!e) return null;
215
+ const r = e.split(".");
216
+ if (r.length !== 3)
190
217
  return null;
191
- let n;
218
+ let o;
192
219
  try {
193
- const t = u(s[0]);
194
- n = JSON.parse(t);
195
- } catch (t) {
220
+ const n = _(r[0]);
221
+ o = JSON.parse(n);
222
+ } catch (n) {
196
223
  return null;
197
224
  }
198
- if (n.alg !== "HS256" || n.typ !== "JWT")
225
+ if (o.alg !== "RS256" || o.typ !== "JWT")
199
226
  return null;
200
- let r;
227
+ let s;
201
228
  try {
202
- const t = JSON.parse(u(s[1]));
203
- r = w.parse(t);
204
- } catch (t) {
229
+ const n = JSON.parse(_(r[1]));
230
+ s = j.parse(n);
231
+ } catch (n) {
205
232
  return null;
206
233
  }
207
- return r;
234
+ return (Array.isArray(s.aud) ? s.aud : [s.aud]).includes(this.providerAddress) ? s : null;
235
+ }
236
+ /**
237
+ * Gets the subject (user identifier) from the token
238
+ */
239
+ getSubject() {
240
+ const e = this.getAuthData();
241
+ return (e == null ? void 0 : e.sub) || null;
242
+ }
243
+ /**
244
+ * Checks if the current token is expired
245
+ */
246
+ isTokenExpired() {
247
+ const e = this.getAuthData();
248
+ return e ? Date.now() / 1e3 > e.exp : !0;
208
249
  }
250
+ /**
251
+ * Clears all stored authentication data
252
+ */
209
253
  logout() {
210
- localStorage.removeItem(l + "access_token"), sessionStorage.removeItem(l + "code_verifier");
254
+ localStorage.removeItem(i + "access_token"), localStorage.removeItem(i + "id_token"), localStorage.removeItem(g), localStorage.removeItem(p), sessionStorage.removeItem(i + "code_verifier");
211
255
  }
212
- }
256
+ /**
257
+ * Gets stored refresh token
258
+ */
259
+ getRefreshToken() {
260
+ return localStorage.getItem(g);
261
+ }
262
+ /**
263
+ * Checks if a refresh token is available
264
+ */
265
+ hasRefreshToken() {
266
+ return !!this.getRefreshToken();
267
+ }
268
+ /**
269
+ * Checks if the access token is expired or will expire soon (within 5 minutes)
270
+ */
271
+ isAccessTokenExpired() {
272
+ const e = localStorage.getItem(p);
273
+ if (!e) return !0;
274
+ const r = parseInt(e, 10), o = Date.now(), s = 300 * 1e3;
275
+ return o >= r - s;
276
+ }
277
+ /**
278
+ * Refreshes the access token using the stored refresh token
279
+ * POST /oauth/token with grant_type=refresh_token
280
+ * Uses singleton pattern to prevent concurrent refresh requests (race condition)
281
+ */
282
+ refreshAccessToken() {
283
+ return h(this, null, function* () {
284
+ return d.refreshPromise || (d.refreshPromise = this.doRefreshAccessToken().finally(() => {
285
+ d.refreshPromise = null;
286
+ })), d.refreshPromise;
287
+ });
288
+ }
289
+ /**
290
+ * Internal method that performs the actual token refresh
291
+ */
292
+ doRefreshAccessToken() {
293
+ return h(this, null, function* () {
294
+ const e = this.getRefreshToken();
295
+ if (!e)
296
+ throw new Error("No refresh token available");
297
+ const r = new URLSearchParams({
298
+ grant_type: "refresh_token",
299
+ refresh_token: e,
300
+ client_id: this.providerAddress
301
+ }), o = yield fetch(
302
+ f(this.config.ssoBaseUrl, "/oauth/token"),
303
+ {
304
+ method: "POST",
305
+ headers: {
306
+ "Content-Type": "application/x-www-form-urlencoded"
307
+ },
308
+ body: r.toString()
309
+ }
310
+ );
311
+ if (!o.ok) {
312
+ const c = yield o.json().catch(() => ({ error: o.statusText }));
313
+ throw this.logout(), new Error(`Token refresh failed: ${c.error_description || c.error || o.statusText}`);
314
+ }
315
+ const s = yield o.json(), a = k.parse(s);
316
+ localStorage.setItem(i + "access_token", a.access_token), localStorage.setItem(i + "id_token", a.id_token), localStorage.setItem(g, a.refresh_token);
317
+ const n = Date.now() + a.expires_in * 1e3;
318
+ return localStorage.setItem(p, n.toString()), a;
319
+ });
320
+ }
321
+ /**
322
+ * Executes a function that makes an authenticated request
323
+ * Automatically refreshes token and retries on 401 error
324
+ */
325
+ withAutoRefresh(e, r = 1) {
326
+ return h(this, null, function* () {
327
+ var o, s, a;
328
+ try {
329
+ return yield e();
330
+ } catch (n) {
331
+ if ((((o = n == null ? void 0 : n.response) == null ? void 0 : o.status) === 401 || ((s = n == null ? void 0 : n.message) == null ? void 0 : s.includes("401")) || ((a = n == null ? void 0 : n.message) == null ? void 0 : a.includes("Unauthorized"))) && r > 0 && this.hasRefreshToken())
332
+ try {
333
+ return yield this.refreshAccessToken(), yield e();
334
+ } catch (l) {
335
+ throw n;
336
+ }
337
+ throw n;
338
+ }
339
+ });
340
+ }
341
+ };
342
+ d.refreshPromise = null;
343
+ let S = d;
213
344
  export {
214
- x as AlienSsoClient,
215
- O as AlienSsoClientSchema,
216
- _ as AuthorizeRequestSchema,
217
- S as AuthorizeResponseSchema,
218
- R as ExchangeCodeRequestSchema,
219
- A as ExchangeCodeResponseSchema,
220
- m as PollRequestSchema,
221
- b as PollResponseSchema,
222
- w as TokenInfoSchema,
223
- j as VerifyTokenRequestSchema,
224
- v as VerifyTokenResponseSchema
345
+ S as AlienSsoClient,
346
+ U as AlienSsoClientSchema,
347
+ T as AuthorizeResponseSchema,
348
+ z as ExchangeCodeResponseSchema,
349
+ b as PollRequestSchema,
350
+ R as PollResponseSchema,
351
+ j as TokenInfoSchema,
352
+ k as TokenResponseSchema,
353
+ x as UserInfoResponseSchema
225
354
  };
package/dist/index.umd.js CHANGED
@@ -1 +1 @@
1
- (function(t,e){typeof exports=="object"&&typeof module!="undefined"?e(exports,require("zod/v4-mini"),require("js-sha256")):typeof define=="function"&&define.amd?define(["exports","zod/v4-mini","js-sha256"],e):(t=typeof globalThis!="undefined"?globalThis:t||self,e(t.AlienSsoCore={},t.Zod,t.jsSha256))})(this,(function(t,e,i){"use strict";var f=(t,e,i)=>new Promise((S,g)=>{var m=c=>{try{u(i.next(c))}catch(d){g(d)}},_=c=>{try{u(i.throw(c))}catch(d){g(d)}},u=c=>c.done?S(c.value):Promise.resolve(c.value).then(m,_);u((i=i.apply(t,e)).next())});const S=e.z.object({code_challenge:e.z.string(),code_challenge_method:e.z.literal("S256")}),g=e.z.object({deep_link:e.z.string(),polling_code:e.z.string(),expired_at:e.z.number()}),m=e.z.object({polling_code:e.z.string()}),_=["pending","authorized","rejected","expired"],u=e.z.enum(_),c=e.z.object({status:u,authorization_code:e.z.optional(e.z.string())}),d=e.z.object({authorization_code:e.z.string(),code_verifier:e.z.string()}),y=e.z.object({access_token:e.z.string()}),R=e.z.object({access_token:e.z.string()}),k=e.z.object({is_valid:e.z.boolean(),access_token:e.z.optional(e.z.string())}),A=e.z.object({app_callback_session_address:e.z.string(),expired_at:e.z.number(),issued_at:e.z.number()});function T(h){return btoa(h).replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}function j(h){let o=h.replace(/-/g,"+").replace(/_/g,"/");for(;o.length%4;)o+="=";return atob(o)}const C="https://sso.alien.com",w=5e3,l="alien-sso_",z=(h,o)=>new URL(o,h).toString(),b=e.z.object({ssoBaseUrl:e.z.url(),providerAddress:e.z.string(),pollingInterval:e.z.optional(e.z.number())});class E{constructor(o){this.config=b.parse(o),this.ssoBaseUrl=this.config.ssoBaseUrl||C,this.providerAddress=this.config.providerAddress,this.pollingInterval=this.config.pollingInterval||w}generateCodeVerifier(o=128){let s;const r=typeof window!="undefined"&&window.crypto;if(r&&r.getRandomValues)s=new Uint8Array(o),r.getRandomValues(s);else{s=new Uint8Array(o);for(let n=0;n<o;n++)s[n]=Math.floor(Math.random()*256)}let a="";for(let n=0;n<s.length;n++)a+=String.fromCharCode(s[n]);return T(a)}generateCodeChallenge(o){return i.sha256(o)}generateDeeplink(){return f(this,null,function*(){const o=this.generateCodeVerifier(),s=this.generateCodeChallenge(o);sessionStorage.setItem(l+"code_verifier",o);const r=`${this.config.ssoBaseUrl}/sso/authorize`,a={code_challenge:s,code_challenge_method:"S256"};S.parse(a);const p=yield(yield fetch(r,{method:"POST",headers:{"Content-Type":"application/json","X-PROVIDER-ADDRESS":this.providerAddress},body:JSON.stringify(a)})).json();return g.parse(p)})}pollAuth(o){return f(this,null,function*(){const s={polling_code:o};m.parse(s);const r=yield fetch(z(this.config.ssoBaseUrl,"/sso/poll"),{method:"POST",headers:{"Content-Type":"application/json","X-PROVIDER-ADDRESS":this.providerAddress},body:JSON.stringify(s)});if(!r.ok)throw new Error(`Poll failed: ${r.statusText}`);const a=yield r.json();return c.parse(a)})}exchangeToken(o){return f(this,null,function*(){const s=sessionStorage.getItem(l+"code_verifier");if(!s)throw new Error("Missing code verifier.");const r={authorization_code:o,code_verifier:s};d.parse(r);const a=yield fetch(z(this.config.ssoBaseUrl,"/sso/access_token/exchange"),{method:"POST",headers:{"Content-Type":"application/json","X-PROVIDER-ADDRESS":this.providerAddress},body:JSON.stringify(r)});if(!a.ok)throw new Error(`ExchangeCode failed: ${a.statusText}`);const n=yield a.json(),p=y.parse(n);if(p.access_token)return localStorage.setItem(l+"access_token",p.access_token),p.access_token;throw new Error("Exchange failed")})}verifyAuth(){return f(this,null,function*(){const o=this.getAccessToken();if(!o)return!1;const s={access_token:o};R.parse(s);const r=yield fetch(z(this.config.ssoBaseUrl,"/sso/access_token/verify"),{method:"POST",headers:{"Content-Type":"application/json","X-PROVIDER-ADDRESS":this.providerAddress},body:JSON.stringify(s)});if(!r.ok)return!1;const a=yield r.json(),n=k.parse(a);return n.access_token&&localStorage.setItem(l+"access_token",n.access_token),n.is_valid})}getAccessToken(){return localStorage.getItem(l+"access_token")}getAuthData(){const o=this.getAccessToken();if(!o)return null;const s=o.split(".");if(s.length!==3)return null;let r;try{const n=j(s[0]);r=JSON.parse(n)}catch(n){return null}if(r.alg!=="HS256"||r.typ!=="JWT")return null;let a;try{const n=JSON.parse(j(s[1]));a=A.parse(n)}catch(n){return null}return a}logout(){localStorage.removeItem(l+"access_token"),sessionStorage.removeItem(l+"code_verifier")}}t.AlienSsoClient=E,t.AlienSsoClientSchema=b,t.AuthorizeRequestSchema=S,t.AuthorizeResponseSchema=g,t.ExchangeCodeRequestSchema=d,t.ExchangeCodeResponseSchema=y,t.PollRequestSchema=m,t.PollResponseSchema=c,t.TokenInfoSchema=A,t.VerifyTokenRequestSchema=R,t.VerifyTokenResponseSchema=k,Object.defineProperty(t,Symbol.toStringTag,{value:"Module"})}));
1
+ (function(c,t){typeof exports=="object"&&typeof module!="undefined"?t(exports,require("zod/v4-mini"),require("js-sha256")):typeof define=="function"&&define.amd?define(["exports","zod/v4-mini","js-sha256"],t):(c=typeof globalThis!="undefined"?globalThis:c||self,t(c.AlienSsoCore={},c.Zod,c.jsSha256))})(this,(function(c,t,g){"use strict";var h=(c,t,g)=>new Promise((k,m)=>{var T=l=>{try{f(g.next(l))}catch(p){m(p)}},z=l=>{try{f(g.throw(l))}catch(p){m(p)}},f=l=>l.done?k(l.value):Promise.resolve(l.value).then(T,z);f((g=g.apply(c,t)).next())});const k=t.z.object({deep_link:t.z.string(),polling_code:t.z.string(),expired_at:t.z.number()}),m=t.z.object({polling_code:t.z.string()}),T=["pending","authorized","rejected","expired"],z=t.z.enum(T),f=t.z.object({status:z,authorization_code:t.z.optional(t.z.string())}),l=t.z.object({access_token:t.z.string(),token_type:t.z.string(),expires_in:t.z.number(),id_token:t.z.string(),refresh_token:t.z.string()}),p=t.z.object({sub:t.z.string()}),b=t.z.object({iss:t.z.string(),sub:t.z.string(),aud:t.z.union([t.z.string(),t.z.array(t.z.string())]),exp:t.z.number(),iat:t.z.number(),nonce:t.z.optional(t.z.string()),auth_time:t.z.optional(t.z.number())}),P=l;function I(S){return btoa(S).replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}function j(S){let e=S.replace(/-/g,"+").replace(/_/g,"/");for(;e.length%4;)e+="=";return atob(e)}const x="https://sso.alien.com",U=5e3,i="alien-sso_",w=i+"refresh_token",_=i+"token_expiry",y=(S,e)=>new URL(e,S).toString(),E=t.z.object({ssoBaseUrl:t.z.url(),providerAddress:t.z.string(),pollingInterval:t.z.optional(t.z.number())}),d=class d{constructor(e){this.config=E.parse(e),this.ssoBaseUrl=this.config.ssoBaseUrl||x,this.providerAddress=this.config.providerAddress,this.pollingInterval=this.config.pollingInterval||U}generateCodeVerifier(e=128){let o;const r=typeof window!="undefined"&&window.crypto;if(r&&r.getRandomValues)o=new Uint8Array(e),r.getRandomValues(o);else{o=new Uint8Array(e);for(let a=0;a<e;a++)o[a]=Math.floor(Math.random()*256)}let s="";for(let a=0;a<o.length;a++)s+=String.fromCharCode(o[a]);return I(s)}generateCodeChallenge(e){const o=g.sha256.array(e),r=String.fromCharCode(...o);return I(r)}generateDeeplink(){return h(this,null,function*(){const e=this.generateCodeVerifier(),o=this.generateCodeChallenge(e);sessionStorage.setItem(i+"code_verifier",e);const r=new URLSearchParams({response_type:"code",response_mode:"json",client_id:this.providerAddress,scope:"openid",code_challenge:o,code_challenge_method:"S256"}),s=`${this.config.ssoBaseUrl}/oauth/authorize?${r.toString()}`,a=yield fetch(s,{method:"GET"});if(!a.ok){const u=yield a.json().catch(()=>({error:a.statusText}));throw new Error(`Authorize failed: ${u.error_description||u.error||a.statusText}`)}const n=yield a.json();return k.parse(n)})}pollAuth(e){return h(this,null,function*(){const o={polling_code:e};m.parse(o);const r=yield fetch(y(this.config.ssoBaseUrl,"/oauth/poll"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(o)});if(!r.ok)throw new Error(`Poll failed: ${r.statusText}`);const s=yield r.json();return f.parse(s)})}exchangeToken(e){return h(this,null,function*(){const o=sessionStorage.getItem(i+"code_verifier");if(!o)throw new Error("Missing code verifier.");const r=new URLSearchParams({grant_type:"authorization_code",code:e,client_id:this.providerAddress,code_verifier:o}),s=yield fetch(y(this.config.ssoBaseUrl,"/oauth/token"),{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:r.toString()});if(!s.ok){const R=yield s.json().catch(()=>({error:s.statusText}));throw new Error(`Token exchange failed: ${R.error_description||R.error||s.statusText}`)}const a=yield s.json(),n=l.parse(a);localStorage.setItem(i+"access_token",n.access_token),localStorage.setItem(i+"id_token",n.id_token),localStorage.setItem(w,n.refresh_token);const u=Date.now()+n.expires_in*1e3;return localStorage.setItem(_,u.toString()),sessionStorage.removeItem(i+"code_verifier"),n})}verifyAuth(){return h(this,null,function*(){return this.withAutoRefresh(()=>h(this,null,function*(){const e=this.getAccessToken();if(!e)return null;const o=yield fetch(y(this.config.ssoBaseUrl,"/oauth/userinfo"),{method:"GET",headers:{Authorization:`Bearer ${e}`}});if(!o.ok){if(o.status===401){const s=new Error("Unauthorized");throw s.response={status:401},s}return null}const r=yield o.json();return p.parse(r)}))})}getAccessToken(){return localStorage.getItem(i+"access_token")}getIdToken(){return localStorage.getItem(i+"id_token")}getAuthData(){const e=this.getIdToken()||this.getAccessToken();if(!e)return null;const o=e.split(".");if(o.length!==3)return null;let r;try{const n=j(o[0]);r=JSON.parse(n)}catch(n){return null}if(r.alg!=="RS256"||r.typ!=="JWT")return null;let s;try{const n=JSON.parse(j(o[1]));s=b.parse(n)}catch(n){return null}return(Array.isArray(s.aud)?s.aud:[s.aud]).includes(this.providerAddress)?s:null}getSubject(){const e=this.getAuthData();return(e==null?void 0:e.sub)||null}isTokenExpired(){const e=this.getAuthData();return e?Date.now()/1e3>e.exp:!0}logout(){localStorage.removeItem(i+"access_token"),localStorage.removeItem(i+"id_token"),localStorage.removeItem(w),localStorage.removeItem(_),sessionStorage.removeItem(i+"code_verifier")}getRefreshToken(){return localStorage.getItem(w)}hasRefreshToken(){return!!this.getRefreshToken()}isAccessTokenExpired(){const e=localStorage.getItem(_);if(!e)return!0;const o=parseInt(e,10),r=Date.now(),s=300*1e3;return r>=o-s}refreshAccessToken(){return h(this,null,function*(){return d.refreshPromise||(d.refreshPromise=this.doRefreshAccessToken().finally(()=>{d.refreshPromise=null})),d.refreshPromise})}doRefreshAccessToken(){return h(this,null,function*(){const e=this.getRefreshToken();if(!e)throw new Error("No refresh token available");const o=new URLSearchParams({grant_type:"refresh_token",refresh_token:e,client_id:this.providerAddress}),r=yield fetch(y(this.config.ssoBaseUrl,"/oauth/token"),{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:o.toString()});if(!r.ok){const u=yield r.json().catch(()=>({error:r.statusText}));throw this.logout(),new Error(`Token refresh failed: ${u.error_description||u.error||r.statusText}`)}const s=yield r.json(),a=l.parse(s);localStorage.setItem(i+"access_token",a.access_token),localStorage.setItem(i+"id_token",a.id_token),localStorage.setItem(w,a.refresh_token);const n=Date.now()+a.expires_in*1e3;return localStorage.setItem(_,n.toString()),a})}withAutoRefresh(e,o=1){return h(this,null,function*(){var r,s,a;try{return yield e()}catch(n){if((((r=n==null?void 0:n.response)==null?void 0:r.status)===401||((s=n==null?void 0:n.message)==null?void 0:s.includes("401"))||((a=n==null?void 0:n.message)==null?void 0:a.includes("Unauthorized")))&&o>0&&this.hasRefreshToken())try{return yield this.refreshAccessToken(),yield e()}catch(R){throw n}throw n}})}};d.refreshPromise=null;let A=d;c.AlienSsoClient=A,c.AlienSsoClientSchema=E,c.AuthorizeResponseSchema=k,c.ExchangeCodeResponseSchema=P,c.PollRequestSchema=m,c.PollResponseSchema=f,c.TokenInfoSchema=b,c.TokenResponseSchema=l,c.UserInfoResponseSchema=p,Object.defineProperty(c,Symbol.toStringTag,{value:"Module"})}));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alien_org/sso-sdk-core",
3
- "version": "1.0.18",
3
+ "version": "1.0.20",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/alien-id/sso-sdk-js.git"