@lanonasis/oauth-client 1.2.6 → 1.2.7
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 +187 -0
- package/dist/browser.d.cts +2 -2
- package/dist/browser.d.ts +2 -2
- package/dist/browser.mjs +187 -0
- package/dist/index.cjs +187 -0
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.mjs +187 -0
- 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,
|
|
@@ -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
|
@@ -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,
|
package/dist/index.cjs
CHANGED
|
@@ -32,6 +32,7 @@ var index_exports = {};
|
|
|
32
32
|
__export(index_exports, {
|
|
33
33
|
ApiKeyStorage: () => ApiKeyStorage,
|
|
34
34
|
ApiKeyStorageWeb: () => ApiKeyStorageWeb,
|
|
35
|
+
AuthGatewayClient: () => AuthGatewayClient,
|
|
35
36
|
BaseOAuthFlow: () => BaseOAuthFlow,
|
|
36
37
|
DesktopOAuthFlow: () => DesktopOAuthFlow,
|
|
37
38
|
MCPClient: () => MCPClient,
|
|
@@ -1791,3 +1792,189 @@ var MCPClient = class {
|
|
|
1791
1792
|
return this.request("memory/delete", { id });
|
|
1792
1793
|
}
|
|
1793
1794
|
};
|
|
1795
|
+
|
|
1796
|
+
// src/client/auth-gateway-client.ts
|
|
1797
|
+
var import_cross_fetch5 = __toESM(require("cross-fetch"), 1);
|
|
1798
|
+
var GatewayOAuthFlow = class extends BaseOAuthFlow {
|
|
1799
|
+
async authenticate() {
|
|
1800
|
+
throw new Error("Interactive authentication is not supported in AuthGatewayClient.");
|
|
1801
|
+
}
|
|
1802
|
+
};
|
|
1803
|
+
var DEFAULT_AUTH_BASE_URL = "https://auth.lanonasis.com";
|
|
1804
|
+
var DEFAULT_CLIENT_ID = "lanonasis-cli";
|
|
1805
|
+
var DEFAULT_PROJECT_SCOPE = "lanonasis-maas";
|
|
1806
|
+
var API_KEY_PREFIXES = ["lano_", "pk_", "sk_"];
|
|
1807
|
+
var AuthGatewayClient = class {
|
|
1808
|
+
constructor(config = {}) {
|
|
1809
|
+
const baseUrl = config.authBaseUrl || DEFAULT_AUTH_BASE_URL;
|
|
1810
|
+
this.authBaseUrl = baseUrl.replace(/\/+$/, "");
|
|
1811
|
+
this.projectScope = config.projectScope || DEFAULT_PROJECT_SCOPE;
|
|
1812
|
+
this.flow = new GatewayOAuthFlow({
|
|
1813
|
+
clientId: config.clientId || DEFAULT_CLIENT_ID,
|
|
1814
|
+
authBaseUrl: this.authBaseUrl
|
|
1815
|
+
});
|
|
1816
|
+
}
|
|
1817
|
+
async exchangeSupabaseToken(supabaseToken, options = {}) {
|
|
1818
|
+
const token = typeof supabaseToken === "string" ? supabaseToken.trim() : "";
|
|
1819
|
+
if (!token) {
|
|
1820
|
+
throw new Error("Supabase access token is required");
|
|
1821
|
+
}
|
|
1822
|
+
const projectScope = options.projectScope || this.projectScope;
|
|
1823
|
+
const platform = options.platform || "web";
|
|
1824
|
+
return this.requestJson("/v1/auth/token/exchange", {
|
|
1825
|
+
method: "POST",
|
|
1826
|
+
headers: {
|
|
1827
|
+
Authorization: `Bearer ${token}`,
|
|
1828
|
+
"Content-Type": "application/json",
|
|
1829
|
+
"X-Project-Scope": projectScope
|
|
1830
|
+
},
|
|
1831
|
+
body: JSON.stringify({
|
|
1832
|
+
project_scope: projectScope,
|
|
1833
|
+
platform
|
|
1834
|
+
})
|
|
1835
|
+
});
|
|
1836
|
+
}
|
|
1837
|
+
async refreshToken(refreshToken) {
|
|
1838
|
+
return this.flow.refreshToken(refreshToken);
|
|
1839
|
+
}
|
|
1840
|
+
async revokeToken(token, tokenType = "access_token") {
|
|
1841
|
+
return this.flow.revokeToken(token, tokenType);
|
|
1842
|
+
}
|
|
1843
|
+
async validateToken(token) {
|
|
1844
|
+
const trimmed = typeof token === "string" ? token.trim() : "";
|
|
1845
|
+
if (!trimmed) {
|
|
1846
|
+
return { valid: false, error: "Token is required" };
|
|
1847
|
+
}
|
|
1848
|
+
if (API_KEY_PREFIXES.some((prefix) => trimmed.startsWith(prefix))) {
|
|
1849
|
+
return this.verifyApiKey(trimmed);
|
|
1850
|
+
}
|
|
1851
|
+
if (trimmed.startsWith("cli_") || trimmed.includes(".")) {
|
|
1852
|
+
return this.verifyToken(trimmed);
|
|
1853
|
+
}
|
|
1854
|
+
return this.introspectToken(trimmed);
|
|
1855
|
+
}
|
|
1856
|
+
async verifyApiKey(apiKey) {
|
|
1857
|
+
try {
|
|
1858
|
+
const data = await this.requestJson("/v1/auth/verify-api-key", {
|
|
1859
|
+
method: "POST",
|
|
1860
|
+
headers: {
|
|
1861
|
+
"Content-Type": "application/json",
|
|
1862
|
+
"X-API-Key": apiKey
|
|
1863
|
+
}
|
|
1864
|
+
});
|
|
1865
|
+
if (!data || data.valid === false) {
|
|
1866
|
+
return {
|
|
1867
|
+
valid: false,
|
|
1868
|
+
type: "api_key",
|
|
1869
|
+
error: data && (data.error || data.message) || "Invalid API key",
|
|
1870
|
+
raw: data
|
|
1871
|
+
};
|
|
1872
|
+
}
|
|
1873
|
+
return {
|
|
1874
|
+
valid: true,
|
|
1875
|
+
type: "api_key",
|
|
1876
|
+
userId: data.userId,
|
|
1877
|
+
projectScope: data.projectScope,
|
|
1878
|
+
permissions: data.permissions || [],
|
|
1879
|
+
raw: data
|
|
1880
|
+
};
|
|
1881
|
+
} catch (error) {
|
|
1882
|
+
return {
|
|
1883
|
+
valid: false,
|
|
1884
|
+
type: "api_key",
|
|
1885
|
+
error: error?.message || "API key validation failed",
|
|
1886
|
+
raw: error?.data
|
|
1887
|
+
};
|
|
1888
|
+
}
|
|
1889
|
+
}
|
|
1890
|
+
async verifyToken(token) {
|
|
1891
|
+
try {
|
|
1892
|
+
const data = await this.requestJson("/v1/auth/verify-token", {
|
|
1893
|
+
method: "POST",
|
|
1894
|
+
headers: { "Content-Type": "application/json" },
|
|
1895
|
+
body: JSON.stringify({ token })
|
|
1896
|
+
});
|
|
1897
|
+
if (!data || data.valid === false) {
|
|
1898
|
+
return {
|
|
1899
|
+
valid: false,
|
|
1900
|
+
type: data?.type ? this.normalizeTokenType(data.type) : "jwt",
|
|
1901
|
+
error: data?.error || "Invalid token",
|
|
1902
|
+
raw: data
|
|
1903
|
+
};
|
|
1904
|
+
}
|
|
1905
|
+
return {
|
|
1906
|
+
valid: true,
|
|
1907
|
+
type: data.type ? this.normalizeTokenType(data.type) : "jwt",
|
|
1908
|
+
userId: data.user?.id,
|
|
1909
|
+
email: data.user?.email,
|
|
1910
|
+
role: data.user?.role,
|
|
1911
|
+
expiresAt: data.expires_at || null,
|
|
1912
|
+
raw: data
|
|
1913
|
+
};
|
|
1914
|
+
} catch (error) {
|
|
1915
|
+
return {
|
|
1916
|
+
valid: false,
|
|
1917
|
+
type: "jwt",
|
|
1918
|
+
error: error?.message || "Token verification failed",
|
|
1919
|
+
raw: error?.data
|
|
1920
|
+
};
|
|
1921
|
+
}
|
|
1922
|
+
}
|
|
1923
|
+
async introspectToken(token) {
|
|
1924
|
+
try {
|
|
1925
|
+
const data = await this.requestJson("/oauth/introspect", {
|
|
1926
|
+
method: "POST",
|
|
1927
|
+
headers: { "Content-Type": "application/json" },
|
|
1928
|
+
body: JSON.stringify({ token })
|
|
1929
|
+
});
|
|
1930
|
+
if (!data || data.active !== true) {
|
|
1931
|
+
return {
|
|
1932
|
+
valid: false,
|
|
1933
|
+
type: "oauth",
|
|
1934
|
+
error: "Token is inactive",
|
|
1935
|
+
raw: data
|
|
1936
|
+
};
|
|
1937
|
+
}
|
|
1938
|
+
return {
|
|
1939
|
+
valid: true,
|
|
1940
|
+
type: "oauth",
|
|
1941
|
+
userId: data.user_id || data.sub,
|
|
1942
|
+
scope: data.scope,
|
|
1943
|
+
expiresAt: data.exp ? new Date(data.exp * 1e3).toISOString() : null,
|
|
1944
|
+
raw: data
|
|
1945
|
+
};
|
|
1946
|
+
} catch (error) {
|
|
1947
|
+
return {
|
|
1948
|
+
valid: false,
|
|
1949
|
+
type: "oauth",
|
|
1950
|
+
error: error?.message || "Token introspection failed",
|
|
1951
|
+
raw: error?.data
|
|
1952
|
+
};
|
|
1953
|
+
}
|
|
1954
|
+
}
|
|
1955
|
+
normalizeTokenType(type) {
|
|
1956
|
+
if (type === "cli_token") return "cli";
|
|
1957
|
+
if (type === "jwt") return "jwt";
|
|
1958
|
+
return "jwt";
|
|
1959
|
+
}
|
|
1960
|
+
async requestJson(path, options) {
|
|
1961
|
+
const response = await (0, import_cross_fetch5.default)(`${this.authBaseUrl}${path.startsWith("/") ? path : `/${path}`}`, options);
|
|
1962
|
+
const text = await response.text();
|
|
1963
|
+
let data = null;
|
|
1964
|
+
if (text) {
|
|
1965
|
+
try {
|
|
1966
|
+
data = JSON.parse(text);
|
|
1967
|
+
} catch (parseError) {
|
|
1968
|
+
data = { raw: text };
|
|
1969
|
+
}
|
|
1970
|
+
}
|
|
1971
|
+
if (!response.ok) {
|
|
1972
|
+
const message = data?.message || data?.error || `Request failed (${response.status})`;
|
|
1973
|
+
const error = new Error(message);
|
|
1974
|
+
error.status = response.status;
|
|
1975
|
+
error.data = data;
|
|
1976
|
+
throw error;
|
|
1977
|
+
}
|
|
1978
|
+
return data;
|
|
1979
|
+
}
|
|
1980
|
+
};
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { B as BaseOAuthFlow, O as OAuthConfig,
|
|
2
|
-
export {
|
|
1
|
+
import { B as BaseOAuthFlow, O as OAuthConfig, c as TokenResponse, T as TokenStorageAdapter } from './api-key-storage-web-J3W8nQi2.cjs';
|
|
2
|
+
export { l as ApiKeyData, m as ApiKeyStorage, b as ApiKeyStorageWeb, e as AuthError, A as AuthGatewayClient, h as AuthGatewayClientConfig, f as AuthTokenType, g as AuthValidationResult, D as DesktopOAuthFlow, d as DeviceCodeResponse, G as GrantType, P as PKCEChallenge, i as TokenExchangeOptions, j as TokenExchangeResponse, k as TokenStorage, a as TokenStorageWeb } from './api-key-storage-web-J3W8nQi2.cjs';
|
|
3
3
|
|
|
4
4
|
declare class TerminalOAuthFlow extends BaseOAuthFlow {
|
|
5
5
|
private pollInterval;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { B as BaseOAuthFlow, O as OAuthConfig,
|
|
2
|
-
export {
|
|
1
|
+
import { B as BaseOAuthFlow, O as OAuthConfig, c as TokenResponse, T as TokenStorageAdapter } from './api-key-storage-web-J3W8nQi2.js';
|
|
2
|
+
export { l as ApiKeyData, m as ApiKeyStorage, b as ApiKeyStorageWeb, e as AuthError, A as AuthGatewayClient, h as AuthGatewayClientConfig, f as AuthTokenType, g as AuthValidationResult, D as DesktopOAuthFlow, d as DeviceCodeResponse, G as GrantType, P as PKCEChallenge, i as TokenExchangeOptions, j as TokenExchangeResponse, k as TokenStorage, a as TokenStorageWeb } from './api-key-storage-web-J3W8nQi2.js';
|
|
3
3
|
|
|
4
4
|
declare class TerminalOAuthFlow extends BaseOAuthFlow {
|
|
5
5
|
private pollInterval;
|
package/dist/index.mjs
CHANGED
|
@@ -1755,9 +1755,196 @@ var MCPClient = class {
|
|
|
1755
1755
|
return this.request("memory/delete", { id });
|
|
1756
1756
|
}
|
|
1757
1757
|
};
|
|
1758
|
+
|
|
1759
|
+
// src/client/auth-gateway-client.ts
|
|
1760
|
+
import fetch5 from "cross-fetch";
|
|
1761
|
+
var GatewayOAuthFlow = class extends BaseOAuthFlow {
|
|
1762
|
+
async authenticate() {
|
|
1763
|
+
throw new Error("Interactive authentication is not supported in AuthGatewayClient.");
|
|
1764
|
+
}
|
|
1765
|
+
};
|
|
1766
|
+
var DEFAULT_AUTH_BASE_URL = "https://auth.lanonasis.com";
|
|
1767
|
+
var DEFAULT_CLIENT_ID = "lanonasis-cli";
|
|
1768
|
+
var DEFAULT_PROJECT_SCOPE = "lanonasis-maas";
|
|
1769
|
+
var API_KEY_PREFIXES = ["lano_", "pk_", "sk_"];
|
|
1770
|
+
var AuthGatewayClient = class {
|
|
1771
|
+
constructor(config = {}) {
|
|
1772
|
+
const baseUrl = config.authBaseUrl || DEFAULT_AUTH_BASE_URL;
|
|
1773
|
+
this.authBaseUrl = baseUrl.replace(/\/+$/, "");
|
|
1774
|
+
this.projectScope = config.projectScope || DEFAULT_PROJECT_SCOPE;
|
|
1775
|
+
this.flow = new GatewayOAuthFlow({
|
|
1776
|
+
clientId: config.clientId || DEFAULT_CLIENT_ID,
|
|
1777
|
+
authBaseUrl: this.authBaseUrl
|
|
1778
|
+
});
|
|
1779
|
+
}
|
|
1780
|
+
async exchangeSupabaseToken(supabaseToken, options = {}) {
|
|
1781
|
+
const token = typeof supabaseToken === "string" ? supabaseToken.trim() : "";
|
|
1782
|
+
if (!token) {
|
|
1783
|
+
throw new Error("Supabase access token is required");
|
|
1784
|
+
}
|
|
1785
|
+
const projectScope = options.projectScope || this.projectScope;
|
|
1786
|
+
const platform = options.platform || "web";
|
|
1787
|
+
return this.requestJson("/v1/auth/token/exchange", {
|
|
1788
|
+
method: "POST",
|
|
1789
|
+
headers: {
|
|
1790
|
+
Authorization: `Bearer ${token}`,
|
|
1791
|
+
"Content-Type": "application/json",
|
|
1792
|
+
"X-Project-Scope": projectScope
|
|
1793
|
+
},
|
|
1794
|
+
body: JSON.stringify({
|
|
1795
|
+
project_scope: projectScope,
|
|
1796
|
+
platform
|
|
1797
|
+
})
|
|
1798
|
+
});
|
|
1799
|
+
}
|
|
1800
|
+
async refreshToken(refreshToken) {
|
|
1801
|
+
return this.flow.refreshToken(refreshToken);
|
|
1802
|
+
}
|
|
1803
|
+
async revokeToken(token, tokenType = "access_token") {
|
|
1804
|
+
return this.flow.revokeToken(token, tokenType);
|
|
1805
|
+
}
|
|
1806
|
+
async validateToken(token) {
|
|
1807
|
+
const trimmed = typeof token === "string" ? token.trim() : "";
|
|
1808
|
+
if (!trimmed) {
|
|
1809
|
+
return { valid: false, error: "Token is required" };
|
|
1810
|
+
}
|
|
1811
|
+
if (API_KEY_PREFIXES.some((prefix) => trimmed.startsWith(prefix))) {
|
|
1812
|
+
return this.verifyApiKey(trimmed);
|
|
1813
|
+
}
|
|
1814
|
+
if (trimmed.startsWith("cli_") || trimmed.includes(".")) {
|
|
1815
|
+
return this.verifyToken(trimmed);
|
|
1816
|
+
}
|
|
1817
|
+
return this.introspectToken(trimmed);
|
|
1818
|
+
}
|
|
1819
|
+
async verifyApiKey(apiKey) {
|
|
1820
|
+
try {
|
|
1821
|
+
const data = await this.requestJson("/v1/auth/verify-api-key", {
|
|
1822
|
+
method: "POST",
|
|
1823
|
+
headers: {
|
|
1824
|
+
"Content-Type": "application/json",
|
|
1825
|
+
"X-API-Key": apiKey
|
|
1826
|
+
}
|
|
1827
|
+
});
|
|
1828
|
+
if (!data || data.valid === false) {
|
|
1829
|
+
return {
|
|
1830
|
+
valid: false,
|
|
1831
|
+
type: "api_key",
|
|
1832
|
+
error: data && (data.error || data.message) || "Invalid API key",
|
|
1833
|
+
raw: data
|
|
1834
|
+
};
|
|
1835
|
+
}
|
|
1836
|
+
return {
|
|
1837
|
+
valid: true,
|
|
1838
|
+
type: "api_key",
|
|
1839
|
+
userId: data.userId,
|
|
1840
|
+
projectScope: data.projectScope,
|
|
1841
|
+
permissions: data.permissions || [],
|
|
1842
|
+
raw: data
|
|
1843
|
+
};
|
|
1844
|
+
} catch (error) {
|
|
1845
|
+
return {
|
|
1846
|
+
valid: false,
|
|
1847
|
+
type: "api_key",
|
|
1848
|
+
error: error?.message || "API key validation failed",
|
|
1849
|
+
raw: error?.data
|
|
1850
|
+
};
|
|
1851
|
+
}
|
|
1852
|
+
}
|
|
1853
|
+
async verifyToken(token) {
|
|
1854
|
+
try {
|
|
1855
|
+
const data = await this.requestJson("/v1/auth/verify-token", {
|
|
1856
|
+
method: "POST",
|
|
1857
|
+
headers: { "Content-Type": "application/json" },
|
|
1858
|
+
body: JSON.stringify({ token })
|
|
1859
|
+
});
|
|
1860
|
+
if (!data || data.valid === false) {
|
|
1861
|
+
return {
|
|
1862
|
+
valid: false,
|
|
1863
|
+
type: data?.type ? this.normalizeTokenType(data.type) : "jwt",
|
|
1864
|
+
error: data?.error || "Invalid token",
|
|
1865
|
+
raw: data
|
|
1866
|
+
};
|
|
1867
|
+
}
|
|
1868
|
+
return {
|
|
1869
|
+
valid: true,
|
|
1870
|
+
type: data.type ? this.normalizeTokenType(data.type) : "jwt",
|
|
1871
|
+
userId: data.user?.id,
|
|
1872
|
+
email: data.user?.email,
|
|
1873
|
+
role: data.user?.role,
|
|
1874
|
+
expiresAt: data.expires_at || null,
|
|
1875
|
+
raw: data
|
|
1876
|
+
};
|
|
1877
|
+
} catch (error) {
|
|
1878
|
+
return {
|
|
1879
|
+
valid: false,
|
|
1880
|
+
type: "jwt",
|
|
1881
|
+
error: error?.message || "Token verification failed",
|
|
1882
|
+
raw: error?.data
|
|
1883
|
+
};
|
|
1884
|
+
}
|
|
1885
|
+
}
|
|
1886
|
+
async introspectToken(token) {
|
|
1887
|
+
try {
|
|
1888
|
+
const data = await this.requestJson("/oauth/introspect", {
|
|
1889
|
+
method: "POST",
|
|
1890
|
+
headers: { "Content-Type": "application/json" },
|
|
1891
|
+
body: JSON.stringify({ token })
|
|
1892
|
+
});
|
|
1893
|
+
if (!data || data.active !== true) {
|
|
1894
|
+
return {
|
|
1895
|
+
valid: false,
|
|
1896
|
+
type: "oauth",
|
|
1897
|
+
error: "Token is inactive",
|
|
1898
|
+
raw: data
|
|
1899
|
+
};
|
|
1900
|
+
}
|
|
1901
|
+
return {
|
|
1902
|
+
valid: true,
|
|
1903
|
+
type: "oauth",
|
|
1904
|
+
userId: data.user_id || data.sub,
|
|
1905
|
+
scope: data.scope,
|
|
1906
|
+
expiresAt: data.exp ? new Date(data.exp * 1e3).toISOString() : null,
|
|
1907
|
+
raw: data
|
|
1908
|
+
};
|
|
1909
|
+
} catch (error) {
|
|
1910
|
+
return {
|
|
1911
|
+
valid: false,
|
|
1912
|
+
type: "oauth",
|
|
1913
|
+
error: error?.message || "Token introspection failed",
|
|
1914
|
+
raw: error?.data
|
|
1915
|
+
};
|
|
1916
|
+
}
|
|
1917
|
+
}
|
|
1918
|
+
normalizeTokenType(type) {
|
|
1919
|
+
if (type === "cli_token") return "cli";
|
|
1920
|
+
if (type === "jwt") return "jwt";
|
|
1921
|
+
return "jwt";
|
|
1922
|
+
}
|
|
1923
|
+
async requestJson(path, options) {
|
|
1924
|
+
const response = await fetch5(`${this.authBaseUrl}${path.startsWith("/") ? path : `/${path}`}`, options);
|
|
1925
|
+
const text = await response.text();
|
|
1926
|
+
let data = null;
|
|
1927
|
+
if (text) {
|
|
1928
|
+
try {
|
|
1929
|
+
data = JSON.parse(text);
|
|
1930
|
+
} catch (parseError) {
|
|
1931
|
+
data = { raw: text };
|
|
1932
|
+
}
|
|
1933
|
+
}
|
|
1934
|
+
if (!response.ok) {
|
|
1935
|
+
const message = data?.message || data?.error || `Request failed (${response.status})`;
|
|
1936
|
+
const error = new Error(message);
|
|
1937
|
+
error.status = response.status;
|
|
1938
|
+
error.data = data;
|
|
1939
|
+
throw error;
|
|
1940
|
+
}
|
|
1941
|
+
return data;
|
|
1942
|
+
}
|
|
1943
|
+
};
|
|
1758
1944
|
export {
|
|
1759
1945
|
ApiKeyStorage,
|
|
1760
1946
|
ApiKeyStorageWeb,
|
|
1947
|
+
AuthGatewayClient,
|
|
1761
1948
|
BaseOAuthFlow,
|
|
1762
1949
|
DesktopOAuthFlow,
|
|
1763
1950
|
MCPClient,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lanonasis/oauth-client",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.7",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "OAuth and API Key authentication client for Lan Onasis MCP integration",
|
|
6
6
|
"license": "MIT",
|
|
@@ -38,7 +38,9 @@
|
|
|
38
38
|
"scripts": {
|
|
39
39
|
"build": "tsup --config tsup.config.ts",
|
|
40
40
|
"dev": "tsup --watch --config tsup.config.ts",
|
|
41
|
-
"test": "
|
|
41
|
+
"test": "vitest run",
|
|
42
|
+
"test:watch": "vitest",
|
|
43
|
+
"test:coverage": "vitest run --coverage",
|
|
42
44
|
"lint": "eslint src"
|
|
43
45
|
},
|
|
44
46
|
"keywords": [
|
|
@@ -52,6 +54,7 @@
|
|
|
52
54
|
],
|
|
53
55
|
"dependencies": {
|
|
54
56
|
"cross-fetch": "^4.0.0",
|
|
57
|
+
"eventsource": "^2.0.2",
|
|
55
58
|
"keytar": "^7.9.0",
|
|
56
59
|
"open": "^9.1.0"
|
|
57
60
|
},
|
|
@@ -59,11 +62,13 @@
|
|
|
59
62
|
"@eslint/js": "^9.17.0",
|
|
60
63
|
"@types/eventsource": "^3.0.0",
|
|
61
64
|
"@types/node": "^20.0.0",
|
|
65
|
+
"@vitest/coverage-v8": "^2.0.0",
|
|
62
66
|
"eslint": "^9.17.0",
|
|
63
67
|
"globals": "^16.4.0",
|
|
64
68
|
"tsup": "^8.5.0",
|
|
65
69
|
"typescript": "^5.3.3",
|
|
66
|
-
"typescript-eslint": "^8.18.1"
|
|
70
|
+
"typescript-eslint": "^8.18.1",
|
|
71
|
+
"vitest": "^2.0.0"
|
|
67
72
|
},
|
|
68
73
|
"peerDependencies": {
|
|
69
74
|
"@supabase/supabase-js": "^2.0.0"
|