@lanonasis/oauth-client 1.2.6 → 1.2.8
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/{api-key-storage-web-DannE11B.d.cts → api-key-storage-web-J3W8nQi2.d.cts} +53 -1
- package/dist/{api-key-storage-web-DannE11B.d.ts → api-key-storage-web-J3W8nQi2.d.ts} +53 -1
- package/dist/browser.cjs +188 -1
- package/dist/browser.d.cts +2 -2
- package/dist/browser.d.ts +2 -2
- package/dist/browser.mjs +188 -1
- package/dist/index.cjs +441 -61
- package/dist/index.d.cts +150 -3
- package/dist/index.d.ts +150 -3
- package/dist/index.mjs +441 -61
- package/package.json +8 -3
|
@@ -29,6 +29,42 @@ interface PKCEChallenge {
|
|
|
29
29
|
codeVerifier: string;
|
|
30
30
|
codeChallenge: string;
|
|
31
31
|
}
|
|
32
|
+
type AuthTokenType = 'api_key' | 'jwt' | 'oauth' | 'cli';
|
|
33
|
+
interface AuthValidationResult {
|
|
34
|
+
valid: boolean;
|
|
35
|
+
type?: AuthTokenType;
|
|
36
|
+
userId?: string;
|
|
37
|
+
email?: string;
|
|
38
|
+
role?: string;
|
|
39
|
+
projectScope?: string;
|
|
40
|
+
permissions?: string[];
|
|
41
|
+
scope?: string | string[];
|
|
42
|
+
expiresAt?: string | null;
|
|
43
|
+
error?: string;
|
|
44
|
+
raw?: unknown;
|
|
45
|
+
}
|
|
46
|
+
interface AuthGatewayClientConfig {
|
|
47
|
+
authBaseUrl?: string;
|
|
48
|
+
clientId?: string;
|
|
49
|
+
projectScope?: string;
|
|
50
|
+
}
|
|
51
|
+
interface TokenExchangeOptions {
|
|
52
|
+
projectScope?: string;
|
|
53
|
+
platform?: string;
|
|
54
|
+
}
|
|
55
|
+
interface TokenExchangeResponse {
|
|
56
|
+
access_token: string;
|
|
57
|
+
refresh_token: string;
|
|
58
|
+
expires_in: number;
|
|
59
|
+
token_type?: string;
|
|
60
|
+
user?: {
|
|
61
|
+
id: string;
|
|
62
|
+
email?: string;
|
|
63
|
+
role?: string;
|
|
64
|
+
project_scope?: string;
|
|
65
|
+
};
|
|
66
|
+
[key: string]: unknown;
|
|
67
|
+
}
|
|
32
68
|
|
|
33
69
|
declare abstract class BaseOAuthFlow {
|
|
34
70
|
protected readonly clientId: string;
|
|
@@ -91,6 +127,22 @@ declare class TokenStorage implements TokenStorageAdapter {
|
|
|
91
127
|
private getWebEncryptionKey;
|
|
92
128
|
}
|
|
93
129
|
|
|
130
|
+
declare class AuthGatewayClient {
|
|
131
|
+
private authBaseUrl;
|
|
132
|
+
private projectScope;
|
|
133
|
+
private flow;
|
|
134
|
+
constructor(config?: AuthGatewayClientConfig);
|
|
135
|
+
exchangeSupabaseToken(supabaseToken: string, options?: TokenExchangeOptions): Promise<TokenExchangeResponse>;
|
|
136
|
+
refreshToken(refreshToken: string): Promise<TokenResponse>;
|
|
137
|
+
revokeToken(token: string, tokenType?: 'access_token' | 'refresh_token'): Promise<void>;
|
|
138
|
+
validateToken(token: string): Promise<AuthValidationResult>;
|
|
139
|
+
verifyApiKey(apiKey: string): Promise<AuthValidationResult>;
|
|
140
|
+
verifyToken(token: string): Promise<AuthValidationResult>;
|
|
141
|
+
introspectToken(token: string): Promise<AuthValidationResult>;
|
|
142
|
+
private normalizeTokenType;
|
|
143
|
+
private requestJson;
|
|
144
|
+
}
|
|
145
|
+
|
|
94
146
|
/**
|
|
95
147
|
* Browser-only token storage that avoids Node/Electron dependencies.
|
|
96
148
|
* Tokens are encrypted with Web Crypto and stored in localStorage.
|
|
@@ -205,4 +257,4 @@ declare class ApiKeyStorageWeb {
|
|
|
205
257
|
private base64Decode;
|
|
206
258
|
}
|
|
207
259
|
|
|
208
|
-
export {
|
|
260
|
+
export { AuthGatewayClient as A, BaseOAuthFlow as B, DesktopOAuthFlow as D, type GrantType as G, type OAuthConfig as O, type PKCEChallenge as P, type TokenStorageAdapter as T, TokenStorageWeb as a, ApiKeyStorageWeb as b, type TokenResponse as c, type DeviceCodeResponse as d, type AuthError as e, type AuthTokenType as f, type AuthValidationResult as g, type AuthGatewayClientConfig as h, type TokenExchangeOptions as i, type TokenExchangeResponse as j, TokenStorage as k, type ApiKeyData as l, ApiKeyStorage as m };
|
|
@@ -29,6 +29,42 @@ interface PKCEChallenge {
|
|
|
29
29
|
codeVerifier: string;
|
|
30
30
|
codeChallenge: string;
|
|
31
31
|
}
|
|
32
|
+
type AuthTokenType = 'api_key' | 'jwt' | 'oauth' | 'cli';
|
|
33
|
+
interface AuthValidationResult {
|
|
34
|
+
valid: boolean;
|
|
35
|
+
type?: AuthTokenType;
|
|
36
|
+
userId?: string;
|
|
37
|
+
email?: string;
|
|
38
|
+
role?: string;
|
|
39
|
+
projectScope?: string;
|
|
40
|
+
permissions?: string[];
|
|
41
|
+
scope?: string | string[];
|
|
42
|
+
expiresAt?: string | null;
|
|
43
|
+
error?: string;
|
|
44
|
+
raw?: unknown;
|
|
45
|
+
}
|
|
46
|
+
interface AuthGatewayClientConfig {
|
|
47
|
+
authBaseUrl?: string;
|
|
48
|
+
clientId?: string;
|
|
49
|
+
projectScope?: string;
|
|
50
|
+
}
|
|
51
|
+
interface TokenExchangeOptions {
|
|
52
|
+
projectScope?: string;
|
|
53
|
+
platform?: string;
|
|
54
|
+
}
|
|
55
|
+
interface TokenExchangeResponse {
|
|
56
|
+
access_token: string;
|
|
57
|
+
refresh_token: string;
|
|
58
|
+
expires_in: number;
|
|
59
|
+
token_type?: string;
|
|
60
|
+
user?: {
|
|
61
|
+
id: string;
|
|
62
|
+
email?: string;
|
|
63
|
+
role?: string;
|
|
64
|
+
project_scope?: string;
|
|
65
|
+
};
|
|
66
|
+
[key: string]: unknown;
|
|
67
|
+
}
|
|
32
68
|
|
|
33
69
|
declare abstract class BaseOAuthFlow {
|
|
34
70
|
protected readonly clientId: string;
|
|
@@ -91,6 +127,22 @@ declare class TokenStorage implements TokenStorageAdapter {
|
|
|
91
127
|
private getWebEncryptionKey;
|
|
92
128
|
}
|
|
93
129
|
|
|
130
|
+
declare class AuthGatewayClient {
|
|
131
|
+
private authBaseUrl;
|
|
132
|
+
private projectScope;
|
|
133
|
+
private flow;
|
|
134
|
+
constructor(config?: AuthGatewayClientConfig);
|
|
135
|
+
exchangeSupabaseToken(supabaseToken: string, options?: TokenExchangeOptions): Promise<TokenExchangeResponse>;
|
|
136
|
+
refreshToken(refreshToken: string): Promise<TokenResponse>;
|
|
137
|
+
revokeToken(token: string, tokenType?: 'access_token' | 'refresh_token'): Promise<void>;
|
|
138
|
+
validateToken(token: string): Promise<AuthValidationResult>;
|
|
139
|
+
verifyApiKey(apiKey: string): Promise<AuthValidationResult>;
|
|
140
|
+
verifyToken(token: string): Promise<AuthValidationResult>;
|
|
141
|
+
introspectToken(token: string): Promise<AuthValidationResult>;
|
|
142
|
+
private normalizeTokenType;
|
|
143
|
+
private requestJson;
|
|
144
|
+
}
|
|
145
|
+
|
|
94
146
|
/**
|
|
95
147
|
* Browser-only token storage that avoids Node/Electron dependencies.
|
|
96
148
|
* Tokens are encrypted with Web Crypto and stored in localStorage.
|
|
@@ -205,4 +257,4 @@ declare class ApiKeyStorageWeb {
|
|
|
205
257
|
private base64Decode;
|
|
206
258
|
}
|
|
207
259
|
|
|
208
|
-
export {
|
|
260
|
+
export { AuthGatewayClient as A, BaseOAuthFlow as B, DesktopOAuthFlow as D, type GrantType as G, type OAuthConfig as O, type PKCEChallenge as P, type TokenStorageAdapter as T, TokenStorageWeb as a, ApiKeyStorageWeb as b, type TokenResponse as c, type DeviceCodeResponse as d, type AuthError as e, type AuthTokenType as f, type AuthValidationResult as g, type AuthGatewayClientConfig as h, type TokenExchangeOptions as i, type TokenExchangeResponse as j, TokenStorage as k, type ApiKeyData as l, ApiKeyStorage as m };
|
package/dist/browser.cjs
CHANGED
|
@@ -31,6 +31,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
31
|
var browser_exports = {};
|
|
32
32
|
__export(browser_exports, {
|
|
33
33
|
ApiKeyStorageWeb: () => ApiKeyStorageWeb,
|
|
34
|
+
AuthGatewayClient: () => AuthGatewayClient,
|
|
34
35
|
BaseOAuthFlow: () => BaseOAuthFlow,
|
|
35
36
|
DesktopOAuthFlow: () => DesktopOAuthFlow,
|
|
36
37
|
MCPClient: () => MCPClient,
|
|
@@ -432,7 +433,7 @@ var APIKeyFlow = class extends BaseOAuthFlow {
|
|
|
432
433
|
*/
|
|
433
434
|
async validateAPIKey() {
|
|
434
435
|
try {
|
|
435
|
-
const response = await (0, import_cross_fetch2.default)(`${this.
|
|
436
|
+
const response = await (0, import_cross_fetch2.default)(`${this.authBaseUrl}/api/v1/health`, {
|
|
436
437
|
headers: {
|
|
437
438
|
"x-api-key": this.apiKey
|
|
438
439
|
}
|
|
@@ -662,6 +663,192 @@ var MCPClient = class {
|
|
|
662
663
|
}
|
|
663
664
|
};
|
|
664
665
|
|
|
666
|
+
// src/client/auth-gateway-client.ts
|
|
667
|
+
var import_cross_fetch4 = __toESM(require("cross-fetch"), 1);
|
|
668
|
+
var GatewayOAuthFlow = class extends BaseOAuthFlow {
|
|
669
|
+
async authenticate() {
|
|
670
|
+
throw new Error("Interactive authentication is not supported in AuthGatewayClient.");
|
|
671
|
+
}
|
|
672
|
+
};
|
|
673
|
+
var DEFAULT_AUTH_BASE_URL = "https://auth.lanonasis.com";
|
|
674
|
+
var DEFAULT_CLIENT_ID = "lanonasis-cli";
|
|
675
|
+
var DEFAULT_PROJECT_SCOPE = "lanonasis-maas";
|
|
676
|
+
var API_KEY_PREFIXES = ["lano_", "pk_", "sk_"];
|
|
677
|
+
var AuthGatewayClient = class {
|
|
678
|
+
constructor(config = {}) {
|
|
679
|
+
const baseUrl = config.authBaseUrl || DEFAULT_AUTH_BASE_URL;
|
|
680
|
+
this.authBaseUrl = baseUrl.replace(/\/+$/, "");
|
|
681
|
+
this.projectScope = config.projectScope || DEFAULT_PROJECT_SCOPE;
|
|
682
|
+
this.flow = new GatewayOAuthFlow({
|
|
683
|
+
clientId: config.clientId || DEFAULT_CLIENT_ID,
|
|
684
|
+
authBaseUrl: this.authBaseUrl
|
|
685
|
+
});
|
|
686
|
+
}
|
|
687
|
+
async exchangeSupabaseToken(supabaseToken, options = {}) {
|
|
688
|
+
const token = typeof supabaseToken === "string" ? supabaseToken.trim() : "";
|
|
689
|
+
if (!token) {
|
|
690
|
+
throw new Error("Supabase access token is required");
|
|
691
|
+
}
|
|
692
|
+
const projectScope = options.projectScope || this.projectScope;
|
|
693
|
+
const platform = options.platform || "web";
|
|
694
|
+
return this.requestJson("/v1/auth/token/exchange", {
|
|
695
|
+
method: "POST",
|
|
696
|
+
headers: {
|
|
697
|
+
Authorization: `Bearer ${token}`,
|
|
698
|
+
"Content-Type": "application/json",
|
|
699
|
+
"X-Project-Scope": projectScope
|
|
700
|
+
},
|
|
701
|
+
body: JSON.stringify({
|
|
702
|
+
project_scope: projectScope,
|
|
703
|
+
platform
|
|
704
|
+
})
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
async refreshToken(refreshToken) {
|
|
708
|
+
return this.flow.refreshToken(refreshToken);
|
|
709
|
+
}
|
|
710
|
+
async revokeToken(token, tokenType = "access_token") {
|
|
711
|
+
return this.flow.revokeToken(token, tokenType);
|
|
712
|
+
}
|
|
713
|
+
async validateToken(token) {
|
|
714
|
+
const trimmed = typeof token === "string" ? token.trim() : "";
|
|
715
|
+
if (!trimmed) {
|
|
716
|
+
return { valid: false, error: "Token is required" };
|
|
717
|
+
}
|
|
718
|
+
if (API_KEY_PREFIXES.some((prefix) => trimmed.startsWith(prefix))) {
|
|
719
|
+
return this.verifyApiKey(trimmed);
|
|
720
|
+
}
|
|
721
|
+
if (trimmed.startsWith("cli_") || trimmed.includes(".")) {
|
|
722
|
+
return this.verifyToken(trimmed);
|
|
723
|
+
}
|
|
724
|
+
return this.introspectToken(trimmed);
|
|
725
|
+
}
|
|
726
|
+
async verifyApiKey(apiKey) {
|
|
727
|
+
try {
|
|
728
|
+
const data = await this.requestJson("/v1/auth/verify-api-key", {
|
|
729
|
+
method: "POST",
|
|
730
|
+
headers: {
|
|
731
|
+
"Content-Type": "application/json",
|
|
732
|
+
"X-API-Key": apiKey
|
|
733
|
+
}
|
|
734
|
+
});
|
|
735
|
+
if (!data || data.valid === false) {
|
|
736
|
+
return {
|
|
737
|
+
valid: false,
|
|
738
|
+
type: "api_key",
|
|
739
|
+
error: data && (data.error || data.message) || "Invalid API key",
|
|
740
|
+
raw: data
|
|
741
|
+
};
|
|
742
|
+
}
|
|
743
|
+
return {
|
|
744
|
+
valid: true,
|
|
745
|
+
type: "api_key",
|
|
746
|
+
userId: data.userId,
|
|
747
|
+
projectScope: data.projectScope,
|
|
748
|
+
permissions: data.permissions || [],
|
|
749
|
+
raw: data
|
|
750
|
+
};
|
|
751
|
+
} catch (error) {
|
|
752
|
+
return {
|
|
753
|
+
valid: false,
|
|
754
|
+
type: "api_key",
|
|
755
|
+
error: error?.message || "API key validation failed",
|
|
756
|
+
raw: error?.data
|
|
757
|
+
};
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
async verifyToken(token) {
|
|
761
|
+
try {
|
|
762
|
+
const data = await this.requestJson("/v1/auth/verify-token", {
|
|
763
|
+
method: "POST",
|
|
764
|
+
headers: { "Content-Type": "application/json" },
|
|
765
|
+
body: JSON.stringify({ token })
|
|
766
|
+
});
|
|
767
|
+
if (!data || data.valid === false) {
|
|
768
|
+
return {
|
|
769
|
+
valid: false,
|
|
770
|
+
type: data?.type ? this.normalizeTokenType(data.type) : "jwt",
|
|
771
|
+
error: data?.error || "Invalid token",
|
|
772
|
+
raw: data
|
|
773
|
+
};
|
|
774
|
+
}
|
|
775
|
+
return {
|
|
776
|
+
valid: true,
|
|
777
|
+
type: data.type ? this.normalizeTokenType(data.type) : "jwt",
|
|
778
|
+
userId: data.user?.id,
|
|
779
|
+
email: data.user?.email,
|
|
780
|
+
role: data.user?.role,
|
|
781
|
+
expiresAt: data.expires_at || null,
|
|
782
|
+
raw: data
|
|
783
|
+
};
|
|
784
|
+
} catch (error) {
|
|
785
|
+
return {
|
|
786
|
+
valid: false,
|
|
787
|
+
type: "jwt",
|
|
788
|
+
error: error?.message || "Token verification failed",
|
|
789
|
+
raw: error?.data
|
|
790
|
+
};
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
async introspectToken(token) {
|
|
794
|
+
try {
|
|
795
|
+
const data = await this.requestJson("/oauth/introspect", {
|
|
796
|
+
method: "POST",
|
|
797
|
+
headers: { "Content-Type": "application/json" },
|
|
798
|
+
body: JSON.stringify({ token })
|
|
799
|
+
});
|
|
800
|
+
if (!data || data.active !== true) {
|
|
801
|
+
return {
|
|
802
|
+
valid: false,
|
|
803
|
+
type: "oauth",
|
|
804
|
+
error: "Token is inactive",
|
|
805
|
+
raw: data
|
|
806
|
+
};
|
|
807
|
+
}
|
|
808
|
+
return {
|
|
809
|
+
valid: true,
|
|
810
|
+
type: "oauth",
|
|
811
|
+
userId: data.user_id || data.sub,
|
|
812
|
+
scope: data.scope,
|
|
813
|
+
expiresAt: data.exp ? new Date(data.exp * 1e3).toISOString() : null,
|
|
814
|
+
raw: data
|
|
815
|
+
};
|
|
816
|
+
} catch (error) {
|
|
817
|
+
return {
|
|
818
|
+
valid: false,
|
|
819
|
+
type: "oauth",
|
|
820
|
+
error: error?.message || "Token introspection failed",
|
|
821
|
+
raw: error?.data
|
|
822
|
+
};
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
normalizeTokenType(type) {
|
|
826
|
+
if (type === "cli_token") return "cli";
|
|
827
|
+
if (type === "jwt") return "jwt";
|
|
828
|
+
return "jwt";
|
|
829
|
+
}
|
|
830
|
+
async requestJson(path, options) {
|
|
831
|
+
const response = await (0, import_cross_fetch4.default)(`${this.authBaseUrl}${path.startsWith("/") ? path : `/${path}`}`, options);
|
|
832
|
+
const text = await response.text();
|
|
833
|
+
let data = null;
|
|
834
|
+
if (text) {
|
|
835
|
+
try {
|
|
836
|
+
data = JSON.parse(text);
|
|
837
|
+
} catch (parseError) {
|
|
838
|
+
data = { raw: text };
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
if (!response.ok) {
|
|
842
|
+
const message = data?.message || data?.error || `Request failed (${response.status})`;
|
|
843
|
+
const error = new Error(message);
|
|
844
|
+
error.status = response.status;
|
|
845
|
+
error.data = data;
|
|
846
|
+
throw error;
|
|
847
|
+
}
|
|
848
|
+
return data;
|
|
849
|
+
}
|
|
850
|
+
};
|
|
851
|
+
|
|
665
852
|
// src/storage/api-key-storage-web.ts
|
|
666
853
|
var ApiKeyStorageWeb = class {
|
|
667
854
|
constructor() {
|
package/dist/browser.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { O as OAuthConfig, T as TokenStorageAdapter } from './api-key-storage-web-
|
|
2
|
-
export {
|
|
1
|
+
import { O as OAuthConfig, T as TokenStorageAdapter } from './api-key-storage-web-J3W8nQi2.cjs';
|
|
2
|
+
export { b as ApiKeyStorageWeb, e as AuthError, A as AuthGatewayClient, h as AuthGatewayClientConfig, f as AuthTokenType, g as AuthValidationResult, B as BaseOAuthFlow, D as DesktopOAuthFlow, d as DeviceCodeResponse, G as GrantType, P as PKCEChallenge, i as TokenExchangeOptions, j as TokenExchangeResponse, c as TokenResponse, a as TokenStorageWeb } from './api-key-storage-web-J3W8nQi2.cjs';
|
|
3
3
|
|
|
4
4
|
interface MCPClientConfig extends Partial<OAuthConfig> {
|
|
5
5
|
mcpEndpoint?: string;
|
package/dist/browser.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { O as OAuthConfig, T as TokenStorageAdapter } from './api-key-storage-web-
|
|
2
|
-
export {
|
|
1
|
+
import { O as OAuthConfig, T as TokenStorageAdapter } from './api-key-storage-web-J3W8nQi2.js';
|
|
2
|
+
export { b as ApiKeyStorageWeb, e as AuthError, A as AuthGatewayClient, h as AuthGatewayClientConfig, f as AuthTokenType, g as AuthValidationResult, B as BaseOAuthFlow, D as DesktopOAuthFlow, d as DeviceCodeResponse, G as GrantType, P as PKCEChallenge, i as TokenExchangeOptions, j as TokenExchangeResponse, c as TokenResponse, a as TokenStorageWeb } from './api-key-storage-web-J3W8nQi2.js';
|
|
3
3
|
|
|
4
4
|
interface MCPClientConfig extends Partial<OAuthConfig> {
|
|
5
5
|
mcpEndpoint?: string;
|
package/dist/browser.mjs
CHANGED
|
@@ -399,7 +399,7 @@ var APIKeyFlow = class extends BaseOAuthFlow {
|
|
|
399
399
|
*/
|
|
400
400
|
async validateAPIKey() {
|
|
401
401
|
try {
|
|
402
|
-
const response = await fetch2(`${this.
|
|
402
|
+
const response = await fetch2(`${this.authBaseUrl}/api/v1/health`, {
|
|
403
403
|
headers: {
|
|
404
404
|
"x-api-key": this.apiKey
|
|
405
405
|
}
|
|
@@ -629,6 +629,192 @@ var MCPClient = class {
|
|
|
629
629
|
}
|
|
630
630
|
};
|
|
631
631
|
|
|
632
|
+
// src/client/auth-gateway-client.ts
|
|
633
|
+
import fetch4 from "cross-fetch";
|
|
634
|
+
var GatewayOAuthFlow = class extends BaseOAuthFlow {
|
|
635
|
+
async authenticate() {
|
|
636
|
+
throw new Error("Interactive authentication is not supported in AuthGatewayClient.");
|
|
637
|
+
}
|
|
638
|
+
};
|
|
639
|
+
var DEFAULT_AUTH_BASE_URL = "https://auth.lanonasis.com";
|
|
640
|
+
var DEFAULT_CLIENT_ID = "lanonasis-cli";
|
|
641
|
+
var DEFAULT_PROJECT_SCOPE = "lanonasis-maas";
|
|
642
|
+
var API_KEY_PREFIXES = ["lano_", "pk_", "sk_"];
|
|
643
|
+
var AuthGatewayClient = class {
|
|
644
|
+
constructor(config = {}) {
|
|
645
|
+
const baseUrl = config.authBaseUrl || DEFAULT_AUTH_BASE_URL;
|
|
646
|
+
this.authBaseUrl = baseUrl.replace(/\/+$/, "");
|
|
647
|
+
this.projectScope = config.projectScope || DEFAULT_PROJECT_SCOPE;
|
|
648
|
+
this.flow = new GatewayOAuthFlow({
|
|
649
|
+
clientId: config.clientId || DEFAULT_CLIENT_ID,
|
|
650
|
+
authBaseUrl: this.authBaseUrl
|
|
651
|
+
});
|
|
652
|
+
}
|
|
653
|
+
async exchangeSupabaseToken(supabaseToken, options = {}) {
|
|
654
|
+
const token = typeof supabaseToken === "string" ? supabaseToken.trim() : "";
|
|
655
|
+
if (!token) {
|
|
656
|
+
throw new Error("Supabase access token is required");
|
|
657
|
+
}
|
|
658
|
+
const projectScope = options.projectScope || this.projectScope;
|
|
659
|
+
const platform = options.platform || "web";
|
|
660
|
+
return this.requestJson("/v1/auth/token/exchange", {
|
|
661
|
+
method: "POST",
|
|
662
|
+
headers: {
|
|
663
|
+
Authorization: `Bearer ${token}`,
|
|
664
|
+
"Content-Type": "application/json",
|
|
665
|
+
"X-Project-Scope": projectScope
|
|
666
|
+
},
|
|
667
|
+
body: JSON.stringify({
|
|
668
|
+
project_scope: projectScope,
|
|
669
|
+
platform
|
|
670
|
+
})
|
|
671
|
+
});
|
|
672
|
+
}
|
|
673
|
+
async refreshToken(refreshToken) {
|
|
674
|
+
return this.flow.refreshToken(refreshToken);
|
|
675
|
+
}
|
|
676
|
+
async revokeToken(token, tokenType = "access_token") {
|
|
677
|
+
return this.flow.revokeToken(token, tokenType);
|
|
678
|
+
}
|
|
679
|
+
async validateToken(token) {
|
|
680
|
+
const trimmed = typeof token === "string" ? token.trim() : "";
|
|
681
|
+
if (!trimmed) {
|
|
682
|
+
return { valid: false, error: "Token is required" };
|
|
683
|
+
}
|
|
684
|
+
if (API_KEY_PREFIXES.some((prefix) => trimmed.startsWith(prefix))) {
|
|
685
|
+
return this.verifyApiKey(trimmed);
|
|
686
|
+
}
|
|
687
|
+
if (trimmed.startsWith("cli_") || trimmed.includes(".")) {
|
|
688
|
+
return this.verifyToken(trimmed);
|
|
689
|
+
}
|
|
690
|
+
return this.introspectToken(trimmed);
|
|
691
|
+
}
|
|
692
|
+
async verifyApiKey(apiKey) {
|
|
693
|
+
try {
|
|
694
|
+
const data = await this.requestJson("/v1/auth/verify-api-key", {
|
|
695
|
+
method: "POST",
|
|
696
|
+
headers: {
|
|
697
|
+
"Content-Type": "application/json",
|
|
698
|
+
"X-API-Key": apiKey
|
|
699
|
+
}
|
|
700
|
+
});
|
|
701
|
+
if (!data || data.valid === false) {
|
|
702
|
+
return {
|
|
703
|
+
valid: false,
|
|
704
|
+
type: "api_key",
|
|
705
|
+
error: data && (data.error || data.message) || "Invalid API key",
|
|
706
|
+
raw: data
|
|
707
|
+
};
|
|
708
|
+
}
|
|
709
|
+
return {
|
|
710
|
+
valid: true,
|
|
711
|
+
type: "api_key",
|
|
712
|
+
userId: data.userId,
|
|
713
|
+
projectScope: data.projectScope,
|
|
714
|
+
permissions: data.permissions || [],
|
|
715
|
+
raw: data
|
|
716
|
+
};
|
|
717
|
+
} catch (error) {
|
|
718
|
+
return {
|
|
719
|
+
valid: false,
|
|
720
|
+
type: "api_key",
|
|
721
|
+
error: error?.message || "API key validation failed",
|
|
722
|
+
raw: error?.data
|
|
723
|
+
};
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
async verifyToken(token) {
|
|
727
|
+
try {
|
|
728
|
+
const data = await this.requestJson("/v1/auth/verify-token", {
|
|
729
|
+
method: "POST",
|
|
730
|
+
headers: { "Content-Type": "application/json" },
|
|
731
|
+
body: JSON.stringify({ token })
|
|
732
|
+
});
|
|
733
|
+
if (!data || data.valid === false) {
|
|
734
|
+
return {
|
|
735
|
+
valid: false,
|
|
736
|
+
type: data?.type ? this.normalizeTokenType(data.type) : "jwt",
|
|
737
|
+
error: data?.error || "Invalid token",
|
|
738
|
+
raw: data
|
|
739
|
+
};
|
|
740
|
+
}
|
|
741
|
+
return {
|
|
742
|
+
valid: true,
|
|
743
|
+
type: data.type ? this.normalizeTokenType(data.type) : "jwt",
|
|
744
|
+
userId: data.user?.id,
|
|
745
|
+
email: data.user?.email,
|
|
746
|
+
role: data.user?.role,
|
|
747
|
+
expiresAt: data.expires_at || null,
|
|
748
|
+
raw: data
|
|
749
|
+
};
|
|
750
|
+
} catch (error) {
|
|
751
|
+
return {
|
|
752
|
+
valid: false,
|
|
753
|
+
type: "jwt",
|
|
754
|
+
error: error?.message || "Token verification failed",
|
|
755
|
+
raw: error?.data
|
|
756
|
+
};
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
async introspectToken(token) {
|
|
760
|
+
try {
|
|
761
|
+
const data = await this.requestJson("/oauth/introspect", {
|
|
762
|
+
method: "POST",
|
|
763
|
+
headers: { "Content-Type": "application/json" },
|
|
764
|
+
body: JSON.stringify({ token })
|
|
765
|
+
});
|
|
766
|
+
if (!data || data.active !== true) {
|
|
767
|
+
return {
|
|
768
|
+
valid: false,
|
|
769
|
+
type: "oauth",
|
|
770
|
+
error: "Token is inactive",
|
|
771
|
+
raw: data
|
|
772
|
+
};
|
|
773
|
+
}
|
|
774
|
+
return {
|
|
775
|
+
valid: true,
|
|
776
|
+
type: "oauth",
|
|
777
|
+
userId: data.user_id || data.sub,
|
|
778
|
+
scope: data.scope,
|
|
779
|
+
expiresAt: data.exp ? new Date(data.exp * 1e3).toISOString() : null,
|
|
780
|
+
raw: data
|
|
781
|
+
};
|
|
782
|
+
} catch (error) {
|
|
783
|
+
return {
|
|
784
|
+
valid: false,
|
|
785
|
+
type: "oauth",
|
|
786
|
+
error: error?.message || "Token introspection failed",
|
|
787
|
+
raw: error?.data
|
|
788
|
+
};
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
normalizeTokenType(type) {
|
|
792
|
+
if (type === "cli_token") return "cli";
|
|
793
|
+
if (type === "jwt") return "jwt";
|
|
794
|
+
return "jwt";
|
|
795
|
+
}
|
|
796
|
+
async requestJson(path, options) {
|
|
797
|
+
const response = await fetch4(`${this.authBaseUrl}${path.startsWith("/") ? path : `/${path}`}`, options);
|
|
798
|
+
const text = await response.text();
|
|
799
|
+
let data = null;
|
|
800
|
+
if (text) {
|
|
801
|
+
try {
|
|
802
|
+
data = JSON.parse(text);
|
|
803
|
+
} catch (parseError) {
|
|
804
|
+
data = { raw: text };
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
if (!response.ok) {
|
|
808
|
+
const message = data?.message || data?.error || `Request failed (${response.status})`;
|
|
809
|
+
const error = new Error(message);
|
|
810
|
+
error.status = response.status;
|
|
811
|
+
error.data = data;
|
|
812
|
+
throw error;
|
|
813
|
+
}
|
|
814
|
+
return data;
|
|
815
|
+
}
|
|
816
|
+
};
|
|
817
|
+
|
|
632
818
|
// src/storage/api-key-storage-web.ts
|
|
633
819
|
var ApiKeyStorageWeb = class {
|
|
634
820
|
constructor() {
|
|
@@ -760,6 +946,7 @@ var ApiKeyStorageWeb = class {
|
|
|
760
946
|
};
|
|
761
947
|
export {
|
|
762
948
|
ApiKeyStorageWeb,
|
|
949
|
+
AuthGatewayClient,
|
|
763
950
|
BaseOAuthFlow,
|
|
764
951
|
DesktopOAuthFlow,
|
|
765
952
|
MCPClient,
|