@alien_org/sso-sdk-core 1.0.18 → 1.0.19

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