mirror of
https://github.com/DeBrosOfficial/network-ts-sdk.git
synced 2025-12-12 18:28:50 +00:00
Compare commits
2 Commits
c6dfb0bfed
...
76bb82d4f8
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
76bb82d4f8 | ||
|
|
dcf8efe428 |
@ -1,10 +1,5 @@
|
|||||||
import { HttpClient } from "../core/http";
|
import { HttpClient } from "../core/http";
|
||||||
import {
|
import { AuthConfig, WhoAmI, StorageAdapter, MemoryStorage } from "./types";
|
||||||
AuthConfig,
|
|
||||||
WhoAmI,
|
|
||||||
StorageAdapter,
|
|
||||||
MemoryStorage,
|
|
||||||
} from "./types";
|
|
||||||
|
|
||||||
export class AuthClient {
|
export class AuthClient {
|
||||||
private httpClient: HttpClient;
|
private httpClient: HttpClient;
|
||||||
@ -33,14 +28,14 @@ export class AuthClient {
|
|||||||
|
|
||||||
setApiKey(apiKey: string) {
|
setApiKey(apiKey: string) {
|
||||||
this.currentApiKey = apiKey;
|
this.currentApiKey = apiKey;
|
||||||
this.currentJwt = undefined;
|
// Don't clear JWT - it will be cleared explicitly on logout
|
||||||
this.httpClient.setApiKey(apiKey);
|
this.httpClient.setApiKey(apiKey);
|
||||||
this.storage.set("apiKey", apiKey);
|
this.storage.set("apiKey", apiKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
setJwt(jwt: string) {
|
setJwt(jwt: string) {
|
||||||
this.currentJwt = jwt;
|
this.currentJwt = jwt;
|
||||||
this.currentApiKey = undefined;
|
// Don't clear API key - keep it as fallback for after logout
|
||||||
this.httpClient.setJwt(jwt);
|
this.httpClient.setJwt(jwt);
|
||||||
this.storage.set("jwt", jwt);
|
this.storage.set("jwt", jwt);
|
||||||
}
|
}
|
||||||
@ -67,6 +62,51 @@ export class AuthClient {
|
|||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logout user and clear JWT, but preserve API key
|
||||||
|
* Use this for user logout in apps where API key is app-level credential
|
||||||
|
*/
|
||||||
|
async logoutUser(): Promise<void> {
|
||||||
|
// Attempt server-side logout if using JWT
|
||||||
|
if (this.currentJwt) {
|
||||||
|
try {
|
||||||
|
await this.httpClient.post("/v1/auth/logout", { all: true });
|
||||||
|
} catch (error) {
|
||||||
|
// Log warning but don't fail - local cleanup is more important
|
||||||
|
console.warn(
|
||||||
|
"Server-side logout failed, continuing with local cleanup:",
|
||||||
|
error
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear JWT only, preserve API key
|
||||||
|
this.currentJwt = undefined;
|
||||||
|
this.httpClient.setJwt(undefined);
|
||||||
|
await this.storage.set("jwt", ""); // Clear JWT from storage
|
||||||
|
|
||||||
|
// Ensure API key is loaded and set as active auth method
|
||||||
|
if (!this.currentApiKey) {
|
||||||
|
// Try to load from storage
|
||||||
|
const storedApiKey = await this.storage.get("apiKey");
|
||||||
|
if (storedApiKey) {
|
||||||
|
this.currentApiKey = storedApiKey;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore API key as the active auth method
|
||||||
|
if (this.currentApiKey) {
|
||||||
|
this.httpClient.setApiKey(this.currentApiKey);
|
||||||
|
console.log("[Auth] API key restored after user logout");
|
||||||
|
} else {
|
||||||
|
console.warn("[Auth] No API key available after logout");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Full logout - clears both JWT and API key
|
||||||
|
* Use this to completely reset authentication state
|
||||||
|
*/
|
||||||
async logout(): Promise<void> {
|
async logout(): Promise<void> {
|
||||||
// Only attempt server-side logout if using JWT
|
// Only attempt server-side logout if using JWT
|
||||||
// API keys don't support server-side logout with all=true
|
// API keys don't support server-side logout with all=true
|
||||||
@ -75,10 +115,13 @@ export class AuthClient {
|
|||||||
await this.httpClient.post("/v1/auth/logout", { all: true });
|
await this.httpClient.post("/v1/auth/logout", { all: true });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Log warning but don't fail - local cleanup is more important
|
// Log warning but don't fail - local cleanup is more important
|
||||||
console.warn('Server-side logout failed, continuing with local cleanup:', error);
|
console.warn(
|
||||||
|
"Server-side logout failed, continuing with local cleanup:",
|
||||||
|
error
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Always clear local state
|
// Always clear local state
|
||||||
this.currentApiKey = undefined;
|
this.currentApiKey = undefined;
|
||||||
this.currentJwt = undefined;
|
this.currentJwt = undefined;
|
||||||
@ -94,4 +137,78 @@ export class AuthClient {
|
|||||||
this.httpClient.setJwt(undefined);
|
this.httpClient.setJwt(undefined);
|
||||||
await this.storage.clear();
|
await this.storage.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request a challenge nonce for wallet authentication
|
||||||
|
*/
|
||||||
|
async challenge(params: {
|
||||||
|
wallet: string;
|
||||||
|
purpose?: string;
|
||||||
|
namespace?: string;
|
||||||
|
}): Promise<{
|
||||||
|
nonce: string;
|
||||||
|
wallet: string;
|
||||||
|
namespace: string;
|
||||||
|
expires_at: string;
|
||||||
|
}> {
|
||||||
|
const response = await this.httpClient.post("/v1/auth/challenge", {
|
||||||
|
wallet: params.wallet,
|
||||||
|
purpose: params.purpose || "authentication",
|
||||||
|
namespace: params.namespace || "default",
|
||||||
|
});
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify wallet signature and get JWT token
|
||||||
|
*/
|
||||||
|
async verify(params: {
|
||||||
|
wallet: string;
|
||||||
|
nonce: string;
|
||||||
|
signature: string;
|
||||||
|
namespace?: string;
|
||||||
|
}): Promise<{
|
||||||
|
access_token: string;
|
||||||
|
refresh_token: string;
|
||||||
|
subject: string;
|
||||||
|
namespace: string;
|
||||||
|
}> {
|
||||||
|
const response = await this.httpClient.post("/v1/auth/verify", {
|
||||||
|
wallet: params.wallet,
|
||||||
|
nonce: params.nonce,
|
||||||
|
signature: params.signature,
|
||||||
|
namespace: params.namespace || "default",
|
||||||
|
});
|
||||||
|
|
||||||
|
// Automatically set the JWT
|
||||||
|
this.setJwt(response.access_token);
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get API key for wallet (creates namespace ownership)
|
||||||
|
*/
|
||||||
|
async getApiKey(params: {
|
||||||
|
wallet: string;
|
||||||
|
nonce: string;
|
||||||
|
signature: string;
|
||||||
|
namespace?: string;
|
||||||
|
}): Promise<{
|
||||||
|
api_key: string;
|
||||||
|
namespace: string;
|
||||||
|
wallet: string;
|
||||||
|
}> {
|
||||||
|
const response = await this.httpClient.post("/v1/auth/api-key", {
|
||||||
|
wallet: params.wallet,
|
||||||
|
nonce: params.nonce,
|
||||||
|
signature: params.signature,
|
||||||
|
namespace: params.namespace || "default",
|
||||||
|
});
|
||||||
|
|
||||||
|
// Automatically set the API key
|
||||||
|
this.setApiKey(response.api_key);
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,20 +27,53 @@ export class HttpClient {
|
|||||||
|
|
||||||
setApiKey(apiKey?: string) {
|
setApiKey(apiKey?: string) {
|
||||||
this.apiKey = apiKey;
|
this.apiKey = apiKey;
|
||||||
this.jwt = undefined;
|
// Don't clear JWT - allow both to coexist
|
||||||
|
if (typeof console !== "undefined") {
|
||||||
|
console.log(
|
||||||
|
"[HttpClient] API key set:",
|
||||||
|
!!apiKey,
|
||||||
|
"JWT still present:",
|
||||||
|
!!this.jwt
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setJwt(jwt?: string) {
|
setJwt(jwt?: string) {
|
||||||
this.jwt = jwt;
|
this.jwt = jwt;
|
||||||
this.apiKey = undefined;
|
// Don't clear API key - allow both to coexist
|
||||||
|
if (typeof console !== "undefined") {
|
||||||
|
console.log(
|
||||||
|
"[HttpClient] JWT set:",
|
||||||
|
!!jwt,
|
||||||
|
"API key still present:",
|
||||||
|
!!this.apiKey
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private getAuthHeaders(): Record<string, string> {
|
private getAuthHeaders(path: string): Record<string, string> {
|
||||||
const headers: Record<string, string> = {};
|
const headers: Record<string, string> = {};
|
||||||
if (this.jwt) {
|
|
||||||
headers["Authorization"] = `Bearer ${this.jwt}`;
|
// For database operations, ONLY use API key to avoid JWT user context
|
||||||
} else if (this.apiKey) {
|
// interfering with namespace-level authorization
|
||||||
headers["X-API-Key"] = this.apiKey;
|
const isDbOperation = path.includes("/v1/rqlite/");
|
||||||
|
|
||||||
|
if (isDbOperation) {
|
||||||
|
// For database operations: use only API key (preferred for namespace operations)
|
||||||
|
if (this.apiKey) {
|
||||||
|
headers["X-API-Key"] = this.apiKey;
|
||||||
|
} else if (this.jwt) {
|
||||||
|
// Fallback to JWT if no API key
|
||||||
|
headers["Authorization"] = `Bearer ${this.jwt}`;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// For auth/other operations: send both JWT and API key
|
||||||
|
if (this.jwt) {
|
||||||
|
headers["Authorization"] = `Bearer ${this.jwt}`;
|
||||||
|
}
|
||||||
|
if (this.apiKey) {
|
||||||
|
headers["X-API-Key"] = this.apiKey;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return headers;
|
return headers;
|
||||||
}
|
}
|
||||||
@ -68,10 +101,29 @@ export class HttpClient {
|
|||||||
|
|
||||||
const headers: Record<string, string> = {
|
const headers: Record<string, string> = {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
...this.getAuthHeaders(),
|
...this.getAuthHeaders(path),
|
||||||
...options.headers,
|
...options.headers,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Debug: Log headers being sent
|
||||||
|
if (
|
||||||
|
typeof console !== "undefined" &&
|
||||||
|
(path.includes("/db/") ||
|
||||||
|
path.includes("/query") ||
|
||||||
|
path.includes("/auth/"))
|
||||||
|
) {
|
||||||
|
console.log("[HttpClient] Request headers for", path, {
|
||||||
|
hasAuth: !!headers["Authorization"],
|
||||||
|
hasApiKey: !!headers["X-API-Key"],
|
||||||
|
authPrefix: headers["Authorization"]
|
||||||
|
? headers["Authorization"].substring(0, 20)
|
||||||
|
: "none",
|
||||||
|
apiKeyPrefix: headers["X-API-Key"]
|
||||||
|
? headers["X-API-Key"].substring(0, 20)
|
||||||
|
: "none",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const controller = new AbortController();
|
const controller = new AbortController();
|
||||||
const requestTimeout = options.timeout ?? this.timeout; // Use override or default
|
const requestTimeout = options.timeout ?? this.timeout; // Use override or default
|
||||||
const timeoutId = setTimeout(() => controller.abort(), requestTimeout);
|
const timeoutId = setTimeout(() => controller.abort(), requestTimeout);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user