@alien_org/sso-sdk-core 1.0.19 → 1.0.21
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 +1 -1
- package/dist/index.d.ts +8 -2
- package/dist/index.esm.js +75 -62
- package/dist/index.umd.js +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var h=(u,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(u,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.optional(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 _(u){return btoa(u).replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}function w(u){let e=u.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=(u,e)=>new URL(e,u).toString(),b=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=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 _(s)}generateCodeChallenge(e){const o=I.sha256.array(e),r=String.fromCharCode(...o);return _(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),n.id_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=w(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(w(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 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(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),a.id_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}})}};d.refreshPromise=null;let k=d;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,6 +5,7 @@ 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;
|
|
@@ -70,8 +71,13 @@ export declare class AlienSsoClient {
|
|
|
70
71
|
/**
|
|
71
72
|
* Refreshes the access token using the stored refresh token
|
|
72
73
|
* POST /oauth/token with grant_type=refresh_token
|
|
74
|
+
* Uses singleton pattern to prevent concurrent refresh requests (race condition)
|
|
73
75
|
*/
|
|
74
76
|
refreshAccessToken(): Promise<TokenResponse>;
|
|
77
|
+
/**
|
|
78
|
+
* Internal method that performs the actual token refresh
|
|
79
|
+
*/
|
|
80
|
+
private doRefreshAccessToken;
|
|
75
81
|
/**
|
|
76
82
|
* Executes a function that makes an authenticated request
|
|
77
83
|
* Automatically refreshes token and retries on 401 error
|
|
@@ -105,7 +111,7 @@ export declare const ExchangeCodeResponseSchema: z.ZodMiniObject<{
|
|
|
105
111
|
access_token: z.ZodMiniString<string>;
|
|
106
112
|
token_type: z.ZodMiniString<string>;
|
|
107
113
|
expires_in: z.ZodMiniNumber<number>;
|
|
108
|
-
id_token: z.ZodMiniString<string
|
|
114
|
+
id_token: z.ZodMiniOptional<z.ZodMiniString<string>>;
|
|
109
115
|
refresh_token: z.ZodMiniString<string>;
|
|
110
116
|
}, z.core.$strip>;
|
|
111
117
|
|
|
@@ -163,7 +169,7 @@ export declare const TokenResponseSchema: z.ZodMiniObject<{
|
|
|
163
169
|
access_token: z.ZodMiniString<string>;
|
|
164
170
|
token_type: z.ZodMiniString<string>;
|
|
165
171
|
expires_in: z.ZodMiniNumber<number>;
|
|
166
|
-
id_token: z.ZodMiniString<string
|
|
172
|
+
id_token: z.ZodMiniOptional<z.ZodMiniString<string>>;
|
|
167
173
|
refresh_token: z.ZodMiniString<string>;
|
|
168
174
|
}, z.core.$strip>;
|
|
169
175
|
|
package/dist/index.esm.js
CHANGED
|
@@ -1,39 +1,40 @@
|
|
|
1
|
-
var
|
|
1
|
+
var h = (u, e, o) => new Promise((r, s) => {
|
|
2
2
|
var a = (l) => {
|
|
3
3
|
try {
|
|
4
4
|
c(o.next(l));
|
|
5
|
-
} catch (
|
|
6
|
-
s(
|
|
5
|
+
} catch (m) {
|
|
6
|
+
s(m);
|
|
7
7
|
}
|
|
8
8
|
}, n = (l) => {
|
|
9
9
|
try {
|
|
10
10
|
c(o.throw(l));
|
|
11
|
-
} catch (
|
|
12
|
-
s(
|
|
11
|
+
} catch (m) {
|
|
12
|
+
s(m);
|
|
13
13
|
}
|
|
14
14
|
}, c = (l) => l.done ? r(l.value) : Promise.resolve(l.value).then(a, n);
|
|
15
|
-
c((o = o.apply(
|
|
15
|
+
c((o = o.apply(u, e)).next());
|
|
16
16
|
});
|
|
17
17
|
import { z as t } from "zod/v4-mini";
|
|
18
|
-
import { sha256 as
|
|
19
|
-
const
|
|
18
|
+
import { sha256 as y } from "js-sha256";
|
|
19
|
+
const T = t.object({
|
|
20
20
|
deep_link: t.string(),
|
|
21
21
|
polling_code: t.string(),
|
|
22
22
|
expired_at: t.number()
|
|
23
|
-
}),
|
|
23
|
+
}), b = t.object({
|
|
24
24
|
polling_code: t.string()
|
|
25
|
-
}),
|
|
26
|
-
status:
|
|
25
|
+
}), A = ["pending", "authorized", "rejected", "expired"], I = t.enum(A), R = t.object({
|
|
26
|
+
status: I,
|
|
27
27
|
authorization_code: t.optional(t.string())
|
|
28
|
-
}),
|
|
28
|
+
}), k = t.object({
|
|
29
29
|
access_token: t.string(),
|
|
30
30
|
token_type: t.string(),
|
|
31
31
|
expires_in: t.number(),
|
|
32
|
-
id_token: t.string(),
|
|
32
|
+
id_token: t.optional(t.string()),
|
|
33
|
+
// Optional - not returned on refresh_token grant
|
|
33
34
|
refresh_token: t.string()
|
|
34
|
-
}),
|
|
35
|
+
}), x = t.object({
|
|
35
36
|
sub: t.string()
|
|
36
|
-
}),
|
|
37
|
+
}), j = t.object({
|
|
37
38
|
iss: t.string(),
|
|
38
39
|
sub: t.string(),
|
|
39
40
|
aud: t.union([t.string(), t.array(t.string())]),
|
|
@@ -41,24 +42,23 @@ const w = t.object({
|
|
|
41
42
|
iat: t.number(),
|
|
42
43
|
nonce: t.optional(t.string()),
|
|
43
44
|
auth_time: t.optional(t.number())
|
|
44
|
-
}),
|
|
45
|
-
function _(
|
|
46
|
-
return btoa(
|
|
45
|
+
}), z = k;
|
|
46
|
+
function _(u) {
|
|
47
|
+
return btoa(u).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
|
47
48
|
}
|
|
48
|
-
function
|
|
49
|
-
let e =
|
|
49
|
+
function w(u) {
|
|
50
|
+
let e = u.replace(/-/g, "+").replace(/_/g, "/");
|
|
50
51
|
for (; e.length % 4; )
|
|
51
52
|
e += "=";
|
|
52
53
|
return atob(e);
|
|
53
54
|
}
|
|
54
|
-
const
|
|
55
|
+
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
56
|
ssoBaseUrl: t.url(),
|
|
56
57
|
providerAddress: t.string(),
|
|
57
58
|
pollingInterval: t.optional(t.number())
|
|
58
|
-
})
|
|
59
|
-
class O {
|
|
59
|
+
}), d = class d {
|
|
60
60
|
constructor(e) {
|
|
61
|
-
this.config =
|
|
61
|
+
this.config = U.parse(e), this.ssoBaseUrl = this.config.ssoBaseUrl || E, this.providerAddress = this.config.providerAddress, this.pollingInterval = this.config.pollingInterval || v;
|
|
62
62
|
}
|
|
63
63
|
generateCodeVerifier(e = 128) {
|
|
64
64
|
let o;
|
|
@@ -76,7 +76,7 @@ class O {
|
|
|
76
76
|
return _(s);
|
|
77
77
|
}
|
|
78
78
|
generateCodeChallenge(e) {
|
|
79
|
-
const o =
|
|
79
|
+
const o = y.array(e), r = String.fromCharCode(...o);
|
|
80
80
|
return _(r);
|
|
81
81
|
}
|
|
82
82
|
/**
|
|
@@ -84,7 +84,7 @@ class O {
|
|
|
84
84
|
* GET /oauth/authorize?response_type=code&response_mode=json&...
|
|
85
85
|
*/
|
|
86
86
|
generateDeeplink() {
|
|
87
|
-
return
|
|
87
|
+
return h(this, null, function* () {
|
|
88
88
|
const e = this.generateCodeVerifier(), o = this.generateCodeChallenge(e);
|
|
89
89
|
sessionStorage.setItem(i + "code_verifier", e);
|
|
90
90
|
const r = new URLSearchParams({
|
|
@@ -102,7 +102,7 @@ class O {
|
|
|
102
102
|
throw new Error(`Authorize failed: ${c.error_description || c.error || a.statusText}`);
|
|
103
103
|
}
|
|
104
104
|
const n = yield a.json();
|
|
105
|
-
return
|
|
105
|
+
return T.parse(n);
|
|
106
106
|
});
|
|
107
107
|
}
|
|
108
108
|
/**
|
|
@@ -110,12 +110,12 @@ class O {
|
|
|
110
110
|
* POST /oauth/poll
|
|
111
111
|
*/
|
|
112
112
|
pollAuth(e) {
|
|
113
|
-
return
|
|
113
|
+
return h(this, null, function* () {
|
|
114
114
|
const o = {
|
|
115
115
|
polling_code: e
|
|
116
116
|
};
|
|
117
|
-
|
|
118
|
-
const r = yield fetch(
|
|
117
|
+
b.parse(o);
|
|
118
|
+
const r = yield fetch(f(this.config.ssoBaseUrl, "/oauth/poll"), {
|
|
119
119
|
method: "POST",
|
|
120
120
|
headers: {
|
|
121
121
|
"Content-Type": "application/json"
|
|
@@ -125,7 +125,7 @@ class O {
|
|
|
125
125
|
if (!r.ok)
|
|
126
126
|
throw new Error(`Poll failed: ${r.statusText}`);
|
|
127
127
|
const s = yield r.json();
|
|
128
|
-
return
|
|
128
|
+
return R.parse(s);
|
|
129
129
|
});
|
|
130
130
|
}
|
|
131
131
|
/**
|
|
@@ -134,7 +134,7 @@ class O {
|
|
|
134
134
|
* Returns both access_token and id_token
|
|
135
135
|
*/
|
|
136
136
|
exchangeToken(e) {
|
|
137
|
-
return
|
|
137
|
+
return h(this, null, function* () {
|
|
138
138
|
const o = sessionStorage.getItem(i + "code_verifier");
|
|
139
139
|
if (!o) throw new Error("Missing code verifier.");
|
|
140
140
|
const r = new URLSearchParams({
|
|
@@ -143,7 +143,7 @@ class O {
|
|
|
143
143
|
client_id: this.providerAddress,
|
|
144
144
|
code_verifier: o
|
|
145
145
|
}), s = yield fetch(
|
|
146
|
-
|
|
146
|
+
f(this.config.ssoBaseUrl, "/oauth/token"),
|
|
147
147
|
{
|
|
148
148
|
method: "POST",
|
|
149
149
|
headers: {
|
|
@@ -156,10 +156,10 @@ class O {
|
|
|
156
156
|
const l = yield s.json().catch(() => ({ error: s.statusText }));
|
|
157
157
|
throw new Error(`Token exchange failed: ${l.error_description || l.error || s.statusText}`);
|
|
158
158
|
}
|
|
159
|
-
const a = yield s.json(), n =
|
|
160
|
-
localStorage.setItem(i + "access_token", n.access_token), localStorage.setItem(i + "id_token", n.id_token), localStorage.setItem(
|
|
159
|
+
const a = yield s.json(), n = k.parse(a);
|
|
160
|
+
localStorage.setItem(i + "access_token", n.access_token), n.id_token && localStorage.setItem(i + "id_token", n.id_token), localStorage.setItem(g, n.refresh_token);
|
|
161
161
|
const c = Date.now() + n.expires_in * 1e3;
|
|
162
|
-
return localStorage.setItem(
|
|
162
|
+
return localStorage.setItem(p, c.toString()), sessionStorage.removeItem(i + "code_verifier"), n;
|
|
163
163
|
});
|
|
164
164
|
}
|
|
165
165
|
/**
|
|
@@ -168,13 +168,13 @@ class O {
|
|
|
168
168
|
* Automatically refreshes token on 401 if refresh token is available
|
|
169
169
|
*/
|
|
170
170
|
verifyAuth() {
|
|
171
|
-
return
|
|
172
|
-
return this.withAutoRefresh(() =>
|
|
171
|
+
return h(this, null, function* () {
|
|
172
|
+
return this.withAutoRefresh(() => h(this, null, function* () {
|
|
173
173
|
const e = this.getAccessToken();
|
|
174
174
|
if (!e)
|
|
175
175
|
return null;
|
|
176
176
|
const o = yield fetch(
|
|
177
|
-
|
|
177
|
+
f(this.config.ssoBaseUrl, "/oauth/userinfo"),
|
|
178
178
|
{
|
|
179
179
|
method: "GET",
|
|
180
180
|
headers: {
|
|
@@ -190,7 +190,7 @@ class O {
|
|
|
190
190
|
return null;
|
|
191
191
|
}
|
|
192
192
|
const r = yield o.json();
|
|
193
|
-
return
|
|
193
|
+
return x.parse(r);
|
|
194
194
|
}));
|
|
195
195
|
});
|
|
196
196
|
}
|
|
@@ -218,7 +218,7 @@ class O {
|
|
|
218
218
|
return null;
|
|
219
219
|
let r;
|
|
220
220
|
try {
|
|
221
|
-
const n =
|
|
221
|
+
const n = w(o[0]);
|
|
222
222
|
r = JSON.parse(n);
|
|
223
223
|
} catch (n) {
|
|
224
224
|
return null;
|
|
@@ -227,8 +227,8 @@ class O {
|
|
|
227
227
|
return null;
|
|
228
228
|
let s;
|
|
229
229
|
try {
|
|
230
|
-
const n = JSON.parse(
|
|
231
|
-
s =
|
|
230
|
+
const n = JSON.parse(w(o[1]));
|
|
231
|
+
s = j.parse(n);
|
|
232
232
|
} catch (n) {
|
|
233
233
|
return null;
|
|
234
234
|
}
|
|
@@ -252,13 +252,13 @@ class O {
|
|
|
252
252
|
* Clears all stored authentication data
|
|
253
253
|
*/
|
|
254
254
|
logout() {
|
|
255
|
-
localStorage.removeItem(i + "access_token"), localStorage.removeItem(i + "id_token"), localStorage.removeItem(
|
|
255
|
+
localStorage.removeItem(i + "access_token"), localStorage.removeItem(i + "id_token"), localStorage.removeItem(g), localStorage.removeItem(p), sessionStorage.removeItem(i + "code_verifier");
|
|
256
256
|
}
|
|
257
257
|
/**
|
|
258
258
|
* Gets stored refresh token
|
|
259
259
|
*/
|
|
260
260
|
getRefreshToken() {
|
|
261
|
-
return localStorage.getItem(
|
|
261
|
+
return localStorage.getItem(g);
|
|
262
262
|
}
|
|
263
263
|
/**
|
|
264
264
|
* Checks if a refresh token is available
|
|
@@ -270,7 +270,7 @@ class O {
|
|
|
270
270
|
* Checks if the access token is expired or will expire soon (within 5 minutes)
|
|
271
271
|
*/
|
|
272
272
|
isAccessTokenExpired() {
|
|
273
|
-
const e = localStorage.getItem(
|
|
273
|
+
const e = localStorage.getItem(p);
|
|
274
274
|
if (!e) return !0;
|
|
275
275
|
const o = parseInt(e, 10), r = Date.now(), s = 300 * 1e3;
|
|
276
276
|
return r >= o - s;
|
|
@@ -278,9 +278,20 @@ class O {
|
|
|
278
278
|
/**
|
|
279
279
|
* Refreshes the access token using the stored refresh token
|
|
280
280
|
* POST /oauth/token with grant_type=refresh_token
|
|
281
|
+
* Uses singleton pattern to prevent concurrent refresh requests (race condition)
|
|
281
282
|
*/
|
|
282
283
|
refreshAccessToken() {
|
|
283
|
-
return
|
|
284
|
+
return h(this, null, function* () {
|
|
285
|
+
return d.refreshPromise || (d.refreshPromise = this.doRefreshAccessToken().finally(() => {
|
|
286
|
+
d.refreshPromise = null;
|
|
287
|
+
})), d.refreshPromise;
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Internal method that performs the actual token refresh
|
|
292
|
+
*/
|
|
293
|
+
doRefreshAccessToken() {
|
|
294
|
+
return h(this, null, function* () {
|
|
284
295
|
const e = this.getRefreshToken();
|
|
285
296
|
if (!e)
|
|
286
297
|
throw new Error("No refresh token available");
|
|
@@ -289,7 +300,7 @@ class O {
|
|
|
289
300
|
refresh_token: e,
|
|
290
301
|
client_id: this.providerAddress
|
|
291
302
|
}), r = yield fetch(
|
|
292
|
-
|
|
303
|
+
f(this.config.ssoBaseUrl, "/oauth/token"),
|
|
293
304
|
{
|
|
294
305
|
method: "POST",
|
|
295
306
|
headers: {
|
|
@@ -302,10 +313,10 @@ class O {
|
|
|
302
313
|
const c = yield r.json().catch(() => ({ error: r.statusText }));
|
|
303
314
|
throw this.logout(), new Error(`Token refresh failed: ${c.error_description || c.error || r.statusText}`);
|
|
304
315
|
}
|
|
305
|
-
const s = yield r.json(), a =
|
|
306
|
-
localStorage.setItem(i + "access_token", a.access_token), localStorage.setItem(i + "id_token", a.id_token), localStorage.setItem(
|
|
316
|
+
const s = yield r.json(), a = k.parse(s);
|
|
317
|
+
localStorage.setItem(i + "access_token", a.access_token), a.id_token && localStorage.setItem(i + "id_token", a.id_token), localStorage.setItem(g, a.refresh_token);
|
|
307
318
|
const n = Date.now() + a.expires_in * 1e3;
|
|
308
|
-
return localStorage.setItem(
|
|
319
|
+
return localStorage.setItem(p, n.toString()), a;
|
|
309
320
|
});
|
|
310
321
|
}
|
|
311
322
|
/**
|
|
@@ -313,7 +324,7 @@ class O {
|
|
|
313
324
|
* Automatically refreshes token and retries on 401 error
|
|
314
325
|
*/
|
|
315
326
|
withAutoRefresh(e, o = 1) {
|
|
316
|
-
return
|
|
327
|
+
return h(this, null, function* () {
|
|
317
328
|
var r, s, a;
|
|
318
329
|
try {
|
|
319
330
|
return yield e();
|
|
@@ -328,15 +339,17 @@ class O {
|
|
|
328
339
|
}
|
|
329
340
|
});
|
|
330
341
|
}
|
|
331
|
-
}
|
|
342
|
+
};
|
|
343
|
+
d.refreshPromise = null;
|
|
344
|
+
let S = d;
|
|
332
345
|
export {
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
346
|
+
S as AlienSsoClient,
|
|
347
|
+
U as AlienSsoClientSchema,
|
|
348
|
+
T as AuthorizeResponseSchema,
|
|
349
|
+
z as ExchangeCodeResponseSchema,
|
|
350
|
+
b as PollRequestSchema,
|
|
351
|
+
R as PollResponseSchema,
|
|
352
|
+
j as TokenInfoSchema,
|
|
353
|
+
k as TokenResponseSchema,
|
|
354
|
+
x as UserInfoResponseSchema
|
|
342
355
|
};
|
package/dist/index.umd.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(function(c,
|
|
1
|
+
(function(c,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):(c=typeof globalThis!="undefined"?globalThis:c||self,e(c.AlienSsoCore={},c.Zod,c.jsSha256))})(this,(function(c,e,f){"use strict";var h=(c,e,f)=>new Promise((k,m)=>{var T=l=>{try{g(f.next(l))}catch(p){m(p)}},z=l=>{try{g(f.throw(l))}catch(p){m(p)}},g=l=>l.done?k(l.value):Promise.resolve(l.value).then(T,z);g((f=f.apply(c,e)).next())});const k=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()}),T=["pending","authorized","rejected","expired"],z=e.z.enum(T),g=e.z.object({status:z,authorization_code:e.z.optional(e.z.string())}),l=e.z.object({access_token:e.z.string(),token_type:e.z.string(),expires_in:e.z.number(),id_token:e.z.optional(e.z.string()),refresh_token:e.z.string()}),p=e.z.object({sub:e.z.string()}),b=e.z.object({iss:e.z.string(),sub:e.z.string(),aud:e.z.union([e.z.string(),e.z.array(e.z.string())]),exp:e.z.number(),iat:e.z.number(),nonce:e.z.optional(e.z.string()),auth_time:e.z.optional(e.z.number())}),P=l;function I(S){return btoa(S).replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}function j(S){let t=S.replace(/-/g,"+").replace(/_/g,"/");for(;t.length%4;)t+="=";return atob(t)}const x="https://sso.alien.com",U=5e3,i="alien-sso_",_=i+"refresh_token",w=i+"token_expiry",y=(S,t)=>new URL(t,S).toString(),E=e.z.object({ssoBaseUrl:e.z.url(),providerAddress:e.z.string(),pollingInterval:e.z.optional(e.z.number())}),d=class d{constructor(t){this.config=E.parse(t),this.ssoBaseUrl=this.config.ssoBaseUrl||x,this.providerAddress=this.config.providerAddress,this.pollingInterval=this.config.pollingInterval||U}generateCodeVerifier(t=128){let o;const r=typeof window!="undefined"&&window.crypto;if(r&&r.getRandomValues)o=new Uint8Array(t),r.getRandomValues(o);else{o=new Uint8Array(t);for(let a=0;a<t;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(t){const o=f.sha256.array(t),r=String.fromCharCode(...o);return I(r)}generateDeeplink(){return h(this,null,function*(){const t=this.generateCodeVerifier(),o=this.generateCodeChallenge(t);sessionStorage.setItem(i+"code_verifier",t);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(t){return h(this,null,function*(){const o={polling_code:t};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 g.parse(s)})}exchangeToken(t){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:t,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),n.id_token&&localStorage.setItem(i+"id_token",n.id_token),localStorage.setItem(_,n.refresh_token);const u=Date.now()+n.expires_in*1e3;return localStorage.setItem(w,u.toString()),sessionStorage.removeItem(i+"code_verifier"),n})}verifyAuth(){return h(this,null,function*(){return this.withAutoRefresh(()=>h(this,null,function*(){const t=this.getAccessToken();if(!t)return null;const o=yield fetch(y(this.config.ssoBaseUrl,"/oauth/userinfo"),{method:"GET",headers:{Authorization:`Bearer ${t}`}});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 t=this.getIdToken()||this.getAccessToken();if(!t)return null;const o=t.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 t=this.getAuthData();return(t==null?void 0:t.sub)||null}isTokenExpired(){const t=this.getAuthData();return t?Date.now()/1e3>t.exp:!0}logout(){localStorage.removeItem(i+"access_token"),localStorage.removeItem(i+"id_token"),localStorage.removeItem(_),localStorage.removeItem(w),sessionStorage.removeItem(i+"code_verifier")}getRefreshToken(){return localStorage.getItem(_)}hasRefreshToken(){return!!this.getRefreshToken()}isAccessTokenExpired(){const t=localStorage.getItem(w);if(!t)return!0;const o=parseInt(t,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 t=this.getRefreshToken();if(!t)throw new Error("No refresh token available");const o=new URLSearchParams({grant_type:"refresh_token",refresh_token:t,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),a.id_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(w,n.toString()),a})}withAutoRefresh(t,o=1){return h(this,null,function*(){var r,s,a;try{return yield t()}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 t()}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=g,c.TokenInfoSchema=b,c.TokenResponseSchema=l,c.UserInfoResponseSchema=p,Object.defineProperty(c,Symbol.toStringTag,{value:"Module"})}));
|