@alien-id/sso 1.0.25
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/README.md +87 -0
- package/dist/index.cjs +1 -0
- package/dist/index.d.ts +186 -0
- package/dist/index.esm.js +355 -0
- package/dist/index.umd.js +1 -0
- package/package.json +56 -0
package/README.md
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# @alien_org/sso-sdk-core
|
|
2
|
+
|
|
3
|
+
Core TypeScript client for [Alien SSO](https://alien.org) authentication. Provides OIDC-compatible authentication with blockchain and TEE backing.
|
|
4
|
+
|
|
5
|
+
## ⚠️ Alpha Version Notice
|
|
6
|
+
|
|
7
|
+
**This is an early alpha version.** The SDK is under active development and may contain bugs or undergo breaking changes. Use with caution in production environments.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install @alien_org/sso-sdk-core
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Features
|
|
16
|
+
|
|
17
|
+
- ✅ **TypeScript-first** with full type safety
|
|
18
|
+
- ✅ **Runtime validation** via Zod schemas
|
|
19
|
+
- ✅ **PKCE support** for secure authorization
|
|
20
|
+
- ✅ **Dual exports**: ESM and CJS
|
|
21
|
+
- ✅ **Zero UI dependencies** - use in any JavaScript environment
|
|
22
|
+
- ✅ **Storage management** for tokens and session data
|
|
23
|
+
|
|
24
|
+
## Documentation
|
|
25
|
+
|
|
26
|
+
📚 **Full documentation at [dev.alien.org/docs](https://dev.alien.org/docs)**
|
|
27
|
+
|
|
28
|
+
- **[Integration Guide](https://dev.alien.org/docs/sso-guide/core-integration)** - Complete integration walkthrough
|
|
29
|
+
- **[API Reference](https://dev.alien.org/docs/sso-api-reference/api-reference-core)** - Detailed API documentation
|
|
30
|
+
- **[What is Alien Session?](https://dev.alien.org/docs/what-is-alien-session)** - Session architecture explained
|
|
31
|
+
- **[Demo App](https://dev.alien.org/docs/sso-demo-app)** - Example application
|
|
32
|
+
|
|
33
|
+
### React Integration
|
|
34
|
+
|
|
35
|
+
If you're using React, check out [@alien_org/sso-sdk-react](https://www.npmjs.com/package/@alien_org/sso-sdk-react) for hooks and pre-built components:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
npm install @alien_org/sso-sdk-react
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Authentication Flow
|
|
42
|
+
|
|
43
|
+
1. **Generate deeplink** → Display QR code or redirect
|
|
44
|
+
2. **User authenticates** in Alien mobile app
|
|
45
|
+
3. **Poll for completion** → Get authorization code
|
|
46
|
+
4. **Exchange code** → Receive access token
|
|
47
|
+
5. **Verify token** → Validate with server (optional)
|
|
48
|
+
|
|
49
|
+
## Storage
|
|
50
|
+
|
|
51
|
+
The SDK uses browser storage for session management:
|
|
52
|
+
|
|
53
|
+
- **localStorage**: `alien-sso_access_token` - Access token
|
|
54
|
+
- **sessionStorage**: `alien-sso_code_verifier` - PKCE code verifier
|
|
55
|
+
|
|
56
|
+
## Getting a Provider Address
|
|
57
|
+
|
|
58
|
+
Register your application at the [Developer Portal](https://dev.alien.org/dashboard) to get your provider credentials.
|
|
59
|
+
|
|
60
|
+
## TypeScript Support
|
|
61
|
+
|
|
62
|
+
Includes full TypeScript declarations with Zod runtime validation:
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
import type {
|
|
66
|
+
AlienSsoClientConfig,
|
|
67
|
+
AuthorizeResponse,
|
|
68
|
+
PollResponse,
|
|
69
|
+
ExchangeCodeResponse,
|
|
70
|
+
TokenInfo
|
|
71
|
+
} from '@alien_org/sso-sdk-core';
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Browser Support
|
|
75
|
+
|
|
76
|
+
- Modern browsers with ES2020+ support
|
|
77
|
+
- Chrome, Firefox, Safari, Edge (latest versions)
|
|
78
|
+
|
|
79
|
+
## License
|
|
80
|
+
|
|
81
|
+
MIT
|
|
82
|
+
|
|
83
|
+
## Links
|
|
84
|
+
|
|
85
|
+
- [Documentation](https://dev.alien.org/docs)
|
|
86
|
+
- [GitHub Repository](https://github.com/alien-org/sso-sdk-js)
|
|
87
|
+
- [NPM Package](https://www.npmjs.com/package/@alien_org/sso-sdk-core)
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
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
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { z } from 'zod/v4-mini';
|
|
2
|
+
|
|
3
|
+
export declare class AlienSsoClient {
|
|
4
|
+
readonly config: AlienSsoClientConfig;
|
|
5
|
+
readonly pollingInterval: number;
|
|
6
|
+
readonly ssoBaseUrl: string;
|
|
7
|
+
readonly providerAddress: string;
|
|
8
|
+
private static refreshPromise;
|
|
9
|
+
constructor(config: AlienSsoClientConfig);
|
|
10
|
+
private generateCodeVerifier;
|
|
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
|
+
*/
|
|
16
|
+
generateDeeplink(): Promise<AuthorizeResponse>;
|
|
17
|
+
/**
|
|
18
|
+
* Polls for authorization completion
|
|
19
|
+
* POST /oauth/poll
|
|
20
|
+
*/
|
|
21
|
+
pollAuth(pollingCode: string): Promise<PollResponse>;
|
|
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
|
+
*/
|
|
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
|
+
*/
|
|
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
|
+
*/
|
|
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>;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export declare type AlienSsoClientConfig = z.infer<typeof AlienSsoClientSchema>;
|
|
89
|
+
|
|
90
|
+
export declare const AlienSsoClientSchema: z.ZodMiniObject<{
|
|
91
|
+
ssoBaseUrl: z.ZodMiniURL;
|
|
92
|
+
providerAddress: z.ZodMiniString<string>;
|
|
93
|
+
pollingInterval: z.ZodMiniOptional<z.ZodMiniNumber<number>>;
|
|
94
|
+
}, z.core.$strip>;
|
|
95
|
+
|
|
96
|
+
export declare type AuthorizeResponse = z.infer<typeof AuthorizeResponseSchema>;
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Authorize response schema (for response_mode=json)
|
|
100
|
+
* GET /oauth/authorize?response_mode=json&...
|
|
101
|
+
*/
|
|
102
|
+
export declare const AuthorizeResponseSchema: z.ZodMiniObject<{
|
|
103
|
+
deep_link: z.ZodMiniString<string>;
|
|
104
|
+
polling_code: z.ZodMiniString<string>;
|
|
105
|
+
expired_at: z.ZodMiniNumber<number>;
|
|
106
|
+
}, z.core.$strip>;
|
|
107
|
+
|
|
108
|
+
export declare type ExchangeCodeResponse = TokenResponse;
|
|
109
|
+
|
|
110
|
+
export declare const ExchangeCodeResponseSchema: z.ZodMiniObject<{
|
|
111
|
+
access_token: z.ZodMiniString<string>;
|
|
112
|
+
token_type: z.ZodMiniString<string>;
|
|
113
|
+
expires_in: z.ZodMiniNumber<number>;
|
|
114
|
+
id_token: z.ZodMiniOptional<z.ZodMiniString<string>>;
|
|
115
|
+
refresh_token: z.ZodMiniString<string>;
|
|
116
|
+
}, z.core.$strip>;
|
|
117
|
+
|
|
118
|
+
export declare interface JWTHeader {
|
|
119
|
+
alg: string;
|
|
120
|
+
typ: string;
|
|
121
|
+
kid?: string;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export declare type PollRequest = z.infer<typeof PollRequestSchema>;
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Poll request/response schema
|
|
128
|
+
* POST /oauth/poll
|
|
129
|
+
*/
|
|
130
|
+
export declare const PollRequestSchema: z.ZodMiniObject<{
|
|
131
|
+
polling_code: z.ZodMiniString<string>;
|
|
132
|
+
}, z.core.$strip>;
|
|
133
|
+
|
|
134
|
+
export declare type PollResponse = z.infer<typeof PollResponseSchema>;
|
|
135
|
+
|
|
136
|
+
export declare const PollResponseSchema: z.ZodMiniObject<{
|
|
137
|
+
status: z.ZodMiniEnum<{
|
|
138
|
+
pending: "pending";
|
|
139
|
+
authorized: "authorized";
|
|
140
|
+
rejected: "rejected";
|
|
141
|
+
expired: "expired";
|
|
142
|
+
}>;
|
|
143
|
+
authorization_code: z.ZodMiniOptional<z.ZodMiniString<string>>;
|
|
144
|
+
}, z.core.$strip>;
|
|
145
|
+
|
|
146
|
+
export declare type TokenInfo = z.infer<typeof TokenInfoSchema>;
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Token info schema (parsed from JWT)
|
|
150
|
+
* Standard OIDC claims
|
|
151
|
+
*/
|
|
152
|
+
export declare const TokenInfoSchema: z.ZodMiniObject<{
|
|
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>>;
|
|
160
|
+
}, z.core.$strip>;
|
|
161
|
+
|
|
162
|
+
export declare type TokenResponse = z.infer<typeof TokenResponseSchema>;
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Token exchange response schema (OAuth2 standard)
|
|
166
|
+
* POST /oauth/token
|
|
167
|
+
*/
|
|
168
|
+
export declare const TokenResponseSchema: z.ZodMiniObject<{
|
|
169
|
+
access_token: z.ZodMiniString<string>;
|
|
170
|
+
token_type: z.ZodMiniString<string>;
|
|
171
|
+
expires_in: z.ZodMiniNumber<number>;
|
|
172
|
+
id_token: z.ZodMiniOptional<z.ZodMiniString<string>>;
|
|
173
|
+
refresh_token: z.ZodMiniString<string>;
|
|
174
|
+
}, z.core.$strip>;
|
|
175
|
+
|
|
176
|
+
export declare type UserInfoResponse = z.infer<typeof UserInfoResponseSchema>;
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* UserInfo response schema
|
|
180
|
+
* GET /oauth/userinfo
|
|
181
|
+
*/
|
|
182
|
+
export declare const UserInfoResponseSchema: z.ZodMiniObject<{
|
|
183
|
+
sub: z.ZodMiniString<string>;
|
|
184
|
+
}, z.core.$strip>;
|
|
185
|
+
|
|
186
|
+
export { }
|
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
var h = (u, e, o) => new Promise((r, s) => {
|
|
2
|
+
var a = (l) => {
|
|
3
|
+
try {
|
|
4
|
+
c(o.next(l));
|
|
5
|
+
} catch (m) {
|
|
6
|
+
s(m);
|
|
7
|
+
}
|
|
8
|
+
}, n = (l) => {
|
|
9
|
+
try {
|
|
10
|
+
c(o.throw(l));
|
|
11
|
+
} catch (m) {
|
|
12
|
+
s(m);
|
|
13
|
+
}
|
|
14
|
+
}, c = (l) => l.done ? r(l.value) : Promise.resolve(l.value).then(a, n);
|
|
15
|
+
c((o = o.apply(u, e)).next());
|
|
16
|
+
});
|
|
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.optional(t.string()),
|
|
33
|
+
// Optional - not returned on refresh_token grant
|
|
34
|
+
refresh_token: t.string()
|
|
35
|
+
}), x = t.object({
|
|
36
|
+
sub: t.string()
|
|
37
|
+
}), j = t.object({
|
|
38
|
+
iss: t.string(),
|
|
39
|
+
sub: t.string(),
|
|
40
|
+
aud: t.union([t.string(), t.array(t.string())]),
|
|
41
|
+
exp: t.number(),
|
|
42
|
+
iat: t.number(),
|
|
43
|
+
nonce: t.optional(t.string()),
|
|
44
|
+
auth_time: t.optional(t.number())
|
|
45
|
+
}), z = k;
|
|
46
|
+
function _(u) {
|
|
47
|
+
return btoa(u).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
|
48
|
+
}
|
|
49
|
+
function w(u) {
|
|
50
|
+
let e = u.replace(/-/g, "+").replace(/_/g, "/");
|
|
51
|
+
for (; e.length % 4; )
|
|
52
|
+
e += "=";
|
|
53
|
+
return atob(e);
|
|
54
|
+
}
|
|
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({
|
|
56
|
+
ssoBaseUrl: t.url(),
|
|
57
|
+
providerAddress: t.string(),
|
|
58
|
+
pollingInterval: t.optional(t.number())
|
|
59
|
+
}), d = class d {
|
|
60
|
+
constructor(e) {
|
|
61
|
+
this.config = U.parse(e), this.ssoBaseUrl = this.config.ssoBaseUrl || E, this.providerAddress = this.config.providerAddress, this.pollingInterval = this.config.pollingInterval || v;
|
|
62
|
+
}
|
|
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);
|
|
68
|
+
else {
|
|
69
|
+
o = new Uint8Array(e);
|
|
70
|
+
for (let a = 0; a < e; a++)
|
|
71
|
+
o[a] = Math.floor(Math.random() * 256);
|
|
72
|
+
}
|
|
73
|
+
let s = "";
|
|
74
|
+
for (let a = 0; a < o.length; a++)
|
|
75
|
+
s += String.fromCharCode(o[a]);
|
|
76
|
+
return _(s);
|
|
77
|
+
}
|
|
78
|
+
generateCodeChallenge(e) {
|
|
79
|
+
const o = y.array(e), r = String.fromCharCode(...o);
|
|
80
|
+
return _(r);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Initiates OAuth2 authorization flow with response_mode=json for SPA
|
|
84
|
+
* GET /oauth/authorize?response_type=code&response_mode=json&...
|
|
85
|
+
*/
|
|
86
|
+
generateDeeplink() {
|
|
87
|
+
return h(this, null, function* () {
|
|
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,
|
|
96
|
+
code_challenge_method: "S256"
|
|
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 T.parse(n);
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Polls for authorization completion
|
|
110
|
+
* POST /oauth/poll
|
|
111
|
+
*/
|
|
112
|
+
pollAuth(e) {
|
|
113
|
+
return h(this, null, function* () {
|
|
114
|
+
const o = {
|
|
115
|
+
polling_code: e
|
|
116
|
+
};
|
|
117
|
+
b.parse(o);
|
|
118
|
+
const r = yield fetch(f(this.config.ssoBaseUrl, "/oauth/poll"), {
|
|
119
|
+
method: "POST",
|
|
120
|
+
headers: {
|
|
121
|
+
"Content-Type": "application/json"
|
|
122
|
+
},
|
|
123
|
+
body: JSON.stringify(o)
|
|
124
|
+
});
|
|
125
|
+
if (!r.ok)
|
|
126
|
+
throw new Error(`Poll failed: ${r.statusText}`);
|
|
127
|
+
const s = yield r.json();
|
|
128
|
+
return R.parse(s);
|
|
129
|
+
});
|
|
130
|
+
}
|
|
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) {
|
|
137
|
+
return h(this, null, function* () {
|
|
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
|
+
f(this.config.ssoBaseUrl, "/oauth/token"),
|
|
147
|
+
{
|
|
148
|
+
method: "POST",
|
|
149
|
+
headers: {
|
|
150
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
151
|
+
},
|
|
152
|
+
body: r.toString()
|
|
153
|
+
}
|
|
154
|
+
);
|
|
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 = 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
|
+
const c = Date.now() + n.expires_in * 1e3;
|
|
162
|
+
return localStorage.setItem(p, c.toString()), sessionStorage.removeItem(i + "code_verifier"), n;
|
|
163
|
+
});
|
|
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
|
+
*/
|
|
170
|
+
verifyAuth() {
|
|
171
|
+
return h(this, null, function* () {
|
|
172
|
+
return this.withAutoRefresh(() => h(this, null, function* () {
|
|
173
|
+
const e = this.getAccessToken();
|
|
174
|
+
if (!e)
|
|
175
|
+
return null;
|
|
176
|
+
const o = yield fetch(
|
|
177
|
+
f(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;
|
|
191
|
+
}
|
|
192
|
+
const r = yield o.json();
|
|
193
|
+
return x.parse(r);
|
|
194
|
+
}));
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Gets stored access token
|
|
199
|
+
*/
|
|
200
|
+
getAccessToken() {
|
|
201
|
+
return localStorage.getItem(i + "access_token");
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Gets stored ID token
|
|
205
|
+
*/
|
|
206
|
+
getIdToken() {
|
|
207
|
+
return localStorage.getItem(i + "id_token");
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Decodes and validates JWT token to extract claims
|
|
211
|
+
* Works with both access_token and id_token (EdDSA signed)
|
|
212
|
+
*/
|
|
213
|
+
getAuthData() {
|
|
214
|
+
const e = this.getIdToken() || this.getAccessToken();
|
|
215
|
+
if (!e) return null;
|
|
216
|
+
const o = e.split(".");
|
|
217
|
+
if (o.length !== 3)
|
|
218
|
+
return null;
|
|
219
|
+
let r;
|
|
220
|
+
try {
|
|
221
|
+
const n = w(o[0]);
|
|
222
|
+
r = JSON.parse(n);
|
|
223
|
+
} catch (n) {
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
226
|
+
if (r.alg !== "RS256" || r.typ !== "JWT")
|
|
227
|
+
return null;
|
|
228
|
+
let s;
|
|
229
|
+
try {
|
|
230
|
+
const n = JSON.parse(w(o[1]));
|
|
231
|
+
s = j.parse(n);
|
|
232
|
+
} catch (n) {
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
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;
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Clears all stored authentication data
|
|
253
|
+
*/
|
|
254
|
+
logout() {
|
|
255
|
+
localStorage.removeItem(i + "access_token"), localStorage.removeItem(i + "id_token"), localStorage.removeItem(g), localStorage.removeItem(p), sessionStorage.removeItem(i + "code_verifier");
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Gets stored refresh token
|
|
259
|
+
*/
|
|
260
|
+
getRefreshToken() {
|
|
261
|
+
return localStorage.getItem(g);
|
|
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(p);
|
|
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
|
+
* Uses singleton pattern to prevent concurrent refresh requests (race condition)
|
|
282
|
+
*/
|
|
283
|
+
refreshAccessToken() {
|
|
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* () {
|
|
295
|
+
const e = this.getRefreshToken();
|
|
296
|
+
if (!e)
|
|
297
|
+
throw new Error("No refresh token available");
|
|
298
|
+
const o = new URLSearchParams({
|
|
299
|
+
grant_type: "refresh_token",
|
|
300
|
+
refresh_token: e,
|
|
301
|
+
client_id: this.providerAddress
|
|
302
|
+
}), r = yield fetch(
|
|
303
|
+
f(this.config.ssoBaseUrl, "/oauth/token"),
|
|
304
|
+
{
|
|
305
|
+
method: "POST",
|
|
306
|
+
headers: {
|
|
307
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
308
|
+
},
|
|
309
|
+
body: o.toString()
|
|
310
|
+
}
|
|
311
|
+
);
|
|
312
|
+
if (!r.ok) {
|
|
313
|
+
const c = yield r.json().catch(() => ({ error: r.statusText }));
|
|
314
|
+
throw this.logout(), new Error(`Token refresh failed: ${c.error_description || c.error || r.statusText}`);
|
|
315
|
+
}
|
|
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);
|
|
318
|
+
const n = Date.now() + a.expires_in * 1e3;
|
|
319
|
+
return localStorage.setItem(p, n.toString()), a;
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Executes a function that makes an authenticated request
|
|
324
|
+
* Automatically refreshes token and retries on 401 error
|
|
325
|
+
*/
|
|
326
|
+
withAutoRefresh(e, o = 1) {
|
|
327
|
+
return h(this, null, function* () {
|
|
328
|
+
var r, s, a;
|
|
329
|
+
try {
|
|
330
|
+
return yield e();
|
|
331
|
+
} catch (n) {
|
|
332
|
+
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())
|
|
333
|
+
try {
|
|
334
|
+
return yield this.refreshAccessToken(), yield e();
|
|
335
|
+
} catch (l) {
|
|
336
|
+
throw n;
|
|
337
|
+
}
|
|
338
|
+
throw n;
|
|
339
|
+
}
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
};
|
|
343
|
+
d.refreshPromise = null;
|
|
344
|
+
let S = d;
|
|
345
|
+
export {
|
|
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
|
|
355
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
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"})}));
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@alien-id/sso",
|
|
3
|
+
"version": "1.0.25",
|
|
4
|
+
"repository": {
|
|
5
|
+
"type": "git",
|
|
6
|
+
"url": "git+https://github.com/alien-id/sso-sdk-js.git"
|
|
7
|
+
},
|
|
8
|
+
"main": "./dist/index.cjs",
|
|
9
|
+
"module": "./dist/index.esm.js",
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"import": "./dist/index.esm.js",
|
|
15
|
+
"require": "./dist/index.cjs"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist",
|
|
20
|
+
"README.md"
|
|
21
|
+
],
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "vite build",
|
|
24
|
+
"dev": "vite build --watch",
|
|
25
|
+
"test": "jest",
|
|
26
|
+
"test:unit": "jest tests/unit",
|
|
27
|
+
"test:integration": "jest tests/integration",
|
|
28
|
+
"test:watch": "jest --watch",
|
|
29
|
+
"release": "npm publish --access public",
|
|
30
|
+
"prepublishOnly": "npm run build"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/express": "^5.0.3",
|
|
34
|
+
"@types/jest": "^30.0.0",
|
|
35
|
+
"@types/node": "^24.3.0",
|
|
36
|
+
"@typescript-eslint/eslint-plugin": "^8.41.0",
|
|
37
|
+
"@typescript-eslint/parser": "^8.41.0",
|
|
38
|
+
"cross-fetch": "^4.1.0",
|
|
39
|
+
"eslint": "^9.34.0",
|
|
40
|
+
"eslint-config-prettier": "^10.1.8",
|
|
41
|
+
"eslint-plugin-prettier": "^5.5.4",
|
|
42
|
+
"express": "^5.1.0",
|
|
43
|
+
"jest": "^30.1.2",
|
|
44
|
+
"jest-environment-jsdom": "^30.1.2",
|
|
45
|
+
"nock": "^14.0.10",
|
|
46
|
+
"prettier": "^3.6.2",
|
|
47
|
+
"ts-jest": "^29.4.1",
|
|
48
|
+
"turbo": "^2.5.6",
|
|
49
|
+
"vite": "^7.1.12",
|
|
50
|
+
"vite-plugin-dts": "^4.5.4"
|
|
51
|
+
},
|
|
52
|
+
"dependencies": {
|
|
53
|
+
"js-sha256": "^0.11.1",
|
|
54
|
+
"zod": "^4.1.5"
|
|
55
|
+
}
|
|
56
|
+
}
|