@gh-platform/auth-sdk 1.0.7 → 1.0.9

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.
@@ -15,7 +15,6 @@ class AuthClient {
15
15
  */
16
16
  constructor({
17
17
  baseUrl,
18
- introspectBaseUrl,
19
18
  tenant = null,
20
19
  loginPath = null,
21
20
  refreshPath = null,
@@ -24,13 +23,9 @@ class AuthClient {
24
23
  }) {
25
24
  if (!baseUrl) throw new Error("baseUrl is required");
26
25
  this.baseUrl = baseUrl.replace(/\/$/, "");
27
- if (!introspectBaseUrl) {
28
- throw new Error("introspectBaseUrl is required");
29
- }
30
26
  const prefix = tenant ? `/api/v1/${tenant}/auth` : `/api/v1/auth`;
31
27
  this.loginUrl = this.baseUrl + (loginPath || `${prefix}/login`);
32
28
  this.refreshUrl = this.baseUrl + (refreshPath || `${prefix}/refresh`);
33
- this.introspectUrl = `${introspectBaseUrl}/introspect`;
34
29
  this.tenant = tenant;
35
30
  this.headers = { "Content-Type": "application/json", ...headers };
36
31
  this.storage = storage;
@@ -104,36 +99,7 @@ class AuthClient {
104
99
  setRefreshPromise(null);
105
100
  }
106
101
  }
107
- async introspect(token = null) {
108
- let finalToken = token;
109
- if (this.storage && !finalToken) {
110
- finalToken = this.storage.accessToken;
111
- }
112
- if (!finalToken) {
113
- throw new Error("No access token available for introspection");
114
- }
115
- if (!this.introspectUrl) {
116
- throw new Error("No introspect url config");
117
- }
118
- const headers = new Headers(this.headers);
119
- headers.set("Authorization", `Bearer ${finalToken}`);
120
- const res = await fetch(this.introspectUrl, {
121
- method: "GET",
122
- headers
123
- });
124
- if (!res.ok) {
125
- const text = await res.text().catch(() => res.statusText);
126
- throw new Error(`Introspect failed: ${res.status} ${text}`);
127
- }
128
- let json;
129
- try {
130
- json = await res.json();
131
- } catch (e) {
132
- console.error("❌ JSON parse error:", e);
133
- throw new Error("Invalid JSON response from server");
134
- }
135
- return json;
136
- }
102
+ // src/client.js
137
103
  }
138
104
  class TokenStorage {
139
105
  /**
@@ -173,8 +139,12 @@ class TokenStorage {
173
139
  }
174
140
  }
175
141
  class AuthFetch {
176
- constructor(authClient, storage = null) {
142
+ constructor(authClient, introspectBaseUrl, storage = null, introspectPath = "introspect") {
177
143
  this.client = authClient;
144
+ if (!introspectBaseUrl) {
145
+ throw new Error("introspectBaseUrl is required");
146
+ }
147
+ this.introspectUrl = `${introspectBaseUrl.replace(/^\//, "")}/${introspectPath.replace(/^\//, "")}`;
178
148
  this.storage = storage || new TokenStorage("auth", authClient.tenant || null);
179
149
  }
180
150
  /**
@@ -252,6 +222,33 @@ class AuthFetch {
252
222
  xhr.send(options.body);
253
223
  });
254
224
  }
225
+ async introspect(token = null) {
226
+ let finalToken = token;
227
+ if (this.storage && !finalToken) {
228
+ finalToken = this.storage.accessToken;
229
+ }
230
+ if (!finalToken) {
231
+ throw new Error("No access token available for introspection");
232
+ }
233
+ if (!this.introspectUrl) {
234
+ throw new Error("No introspect url config");
235
+ }
236
+ const res = await this.fetch(this.introspectUrl, {
237
+ method: "GET"
238
+ });
239
+ if (!res.ok) {
240
+ const text = await res.text().catch(() => res.statusText);
241
+ throw new Error(`Introspect failed: ${res.status} ${text}`);
242
+ }
243
+ let json;
244
+ try {
245
+ json = await res.json();
246
+ } catch (e) {
247
+ console.error("❌ JSON parse error:", e);
248
+ throw new Error("Invalid JSON response from server");
249
+ }
250
+ return json;
251
+ }
255
252
  }
256
253
  const index = { AuthClient, AuthFetch, TokenStorage };
257
254
  export {
@@ -1 +1 @@
1
- !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).AuthSDK={})}(this,function(e){"use strict";let t=null;function r(){return t}function s(e){t=e}class o{constructor({baseUrl:e,introspectBaseUrl:t,tenant:r=null,loginPath:s=null,refreshPath:o=null,headers:n={},storage:a=null}){if(!e)throw new Error("baseUrl is required");if(this.baseUrl=e.replace(/\/$/,""),!t)throw new Error("introspectBaseUrl is required");const i=r?`/api/v1/${r}/auth`:"/api/v1/auth";this.loginUrl=this.baseUrl+(s||`${i}/login`),this.refreshUrl=this.baseUrl+(o||`${i}/refresh`),this.introspectUrl=`${t}/introspect`,this.tenant=r,this.headers={"Content-Type":"application/json",...n},this.storage=a}async login(e,t,r=null,s={}){const o={identifier:e,password:t,...s};r&&(o.totp=r);const n=await fetch(this.loginUrl,{method:"POST",headers:this.headers,body:JSON.stringify(o)});if(!n.ok){const e=await n.text().catch(()=>n.statusText);throw new Error(`Login failed: ${n.status} ${e}`)}let a;try{a=await n.json()}catch(e){throw console.error("❌ JSON parse error:",e),new Error("Invalid JSON response from server")}const i=a.data||a;return this.storage&&(i.access_token&&(this.storage.accessToken=i.access_token),i.refresh_token&&(this.storage.refreshToken=i.refresh_token)),a}async refresh(e){if(r())return r();const t=(async()=>{const t=await fetch(this.refreshUrl,{method:"POST",headers:this.headers,body:JSON.stringify({refresh_token:e})});if(!t.ok){const e=await t.text().catch(()=>t.statusText);throw new Error(`Refresh failed: ${t.status} ${e}`)}let r;try{r=await t.json()}catch(e){throw console.error("❌ JSON parse error:",e),new Error("Invalid JSON response from server")}const s=r.data||r;return this.storage&&(s.access_token&&(this.storage.accessToken=s.access_token),s.refresh_token&&(this.storage.refreshToken=s.refresh_token)),r})();s(t);try{return await t}finally{s(null)}}async introspect(e=null){let t=e;if(this.storage&&!t&&(t=this.storage.accessToken),!t)throw new Error("No access token available for introspection");if(!this.introspectUrl)throw new Error("No introspect url config");const r=new Headers(this.headers);r.set("Authorization",`Bearer ${t}`);const s=await fetch(this.introspectUrl,{method:"GET",headers:r});if(!s.ok){const e=await s.text().catch(()=>s.statusText);throw new Error(`Introspect failed: ${s.status} ${e}`)}let o;try{o=await s.json()}catch(e){throw console.error("❌ JSON parse error:",e),new Error("Invalid JSON response from server")}return o}}class n{constructor(e="auth",t=null){this.prefix=e,this.tenant=t}_key(e){return this.tenant?`${this.prefix}:${this.tenant}_${e}`:`${this.prefix}_${e}`}get accessToken(){return localStorage.getItem(this._key("access_token"))}set accessToken(e){null==e?localStorage.removeItem(this._key("access_token")):localStorage.setItem(this._key("access_token"),e)}get refreshToken(){return localStorage.getItem(this._key("refresh_token"))}set refreshToken(e){null==e?localStorage.removeItem(this._key("refresh_token")):localStorage.setItem(this._key("refresh_token"),e)}clear(){localStorage.removeItem(this._key("access_token")),localStorage.removeItem(this._key("refresh_token"))}}class a{constructor(e,t=null){this.client=e,this.storage=t||new n("auth",e.tenant||null)}async fetch(e,t={},r=null){const s=this.storage.accessToken,o=new Headers(t.headers||{});return s&&o.set("Authorization",`Bearer ${s}`),t.method&&"GET"!==t.method&&t.body?await this._xhrRequest(e,t,o,r):await this._fetchWithDownloadProgress(e,t,o,r)}async _fetchWithDownloadProgress(e,t,r,s){let o=await fetch(e,{...t,headers:r});if(401===o.status&&this.storage.refreshToken)try{const s=await this.client.refresh(this.storage.refreshToken);s.access_token&&(this.storage.accessToken=s.access_token),s.refresh_token&&(this.storage.refreshToken=s.refresh_token),r.set("Authorization",`Bearer ${this.storage.accessToken}`),o=await fetch(e,{...t,headers:r})}catch{throw this.storage.clear(),new Error("Unauthorized, please login again")}if(!s||!o.body)return o;const n=o.body.getReader(),a=+o.headers.get("Content-Length")||0;let i=0;const h=[];for(;;){const{done:e,value:t}=await n.read();if(e)break;h.push(t),i+=t.length,a?s(Math.round(i/a*100),i,a):s(null,i,null)}const c=new Blob(h);return new Response(c,o)}_xhrRequest(e,t,r,s){return new Promise((o,n)=>{const a=new XMLHttpRequest;a.open(t.method||"POST",e,!0);for(const[e,t]of r.entries())a.setRequestHeader(e,t);a.upload&&s&&(a.upload.onprogress=e=>{if(e.lengthComputable){const t=Math.round(e.loaded/e.total*100);s(t,e.loaded,e.total)}else s(null,e.loaded,null)}),a.onload=()=>{o(new Response(a.response,{status:a.status}))},a.onerror=()=>n(new Error("Network error")),a.send(t.body)})}}const i={AuthClient:o,AuthFetch:a,TokenStorage:n};e.AuthClient=o,e.AuthFetch=a,e.TokenStorage=n,e.default=i,Object.defineProperties(e,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});
1
+ !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).AuthSDK={})}(this,function(e){"use strict";let t=null;function r(){return t}function s(e){t=e}class o{constructor({baseUrl:e,tenant:t=null,loginPath:r=null,refreshPath:s=null,headers:o={},storage:n=null}){if(!e)throw new Error("baseUrl is required");this.baseUrl=e.replace(/\/$/,"");const a=t?`/api/v1/${t}/auth`:"/api/v1/auth";this.loginUrl=this.baseUrl+(r||`${a}/login`),this.refreshUrl=this.baseUrl+(s||`${a}/refresh`),this.tenant=t,this.headers={"Content-Type":"application/json",...o},this.storage=n}async login(e,t,r=null,s={}){const o={identifier:e,password:t,...s};r&&(o.totp=r);const n=await fetch(this.loginUrl,{method:"POST",headers:this.headers,body:JSON.stringify(o)});if(!n.ok){const e=await n.text().catch(()=>n.statusText);throw new Error(`Login failed: ${n.status} ${e}`)}let a;try{a=await n.json()}catch(e){throw console.error("❌ JSON parse error:",e),new Error("Invalid JSON response from server")}const i=a.data||a;return this.storage&&(i.access_token&&(this.storage.accessToken=i.access_token),i.refresh_token&&(this.storage.refreshToken=i.refresh_token)),a}async refresh(e){if(r())return r();const t=(async()=>{const t=await fetch(this.refreshUrl,{method:"POST",headers:this.headers,body:JSON.stringify({refresh_token:e})});if(!t.ok){const e=await t.text().catch(()=>t.statusText);throw new Error(`Refresh failed: ${t.status} ${e}`)}let r;try{r=await t.json()}catch(e){throw console.error("❌ JSON parse error:",e),new Error("Invalid JSON response from server")}const s=r.data||r;return this.storage&&(s.access_token&&(this.storage.accessToken=s.access_token),s.refresh_token&&(this.storage.refreshToken=s.refresh_token)),r})();s(t);try{return await t}finally{s(null)}}}class n{constructor(e="auth",t=null){this.prefix=e,this.tenant=t}_key(e){return this.tenant?`${this.prefix}:${this.tenant}_${e}`:`${this.prefix}_${e}`}get accessToken(){return localStorage.getItem(this._key("access_token"))}set accessToken(e){null==e?localStorage.removeItem(this._key("access_token")):localStorage.setItem(this._key("access_token"),e)}get refreshToken(){return localStorage.getItem(this._key("refresh_token"))}set refreshToken(e){null==e?localStorage.removeItem(this._key("refresh_token")):localStorage.setItem(this._key("refresh_token"),e)}clear(){localStorage.removeItem(this._key("access_token")),localStorage.removeItem(this._key("refresh_token"))}}class a{constructor(e,t,r=null,s="introspect"){if(this.client=e,!t)throw new Error("introspectBaseUrl is required");this.introspectUrl=`${t.replace(/^\//,"")}/${s.replace(/^\//,"")}`,this.storage=r||new n("auth",e.tenant||null)}async fetch(e,t={},r=null){const s=this.storage.accessToken,o=new Headers(t.headers||{});return s&&o.set("Authorization",`Bearer ${s}`),t.method&&"GET"!==t.method&&t.body?await this._xhrRequest(e,t,o,r):await this._fetchWithDownloadProgress(e,t,o,r)}async _fetchWithDownloadProgress(e,t,r,s){let o=await fetch(e,{...t,headers:r});if(401===o.status&&this.storage.refreshToken)try{const s=await this.client.refresh(this.storage.refreshToken);s.access_token&&(this.storage.accessToken=s.access_token),s.refresh_token&&(this.storage.refreshToken=s.refresh_token),r.set("Authorization",`Bearer ${this.storage.accessToken}`),o=await fetch(e,{...t,headers:r})}catch{throw this.storage.clear(),new Error("Unauthorized, please login again")}if(!s||!o.body)return o;const n=o.body.getReader(),a=+o.headers.get("Content-Length")||0;let i=0;const h=[];for(;;){const{done:e,value:t}=await n.read();if(e)break;h.push(t),i+=t.length,a?s(Math.round(i/a*100),i,a):s(null,i,null)}const c=new Blob(h);return new Response(c,o)}_xhrRequest(e,t,r,s){return new Promise((o,n)=>{const a=new XMLHttpRequest;a.open(t.method||"POST",e,!0);for(const[e,t]of r.entries())a.setRequestHeader(e,t);a.upload&&s&&(a.upload.onprogress=e=>{if(e.lengthComputable){const t=Math.round(e.loaded/e.total*100);s(t,e.loaded,e.total)}else s(null,e.loaded,null)}),a.onload=()=>{o(new Response(a.response,{status:a.status}))},a.onerror=()=>n(new Error("Network error")),a.send(t.body)})}async introspect(e=null){let t=e;if(this.storage&&!t&&(t=this.storage.accessToken),!t)throw new Error("No access token available for introspection");if(!this.introspectUrl)throw new Error("No introspect url config");const r=await this.fetch(this.introspectUrl,{method:"GET"});if(!r.ok){const e=await r.text().catch(()=>r.statusText);throw new Error(`Introspect failed: ${r.status} ${e}`)}let s;try{s=await r.json()}catch(e){throw console.error("❌ JSON parse error:",e),new Error("Invalid JSON response from server")}return s}}const i={AuthClient:o,AuthFetch:a,TokenStorage:n};e.AuthClient=o,e.AuthFetch=a,e.TokenStorage=n,e.default=i,Object.defineProperties(e,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});
@@ -1 +1 @@
1
- !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).AuthSDK={})}(this,function(e){"use strict";let t=null;function r(){return t}function s(e){t=e}class o{constructor({baseUrl:e,introspectBaseUrl:t,tenant:r=null,loginPath:s=null,refreshPath:o=null,headers:n={},storage:a=null}){if(!e)throw new Error("baseUrl is required");if(this.baseUrl=e.replace(/\/$/,""),!t)throw new Error("introspectBaseUrl is required");const i=r?`/api/v1/${r}/auth`:"/api/v1/auth";this.loginUrl=this.baseUrl+(s||`${i}/login`),this.refreshUrl=this.baseUrl+(o||`${i}/refresh`),this.introspectUrl=`${t}/introspect`,this.tenant=r,this.headers={"Content-Type":"application/json",...n},this.storage=a}async login(e,t,r=null,s={}){const o={identifier:e,password:t,...s};r&&(o.totp=r);const n=await fetch(this.loginUrl,{method:"POST",headers:this.headers,body:JSON.stringify(o)});if(!n.ok){const e=await n.text().catch(()=>n.statusText);throw new Error(`Login failed: ${n.status} ${e}`)}let a;try{a=await n.json()}catch(h){throw console.error("❌ JSON parse error:",h),new Error("Invalid JSON response from server")}const i=a.data||a;return this.storage&&(i.access_token&&(this.storage.accessToken=i.access_token),i.refresh_token&&(this.storage.refreshToken=i.refresh_token)),a}async refresh(e){if(r())return r();const t=(async()=>{const t=await fetch(this.refreshUrl,{method:"POST",headers:this.headers,body:JSON.stringify({refresh_token:e})});if(!t.ok){const e=await t.text().catch(()=>t.statusText);throw new Error(`Refresh failed: ${t.status} ${e}`)}let r;try{r=await t.json()}catch(o){throw console.error("❌ JSON parse error:",o),new Error("Invalid JSON response from server")}const s=r.data||r;return this.storage&&(s.access_token&&(this.storage.accessToken=s.access_token),s.refresh_token&&(this.storage.refreshToken=s.refresh_token)),r})();s(t);try{return await t}finally{s(null)}}async introspect(e=null){let t=e;if(this.storage&&!t&&(t=this.storage.accessToken),!t)throw new Error("No access token available for introspection");if(!this.introspectUrl)throw new Error("No introspect url config");const r=new Headers(this.headers);r.set("Authorization",`Bearer ${t}`);const s=await fetch(this.introspectUrl,{method:"GET",headers:r});if(!s.ok){const e=await s.text().catch(()=>s.statusText);throw new Error(`Introspect failed: ${s.status} ${e}`)}let o;try{o=await s.json()}catch(n){throw console.error("❌ JSON parse error:",n),new Error("Invalid JSON response from server")}return o}}class n{constructor(e="auth",t=null){this.prefix=e,this.tenant=t}_key(e){return this.tenant?`${this.prefix}:${this.tenant}_${e}`:`${this.prefix}_${e}`}get accessToken(){return localStorage.getItem(this._key("access_token"))}set accessToken(e){null==e?localStorage.removeItem(this._key("access_token")):localStorage.setItem(this._key("access_token"),e)}get refreshToken(){return localStorage.getItem(this._key("refresh_token"))}set refreshToken(e){null==e?localStorage.removeItem(this._key("refresh_token")):localStorage.setItem(this._key("refresh_token"),e)}clear(){localStorage.removeItem(this._key("access_token")),localStorage.removeItem(this._key("refresh_token"))}}class a{constructor(e,t=null){this.client=e,this.storage=t||new n("auth",e.tenant||null)}async fetch(e,t={},r=null){const s=this.storage.accessToken,o=new Headers(t.headers||{});return s&&o.set("Authorization",`Bearer ${s}`),t.method&&"GET"!==t.method&&t.body?await this._xhrRequest(e,t,o,r):await this._fetchWithDownloadProgress(e,t,o,r)}async _fetchWithDownloadProgress(e,t,r,s){let o=await fetch(e,{...t,headers:r});if(401===o.status&&this.storage.refreshToken)try{const s=await this.client.refresh(this.storage.refreshToken);s.access_token&&(this.storage.accessToken=s.access_token),s.refresh_token&&(this.storage.refreshToken=s.refresh_token),r.set("Authorization",`Bearer ${this.storage.accessToken}`),o=await fetch(e,{...t,headers:r})}catch{throw this.storage.clear(),new Error("Unauthorized, please login again")}if(!s||!o.body)return o;const n=o.body.getReader(),a=+o.headers.get("Content-Length")||0;let i=0;const h=[];for(;;){const{done:e,value:t}=await n.read();if(e)break;if(h.push(t),i+=t.length,a){s(Math.round(i/a*100),i,a)}else s(null,i,null)}const c=new Blob(h);return new Response(c,o)}_xhrRequest(e,t,r,s){return new Promise((o,n)=>{const a=new XMLHttpRequest;a.open(t.method||"POST",e,!0);for(const[e,t]of r.entries())a.setRequestHeader(e,t);a.upload&&s&&(a.upload.onprogress=e=>{if(e.lengthComputable){const t=Math.round(e.loaded/e.total*100);s(t,e.loaded,e.total)}else s(null,e.loaded,null)}),a.onload=()=>{o(new Response(a.response,{status:a.status}))},a.onerror=()=>n(new Error("Network error")),a.send(t.body)})}}const i={AuthClient:o,AuthFetch:a,TokenStorage:n};e.AuthClient=o,e.AuthFetch=a,e.TokenStorage=n,e.default=i,Object.defineProperties(e,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});
1
+ !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).AuthSDK={})}(this,function(e){"use strict";let t=null;function r(){return t}function s(e){t=e}class o{constructor({baseUrl:e,tenant:t=null,loginPath:r=null,refreshPath:s=null,headers:o={},storage:n=null}){if(!e)throw new Error("baseUrl is required");this.baseUrl=e.replace(/\/$/,"");const a=t?`/api/v1/${t}/auth`:"/api/v1/auth";this.loginUrl=this.baseUrl+(r||`${a}/login`),this.refreshUrl=this.baseUrl+(s||`${a}/refresh`),this.tenant=t,this.headers={"Content-Type":"application/json",...o},this.storage=n}async login(e,t,r=null,s={}){const o={identifier:e,password:t,...s};r&&(o.totp=r);const n=await fetch(this.loginUrl,{method:"POST",headers:this.headers,body:JSON.stringify(o)});if(!n.ok){const e=await n.text().catch(()=>n.statusText);throw new Error(`Login failed: ${n.status} ${e}`)}let a;try{a=await n.json()}catch(h){throw console.error("❌ JSON parse error:",h),new Error("Invalid JSON response from server")}const i=a.data||a;return this.storage&&(i.access_token&&(this.storage.accessToken=i.access_token),i.refresh_token&&(this.storage.refreshToken=i.refresh_token)),a}async refresh(e){if(r())return r();const t=(async()=>{const t=await fetch(this.refreshUrl,{method:"POST",headers:this.headers,body:JSON.stringify({refresh_token:e})});if(!t.ok){const e=await t.text().catch(()=>t.statusText);throw new Error(`Refresh failed: ${t.status} ${e}`)}let r;try{r=await t.json()}catch(o){throw console.error("❌ JSON parse error:",o),new Error("Invalid JSON response from server")}const s=r.data||r;return this.storage&&(s.access_token&&(this.storage.accessToken=s.access_token),s.refresh_token&&(this.storage.refreshToken=s.refresh_token)),r})();s(t);try{return await t}finally{s(null)}}}class n{constructor(e="auth",t=null){this.prefix=e,this.tenant=t}_key(e){return this.tenant?`${this.prefix}:${this.tenant}_${e}`:`${this.prefix}_${e}`}get accessToken(){return localStorage.getItem(this._key("access_token"))}set accessToken(e){null==e?localStorage.removeItem(this._key("access_token")):localStorage.setItem(this._key("access_token"),e)}get refreshToken(){return localStorage.getItem(this._key("refresh_token"))}set refreshToken(e){null==e?localStorage.removeItem(this._key("refresh_token")):localStorage.setItem(this._key("refresh_token"),e)}clear(){localStorage.removeItem(this._key("access_token")),localStorage.removeItem(this._key("refresh_token"))}}class a{constructor(e,t,r=null,s="introspect"){if(this.client=e,!t)throw new Error("introspectBaseUrl is required");this.introspectUrl=`${t.replace(/^\//,"")}/${s.replace(/^\//,"")}`,this.storage=r||new n("auth",e.tenant||null)}async fetch(e,t={},r=null){const s=this.storage.accessToken,o=new Headers(t.headers||{});return s&&o.set("Authorization",`Bearer ${s}`),t.method&&"GET"!==t.method&&t.body?await this._xhrRequest(e,t,o,r):await this._fetchWithDownloadProgress(e,t,o,r)}async _fetchWithDownloadProgress(e,t,r,s){let o=await fetch(e,{...t,headers:r});if(401===o.status&&this.storage.refreshToken)try{const s=await this.client.refresh(this.storage.refreshToken);s.access_token&&(this.storage.accessToken=s.access_token),s.refresh_token&&(this.storage.refreshToken=s.refresh_token),r.set("Authorization",`Bearer ${this.storage.accessToken}`),o=await fetch(e,{...t,headers:r})}catch{throw this.storage.clear(),new Error("Unauthorized, please login again")}if(!s||!o.body)return o;const n=o.body.getReader(),a=+o.headers.get("Content-Length")||0;let i=0;const h=[];for(;;){const{done:e,value:t}=await n.read();if(e)break;if(h.push(t),i+=t.length,a){s(Math.round(i/a*100),i,a)}else s(null,i,null)}const c=new Blob(h);return new Response(c,o)}_xhrRequest(e,t,r,s){return new Promise((o,n)=>{const a=new XMLHttpRequest;a.open(t.method||"POST",e,!0);for(const[e,t]of r.entries())a.setRequestHeader(e,t);a.upload&&s&&(a.upload.onprogress=e=>{if(e.lengthComputable){const t=Math.round(e.loaded/e.total*100);s(t,e.loaded,e.total)}else s(null,e.loaded,null)}),a.onload=()=>{o(new Response(a.response,{status:a.status}))},a.onerror=()=>n(new Error("Network error")),a.send(t.body)})}async introspect(e=null){let t=e;if(this.storage&&!t&&(t=this.storage.accessToken),!t)throw new Error("No access token available for introspection");if(!this.introspectUrl)throw new Error("No introspect url config");const r=await this.fetch(this.introspectUrl,{method:"GET"});if(!r.ok){const e=await r.text().catch(()=>r.statusText);throw new Error(`Introspect failed: ${r.status} ${e}`)}let s;try{s=await r.json()}catch(o){throw console.error("❌ JSON parse error:",o),new Error("Invalid JSON response from server")}return s}}const i={AuthClient:o,AuthFetch:a,TokenStorage:n};e.AuthClient=o,e.AuthFetch=a,e.TokenStorage=n,e.default=i,Object.defineProperties(e,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});
package/dist/index.d.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  export interface AuthClientOptions {
2
2
  baseUrl: string;
3
- introspectBaseUrl: string;
4
3
  tenant?: string | null;
5
4
  loginPath?: string;
6
5
  refreshPath?: string;
@@ -68,7 +67,7 @@ export class AuthClient {
68
67
  tenant: string | null;
69
68
  headers: Record<string, string>;
70
69
  storage: TokenStorage | null;
71
-
70
+ fetcher: AuthFetch | null;
72
71
  login(
73
72
  identifier: string,
74
73
  password: string,
@@ -77,12 +76,10 @@ export class AuthClient {
77
76
  ): Promise<AuthResponse>;
78
77
 
79
78
  refresh(refreshToken: string): Promise<AuthResponse>;
80
-
81
- introspect(token?: string | null): Promise<AuthResponse>;
82
79
  }
83
80
 
84
81
  export class AuthFetch {
85
- constructor(authClient: AuthClient, storage?: TokenStorage);
82
+ constructor(authClient: AuthClient, introspectBaseUrl: string, storage?: TokenStorage, introspectPath?: string = "introspect");
86
83
 
87
84
  /**
88
85
  * fetch wrapper — hỗ trợ:
@@ -96,6 +93,7 @@ export class AuthFetch {
96
93
  init?: RequestInit,
97
94
  onProgress?: ProgressCallback
98
95
  ): Promise<Response>;
96
+ introspect(token?: string | null): Promise<AuthResponse>;
99
97
  }
100
98
 
101
99
  declare const _default: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gh-platform/auth-sdk",
3
- "version": "1.0.7",
3
+ "version": "1.0.9",
4
4
  "description": "VanillaJS Auth SDK for GH Platform",
5
5
  "type": "module",
6
6
  "main": "dist/auth-sdk.umd.js",
package/src/client.js CHANGED
@@ -11,7 +11,6 @@ export default class AuthClient {
11
11
  */
12
12
  constructor({
13
13
  baseUrl,
14
- introspectBaseUrl,
15
14
  tenant = null,
16
15
  loginPath = null,
17
16
  refreshPath = null,
@@ -20,16 +19,13 @@ export default class AuthClient {
20
19
  }) {
21
20
  if (!baseUrl) throw new Error("baseUrl is required");
22
21
  this.baseUrl = baseUrl.replace(/\/$/, "");
23
- if (!introspectBaseUrl) {
24
- throw new Error("introspectBaseUrl is required");
25
- }
22
+
26
23
  // default path builder: tenant-aware
27
24
  const prefix = tenant ? `/api/v1/${tenant}/auth` : `/api/v1/auth`;
28
25
 
29
26
  this.loginUrl = this.baseUrl + (loginPath || `${prefix}/login`);
30
27
  this.refreshUrl = this.baseUrl + (refreshPath || `${prefix}/refresh`);
31
28
 
32
- this.introspectUrl = (`${introspectBaseUrl}/introspect`);
33
29
 
34
30
  this.tenant = tenant;
35
31
  this.headers = { "Content-Type": "application/json", ...headers };
@@ -120,40 +116,7 @@ export default class AuthClient {
120
116
  }
121
117
  }
122
118
 
123
- async introspect(token = null) {
124
- // -----------------------------
125
- // 🔥 Auto-get token from storage
126
- // -----------------------------
127
- let finalToken = token;
119
+ // src/client.js
128
120
 
129
- if (this.storage && !finalToken) {
130
- finalToken = this.storage.accessToken;
131
- }
132
121
 
133
- if (!finalToken) {
134
- throw new Error("No access token available for introspection");
135
- }
136
- if (!this.introspectUrl) {
137
- throw new Error("No introspect url config");
138
- }
139
- const headers = new Headers(this.headers);
140
- headers.set("Authorization", `Bearer ${finalToken}`);
141
- const res = await fetch(this.introspectUrl, {
142
- method: "GET",
143
- headers: headers,
144
- });
145
-
146
- if (!res.ok) {
147
- const text = await res.text().catch(() => res.statusText);
148
- throw new Error(`Introspect failed: ${res.status} ${text}`);
149
- }
150
- let json; // 👈 MUST DECLARE
151
- try {
152
- json = await res.json();
153
- } catch (e) {
154
- console.error("❌ JSON parse error:", e);
155
- throw new Error("Invalid JSON response from server");
156
- }
157
- return json;
158
- }
159
122
  }
package/src/index.d.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  export interface AuthClientOptions {
2
2
  baseUrl: string;
3
- introspectBaseUrl: string;
4
3
  tenant?: string | null;
5
4
  loginPath?: string;
6
5
  refreshPath?: string;
@@ -68,7 +67,7 @@ export class AuthClient {
68
67
  tenant: string | null;
69
68
  headers: Record<string, string>;
70
69
  storage: TokenStorage | null;
71
-
70
+ fetcher: AuthFetch | null;
72
71
  login(
73
72
  identifier: string,
74
73
  password: string,
@@ -77,12 +76,10 @@ export class AuthClient {
77
76
  ): Promise<AuthResponse>;
78
77
 
79
78
  refresh(refreshToken: string): Promise<AuthResponse>;
80
-
81
- introspect(token?: string | null): Promise<AuthResponse>;
82
79
  }
83
80
 
84
81
  export class AuthFetch {
85
- constructor(authClient: AuthClient, storage?: TokenStorage);
82
+ constructor(authClient: AuthClient, introspectBaseUrl: string, storage?: TokenStorage, introspectPath?: string = "introspect");
86
83
 
87
84
  /**
88
85
  * fetch wrapper — hỗ trợ:
@@ -96,6 +93,7 @@ export class AuthFetch {
96
93
  init?: RequestInit,
97
94
  onProgress?: ProgressCallback
98
95
  ): Promise<Response>;
96
+ introspect(token?: string | null): Promise<AuthResponse>;
99
97
  }
100
98
 
101
99
  declare const _default: {
package/src/middleware.js CHANGED
@@ -2,8 +2,13 @@
2
2
  import { TokenStorage } from "./storage.js";
3
3
 
4
4
  export class AuthFetch {
5
- constructor(authClient, storage = null) {
5
+ constructor(authClient, introspectBaseUrl, storage = null, introspectPath = "introspect") {
6
6
  this.client = authClient;
7
+ if (!introspectBaseUrl) {
8
+ throw new Error("introspectBaseUrl is required");
9
+ }
10
+ this.introspectUrl = (`${introspectBaseUrl.replace(/^\//, "")}/${introspectPath.replace(/^\//, "")}`);
11
+
7
12
  this.storage = storage || new TokenStorage("auth", authClient.tenant || null);
8
13
  }
9
14
 
@@ -106,5 +111,40 @@ export class AuthFetch {
106
111
  xhr.send(options.body);
107
112
  });
108
113
  }
114
+
115
+
116
+ async introspect(token = null) {
117
+ let finalToken = token;
118
+
119
+ if (this.storage && !finalToken) {
120
+ finalToken = this.storage.accessToken;
121
+ }
122
+
123
+ if (!finalToken) {
124
+ throw new Error("No access token available for introspection");
125
+ }
126
+ if (!this.introspectUrl) {
127
+ throw new Error("No introspect url config");
128
+ }
129
+
130
+ const res = await this.fetch(this.introspectUrl, {
131
+ method: "GET",
132
+ });
133
+
134
+ if (!res.ok) {
135
+ const text = await res.text().catch(() => res.statusText);
136
+ throw new Error(`Introspect failed: ${res.status} ${text}`);
137
+ }
138
+
139
+ let json;
140
+ try {
141
+ json = await res.json();
142
+ } catch (e) {
143
+ console.error("❌ JSON parse error:", e);
144
+ throw new Error("Invalid JSON response from server");
145
+ }
146
+
147
+ return json;
148
+ }
109
149
  }
110
150
  export default AuthFetch;