@authava/client
Version:
Client library for seamless integration with Authava's white-label authentication service
1,010 lines (1,005 loc) • 31.7 kB
JavaScript
// src/api/base.ts
var BaseApiClient = class {
config;
constructor(config) {
this.config = config;
}
/**
* Make a request to the Authava API
*/
async request(endpoint, method, data, options = {}) {
const protocol = this.config.secure ? "https" : "http";
const url = `${protocol}://${this.config.resolverDomain}${endpoint}`;
const defaultHeaders = {
"Content-Type": "application/json",
Accept: "application/json",
host: this.config.domain
};
const configCustomHeaders = typeof this.config.customHeaders === "function" ? this.config.customHeaders() : this.config.customHeaders;
const headers = { ...defaultHeaders, ...configCustomHeaders, ...options.headers };
const hasSessionCookie = typeof document !== "undefined" && document.cookie.includes("session=");
const tokenFromStorage = typeof localStorage !== "undefined" ? localStorage.getItem("session_token") : null;
if (!hasSessionCookie && tokenFromStorage && !headers["Authorization"]) {
headers["Authorization"] = `Bearer ${tokenFromStorage}`;
}
const requestOptions = {
method,
headers,
credentials: options.credentials || "include"
};
if (method !== "GET" && data !== void 0) {
requestOptions.body = JSON.stringify(data);
}
try {
const response = await fetch(url, requestOptions);
const contentType = response.headers.get("content-type");
const isJson = contentType && contentType.includes("application/json");
if (response.status === 204 || response.status === 200 && response.headers.get("content-length") === "0") {
return {};
}
const responseData = isJson ? await response.json() : await response.text();
if (!response.ok) {
const errorResponse = responseData;
this.logError(`API error (${response.status})`, errorResponse.error);
return errorResponse;
}
return responseData;
} catch (error) {
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
this.logError("API request failed", error);
return { error: errorMessage };
}
}
/**
* Log an error if debug mode is enabled
*/
logError(message, error) {
if (this.config.debug) {
console.error(`[AuthavaClient] ${message}:`, error);
}
}
/**
* Log a message if debug mode is enabled
*/
log(...args) {
if (this.config.debug) {
console.log("[AuthavaClient]", ...args);
}
}
};
// src/api/auth.ts
var AuthApi = class extends BaseApiClient {
/**
* Login with email and password
*/
async login(data) {
const res = await this.request("/login", "POST", data);
return toAuthavaResult(res);
}
/**
* Register a new user
*/
async register(data) {
const res = await this.request("/register", "POST", data);
return toAuthavaResult(res);
}
/**
* Get the current session
*/
async getSession() {
const res = await this.request("/session", "GET");
return toAuthavaResult(res);
}
/**
* Logout the current user
*/
async logout() {
const res = await this.request("/logout", "POST");
return toAuthavaResult(res);
}
/**
* Request a password reset
*/
async forgotPassword(data) {
const res = await this.request("/forgot-password", "POST", data);
return toAuthavaResult(res);
}
/**
* Reset password with token
*/
async resetPassword(data) {
const res = await this.request("/reset-password", "POST", data);
return toAuthavaResult(res);
}
/**
* Verify MFA code
*/
async verifyMfa(data) {
const res = await this.request("/verify-mfa", "POST", data);
return toAuthavaResult(res);
}
/**
* Send MFA verification email
*/
async sendMfaEmail(data) {
const res = await this.request("/send-mfa-email", "POST", data);
return toAuthavaResult(res);
}
};
// src/api/profile.ts
var ProfileApi = class extends BaseApiClient {
/**
* Get user profile information
*/
async getProfile() {
return this.request("/v1/profile", "GET");
}
/**
* Change email address
*/
async changeEmail(data) {
return this.request("/v1/profile/email", "PUT", data);
}
/**
* Change password
*/
async changePassword(data) {
return this.request("/v1/profile/password", "PUT", data);
}
/**
* Update notification preferences
*/
async updateNotificationPreferences(data) {
return this.request("/v1/profile/notifications", "PUT", data);
}
/**
* Remove an MFA method
*/
async removeMfaMethod(methodId) {
return this.request(`/v1/profile/mfa/${methodId}`, "DELETE");
}
};
// src/api/mfa.ts
var MfaApi = class extends BaseApiClient {
/**
* Initialize Email MFA setup
*/
async setupEmailMfa(data) {
const res = await this.request("/v1/profile/mfa/email/setup", "POST", data);
return toAuthavaResult(res);
}
/**
* Verify and activate Email MFA
*/
async verifyEmailMfa(data) {
const res = await this.request(
"/v1/profile/mfa/email/verify",
"POST",
data
);
return toAuthavaResult(res);
}
/**
* Initialize TOTP MFA setup
*/
async setupTotp(data) {
const res = await this.request("/v1/profile/mfa/totp/setup", "POST", data);
return toAuthavaResult(res);
}
/**
* Verify and activate TOTP MFA
*/
async verifyTotp(data) {
const res = await this.request("/v1/profile/mfa/totp/verify", "POST", data);
return toAuthavaResult(res);
}
};
// src/client.ts
function toAuthavaResult(response) {
return response && typeof response === "object" && "error" in response ? { success: false, error: response.error.toString() } : { success: true, data: response };
}
var AuthavaClient = class {
config;
refreshTimer;
refreshing = false;
currentState;
subscribers = /* @__PURE__ */ new Set();
broadcastChannel;
autoRefreshConfig;
// API clients
authApi;
profileApi;
mfaApi;
constructor(config) {
const resolverDomain = config.resolverDomain || config.domain;
this.config = {
domain: config.domain,
resolverDomain,
secure: config.secure ?? true,
autoRefresh: config.autoRefresh ?? true,
refreshBuffer: config.refreshBuffer ?? 5,
customHeaders: config.customHeaders ?? {},
debug: config.debug ?? false
};
this.autoRefreshConfig = {
enabled: this.config.autoRefresh,
refreshBuffer: this.config.refreshBuffer,
maxRetries: 3,
retryDelay: 1e3
};
this.currentState = {
status: "expired",
user: null
};
this.authApi = new AuthApi(this.config);
this.profileApi = new ProfileApi(this.config);
this.mfaApi = new MfaApi(this.config);
if (typeof BroadcastChannel !== "undefined") {
this.broadcastChannel = new BroadcastChannel("authava-session");
this.broadcastChannel.onmessage = (event) => {
this.handleSessionBroadcast(event.data);
};
}
this.log("Initialized with config:", this.config);
this.checkSession();
}
hasRoles(roles) {
if (!this.currentState.user || !Array.isArray(this.currentState.user.roles)) return false;
const required = Array.isArray(roles) ? roles : [roles];
return required.every((r) => this.currentState.user.roles.includes(r));
}
hasPermissions(permissions) {
if (!this.currentState.user || !Array.isArray(this.currentState.user.permissions)) return false;
const required = Array.isArray(permissions) ? permissions : [permissions];
return required.every((p) => this.currentState.user.permissions.includes(p));
}
async hasScopes({ resource_type, resource_id, actions, resolver }) {
const user = this.currentState.user;
if (!user || !Array.isArray(user.teams)) return false;
for (const team of user.teams) {
if (!Array.isArray(team.scopes)) continue;
const results = await Promise.all(
actions.map(
(action) => this.teamHasMatchingScope(
team.scopes,
resource_type,
resource_id,
[action],
team,
resolver
)
)
);
if (results.every(Boolean)) {
return true;
}
}
return false;
}
async teamHasMatchingScope(scopes, resource_type, resource_id, actions, team, resolver) {
for (const scope of scopes) {
if (scope.resource_type !== resource_type) continue;
if (!actions.includes(scope.action)) continue;
if (scope.resource_id === resource_id) return true;
if (scope.resource_id !== "*") continue;
if (!resolver) continue;
try {
const allowed = await resolver(resource_type, team);
if (allowed.includes(resource_id)) return true;
} catch {
continue;
}
}
return false;
}
encodeBase64Url(input) {
return btoa(input).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
}
setLogoutReason(reason) {
if (typeof document === "undefined") return;
document.cookie = `logout_reason=${encodeURIComponent(reason)}; path=/; SameSite=None; Secure`;
}
// Private method for logging
log(...args) {
if (this.config.debug) {
console.log("[AuthavaClient]", ...args);
}
}
/**
* Log the user out.
* Clears the session cookie, updates the state, and redirects the user.
*/
async logout(reason) {
this.log("Logging out user.");
await this.authApi.logout();
if (typeof document !== "undefined") {
document.cookie = "session=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;";
}
this.updateState({ status: "expired", user: null });
if (typeof window === "undefined") {
this.log("Skipping redirect, window is not defined (SSR or backend).");
return;
}
const defaultRedirect = `${this.config.secure ? "https" : "http"}://${this.config.domain}`;
const redirectUrl = localStorage.getItem("redirect_after_logout") || defaultRedirect;
localStorage.removeItem("redirect_after_logout");
const reasonParam = reason ? `reason=${encodeURIComponent(reason)}` : "";
const currentUrl = window?.location?.href || "";
const nextParam = currentUrl ? `next=${this.encodeBase64Url(currentUrl)}` : "";
const queryParams = [reasonParam, nextParam].filter(Boolean).join("&");
const finalRedirect = queryParams ? `${redirectUrl}?${queryParams}` : redirectUrl;
window.location.href = finalRedirect;
}
getLogoutReason() {
if (typeof document === "undefined") return null;
const match = document.cookie.match(/(?:^|; )logout_reason=([^;]*)/);
return match ? decodeURIComponent(match[1]) : null;
}
/**
* Get the current session if it exists.
* If a session error occurs (including CORS errors), logs out the user.
*/
fetchFailureCount = 0;
getTokenFromStorage() {
if (typeof localStorage === "undefined") return null;
const stored = localStorage.getItem("session_token");
if (stored) return stored;
if (typeof window !== "undefined" && window.location?.href) {
try {
const url = new URL(window.location.href);
const urlToken = url.searchParams.get("token");
if (urlToken) {
localStorage.setItem("session_token", urlToken);
return urlToken;
}
} catch (e) {
}
}
return null;
}
async getSession(extraHeaders) {
try {
const protocol = this.config.secure ? "https" : "http";
const defaultHeaders = {
Accept: "application/json",
host: this.config.domain
};
const tokenFromCookie = this.getTokenFromCookie();
const tokenFromStorage = this.getTokenFromStorage();
if (!tokenFromCookie && tokenFromStorage) {
defaultHeaders.Authorization = `Bearer ${tokenFromStorage}`;
}
const configCustom = (typeof this.config.customHeaders === "function" ? this.config.customHeaders() : this.config.customHeaders) || {};
const headers = { ...defaultHeaders, ...configCustom, ...extraHeaders };
const url = `${protocol}://${this.config.resolverDomain}/session`;
this.log("getSession request to:", url, "with headers:", headers);
const response = await fetch(url, {
method: "POST",
credentials: "include",
headers
});
if (!response.ok) {
if (response.status === 401) {
this.log("Session expired or unauthorized");
this.setLogoutReason("unauthorized");
await this.logout("unauthorized");
return null;
}
throw new Error(`Session check failed: ${response.statusText}`);
}
const session = await response.json();
if (!session || !session.user || !session.user.id || !session.user.email) {
throw new Error("Invalid session data received");
}
this.fetchFailureCount = 0;
this.updateState({ status: "valid", user: session.user });
this.log("Session retrieved successfully:", session);
return session;
} catch (error) {
this.log("getSession error:", error);
const isNetworkError = error instanceof TypeError && error.message.includes("Failed to fetch");
if (isNetworkError) {
this.fetchFailureCount++;
this.log(`\u274C Network error #${this.fetchFailureCount}:`, error);
if (this.fetchFailureCount >= 3) {
this.log("\u{1F6A8} Logging out due to repeated fetch failures.");
this.setLogoutReason("cors");
await this.logout("cors");
} else {
this.updateState({ status: "error", error });
}
} else {
this.updateState({ status: "error", error });
}
return null;
}
}
/**
* Subscribe to session changes.
* Returns an unsubscribe function.
*/
onSessionChange(callback) {
this.subscribers.add(callback);
callback(this.currentState);
return () => {
this.subscribers.delete(callback);
};
}
async checkSession() {
const session = await this.getSession();
if (session && this.autoRefreshConfig.enabled) {
const token = this.getTokenFromCookie();
if (token) {
const expiresAt = this.getTokenExpiration(token);
if (expiresAt) {
this.startAutoRefresh(expiresAt);
}
}
}
}
getTokenFromCookie() {
if (typeof document === "undefined") return null;
const cookieMatch = document.cookie.match(/(?:^|; )session=([^;]*)/);
return cookieMatch ? decodeURIComponent(cookieMatch[1]) : null;
}
getTokenExpiration(token) {
try {
const payload = JSON.parse(atob(token.split(".")[1]));
return new Date(payload.exp * 1e3);
} catch {
return null;
}
}
async startAutoRefresh(expiresAt) {
if (!this.autoRefreshConfig.enabled) return;
const buffer = this.autoRefreshConfig.refreshBuffer * 60 * 1e3;
const refreshTime = new Date(expiresAt.getTime() - buffer);
if (this.refreshTimer) {
clearTimeout(this.refreshTimer);
}
const delay = Math.max(0, refreshTime.getTime() - Date.now());
this.refreshTimer = setTimeout(() => this.refreshSession(), delay);
}
async refreshSession(retryCount = 0) {
if (this.refreshing) return;
try {
this.refreshing = true;
this.updateState({ status: "refreshing" });
const session = await this.getSession();
if (!session) {
await this.logout();
return;
}
const token = this.getTokenFromCookie();
if (!token) {
return;
}
const expiresAt = this.getTokenExpiration(token);
if (expiresAt) {
this.updateState({
status: "valid",
user: session.user,
expiresAt
});
this.startAutoRefresh(expiresAt);
}
} catch (error) {
if (retryCount < this.autoRefreshConfig.maxRetries) {
const delay = this.autoRefreshConfig.retryDelay * Math.pow(2, retryCount);
await new Promise((resolve) => setTimeout(resolve, delay));
return this.refreshSession(retryCount + 1);
}
this.updateState({
status: "error",
error
});
} finally {
this.refreshing = false;
}
}
updateState(newState) {
this.currentState = { ...this.currentState, ...newState };
this.notifySubscribers();
if (this.broadcastChannel) {
this.broadcastChannel.postMessage(this.currentState);
}
}
notifySubscribers() {
for (const callback of this.subscribers) {
try {
callback(this.currentState);
} catch (error) {
console.error("Error in session change callback:", error);
}
}
}
handleSessionBroadcast(state) {
if (JSON.stringify(state) !== JSON.stringify(this.currentState)) {
this.currentState = state;
this.notifySubscribers();
}
}
// ===== Authentication Methods =====
/**
* Login with email and password
*/
async login(data) {
const response = await this.authApi.login(data);
if (!response.success) {
return response;
}
this.updateState({
status: "valid",
user: response.data.user
});
const token = this.getTokenFromCookie();
if (token && this.autoRefreshConfig.enabled) {
const expiresAt = this.getTokenExpiration(token);
if (expiresAt) {
this.startAutoRefresh(expiresAt);
}
}
return response;
}
/**
* Register a new user
*/
async register(data) {
return this.authApi.register(data);
}
/**
* Request a password reset
*/
async forgotPassword(data) {
return this.authApi.forgotPassword(data);
}
/**
* Reset password with token
*/
async resetPassword(data) {
return this.authApi.resetPassword(data);
}
/**
* Verify MFA code
*/
async verifyMfa(data) {
const response = await this.authApi.verifyMfa(data);
if (response.success) {
this.updateState({
status: "valid",
user: response.data.user
});
}
return response;
}
/**
* Send MFA verification email
*/
async sendMfaEmail(data) {
return this.authApi.sendMfaEmail(data);
}
// ===== Profile Methods =====
/**
* Get user profile information
*/
async getProfile() {
const res = await this.profileApi.getProfile();
return toAuthavaResult(res);
}
/**
* Change email address
*/
async changeEmail(data) {
const res = await this.profileApi.changeEmail(data);
return toAuthavaResult(res);
}
/**
* Change password
*/
async changePassword(data) {
const res = await this.profileApi.changePassword(data);
return toAuthavaResult(res);
}
/**
* Update notification preferences
*/
async updateNotificationPreferences(data) {
const res = await this.profileApi.updateNotificationPreferences(data);
return toAuthavaResult(res);
}
/**
* Remove an MFA method
*/
async removeMfaMethod(methodId) {
const res = await this.profileApi.removeMfaMethod(methodId);
return toAuthavaResult(res);
}
// ===== MFA Methods =====
/**
* Initialize Email MFA setup
*/
async setupEmailMfa(data) {
return this.mfaApi.setupEmailMfa(data);
}
/**
* Verify and activate Email MFA
*/
async verifyEmailMfa(data) {
return this.mfaApi.verifyEmailMfa(data);
}
/**
* Initialize TOTP MFA setup
*/
async setupTotp(data) {
return this.mfaApi.setupTotp(data);
}
/**
* Verify and activate TOTP MFA
*/
async verifyTotp(data) {
return this.mfaApi.verifyTotp(data);
}
};
// src/mock.ts
var MockAuthavaClient = class {
currentState;
subscribers = /* @__PURE__ */ new Set();
config;
mockMfaMethods = [];
mockUser = null;
constructor(config) {
this.config = config || {};
this.mockUser = config?.mockUser || null;
this.currentState = this.mockUser ? {
status: "valid",
user: this.mockUser
} : {
status: "expired",
user: null
};
if (this.mockUser) {
this.log("Initialized with mock user:", this.mockUser);
}
if (config?.debug) {
console.log("[MockAuthavaClient] Initialized");
}
}
// Helper method to create a successful API response
createSuccessResponse(data) {
return { data, success: true };
}
// Helper method for void responses
createVoidSuccessResponse() {
return { success: true, data: void 0 };
}
// Helper method to create an error API response
createErrorResponse(message, code = "400", status = 400) {
return { success: false, error: message, code };
}
// Helper method to log debug messages
log(...args) {
if (this.config.debug) {
console.log("[MockAuthavaClient]", ...args);
}
}
// ===== Session Management =====
async getSession() {
return this.currentState.status === "valid" ? {
user: this.currentState.user,
redirect_url: "https://dashboard.example.com",
authority: "auth.example.com",
tenant_id: "mock-tenant-id"
} : null;
}
setSession(session) {
this.currentState = {
status: "valid",
user: session.user
};
this.notifySubscribers();
}
expireSession() {
this.currentState = {
status: "expired",
user: null
};
this.notifySubscribers();
}
onSessionChange(callback) {
this.subscribers.add(callback);
callback(this.currentState);
return () => {
this.subscribers.delete(callback);
};
}
notifySubscribers() {
for (const callback of this.subscribers) {
try {
callback(this.currentState);
} catch (error) {
console.error("MockAuthavaClient callback error:", error);
}
}
}
async logout() {
this.expireSession();
const domain = this.config.domain || "example.com";
const secure = this.config.secure !== false;
const defaultRedirect = `${secure ? "https" : "http"}://${domain}`;
const redirectUrl = defaultRedirect;
if (typeof window !== "undefined") {
window.location.href = redirectUrl;
} else if (this.config.debug) {
this.log(`Would redirect to: ${redirectUrl}`);
}
return this.createVoidSuccessResponse();
}
// ===== Authentication Methods =====
async login(data) {
this.log("Mock login with:", data);
if (!data.email || !data.password) {
return this.createErrorResponse("Email and password are required");
}
const user = {
id: "mock-user-id",
email: data.email,
roles: [],
permissions: [],
teams: [],
last_login_at: (/* @__PURE__ */ new Date()).toISOString()
};
const response = {
user,
redirect_url: "https://dashboard.example.com",
authority: "auth.example.com",
tenant_id: "mock-tenant-id"
};
this.setSession({
user,
redirect_url: response.redirect_url,
authority: "auth.example.com",
tenant_id: "mock-tenant-id"
});
return this.createSuccessResponse(response);
}
async register(data) {
this.log("Mock register with:", data);
if (!data.email || !data.password) {
return this.createErrorResponse("Email and password are required");
}
return this.createVoidSuccessResponse();
}
async forgotPassword(data) {
this.log("Mock forgot password with:", data);
if (!data.email) {
return this.createErrorResponse("Email is required");
}
return this.createVoidSuccessResponse();
}
async resetPassword(data) {
this.log("Mock reset password with:", data);
if (!data.token || !data.password) {
return this.createErrorResponse("Token and password are required");
}
return this.createVoidSuccessResponse();
}
async verifyMfa(data) {
this.log("Mock verify MFA with:", data);
if (!data.session_id || !data.method_id || !data.code) {
return this.createErrorResponse("Session ID, method ID, and code are required");
}
const user = {
id: "mock-user-id",
email: "mock@example.com",
roles: [],
permissions: [],
teams: [],
last_login_at: (/* @__PURE__ */ new Date()).toISOString()
};
const response = {
user,
redirect_url: "https://dashboard.example.com",
authority: "auth.example.com",
tenant_id: "mock-tenant-id"
};
this.setSession({
user,
redirect_url: response.redirect_url,
authority: "auth.example.com",
tenant_id: "mock-tenant-id"
});
return this.createSuccessResponse(response);
}
async sendMfaEmail(data) {
this.log("Mock send MFA email with:", data);
if (!data.session_id || !data.method_id) {
return this.createErrorResponse("Session ID and method ID are required");
}
return this.createVoidSuccessResponse();
}
// ===== Profile Methods =====
async getProfile() {
this.log("Mock get profile");
if (this.currentState.status !== "valid" || !this.currentState.user) {
return this.createErrorResponse("Not authenticated", "401", 401);
}
if (!this.mockUser) {
this.mockUser = {
id: this.currentState.user.id,
email: this.currentState.user.email,
status: "active",
email_verified: true,
roles: [],
permissions: [],
teams: [],
created_at: new Date(Date.now() - 30 * 24 * 60 * 60 * 1e3).toISOString(),
// 30 days ago
updated_at: (/* @__PURE__ */ new Date()).toISOString(),
last_login_at: (/* @__PURE__ */ new Date()).toISOString()
};
}
const response = {
user: this.mockUser,
mfa_methods: this.mockMfaMethods,
notification_preferences: {
user_id: this.mockUser.id,
security_alerts: true,
account_activity: true,
product_updates: false,
marketing_emails: false,
created_at: this.mockUser.created_at,
updated_at: this.mockUser.updated_at
},
recent_security_events: [
{
id: "mock-event-1",
user_id: this.mockUser.id,
event_type: "login",
created_at: (/* @__PURE__ */ new Date()).toISOString(),
ip_address: "127.0.0.1",
user_agent: "Mock Browser"
}
]
};
return this.createSuccessResponse(response);
}
async changeEmail(data) {
this.log("Mock change email with:", data);
if (!data.new_email || !data.password) {
return this.createErrorResponse("New email and password are required");
}
if (this.currentState.status !== "valid" || !this.currentState.user) {
return this.createErrorResponse("Not authenticated", "401", 401);
}
if (this.mockUser) {
this.mockUser.email = data.new_email;
this.mockUser.updated_at = (/* @__PURE__ */ new Date()).toISOString();
}
const updatedUser = { ...this.currentState.user, email: data.new_email };
this.setSession({
user: updatedUser,
redirect_url: "https://dashboard.example.com",
authority: "auth.example.com",
tenant_id: "mock-tenant-id"
});
return this.createVoidSuccessResponse();
}
async changePassword(data) {
this.log("Mock change password with:", data);
if (!data.current_password || !data.new_password) {
return this.createErrorResponse("Current password and new password are required");
}
if (this.currentState.status !== "valid" || !this.currentState.user) {
return this.createErrorResponse("Not authenticated", "401", 401);
}
if (this.mockUser) {
this.mockUser.updated_at = (/* @__PURE__ */ new Date()).toISOString();
}
return this.createVoidSuccessResponse();
}
async updateNotificationPreferences(data) {
this.log("Mock update notification preferences with:", data);
if (this.currentState.status !== "valid" || !this.currentState.user) {
return this.createErrorResponse("Not authenticated", "401", 401);
}
const response = {
user_id: this.currentState.user.id,
security_alerts: data.security_alerts,
account_activity: data.account_activity,
product_updates: data.product_updates,
marketing_emails: data.marketing_emails,
created_at: this.mockUser?.created_at || new Date(Date.now() - 30 * 24 * 60 * 60 * 1e3).toISOString(),
updated_at: (/* @__PURE__ */ new Date()).toISOString()
};
return this.createSuccessResponse(response);
}
async removeMfaMethod(methodId) {
this.log("Mock remove MFA method:", methodId);
if (this.currentState.status !== "valid" || !this.currentState.user) {
return this.createErrorResponse("Not authenticated", "401", 401);
}
this.mockMfaMethods = this.mockMfaMethods.filter((method) => method.id !== methodId);
return this.createVoidSuccessResponse();
}
// ===== MFA Methods =====
async setupEmailMfa(data) {
this.log("Mock setup email MFA with:", data);
if (this.currentState.status !== "valid" || !this.currentState.user) {
return this.createErrorResponse("Not authenticated", "401", 401);
}
const methodId = `mock-email-mfa-${Date.now()}`;
this.mockMfaMethods.push({
id: methodId,
mfa_type: "email",
verified: false,
name: data.name || "Email MFA",
created_at: (/* @__PURE__ */ new Date()).toISOString()
});
return this.createVoidSuccessResponse();
}
async verifyEmailMfa(data) {
this.log("Mock verify email MFA with:", data);
if (!data.code) {
return this.createErrorResponse("Verification code is required");
}
if (this.currentState.status !== "valid" || !this.currentState.user) {
return this.createErrorResponse("Not authenticated", "401", 401);
}
const emailMethod = this.mockMfaMethods.find((m) => m.mfa_type === "email" && !m.verified);
if (emailMethod) {
emailMethod.verified = true;
emailMethod.last_used_at = (/* @__PURE__ */ new Date()).toISOString();
}
const backupCodes = Array.from(
{ length: 10 },
(_, i) => `MOCK-${i.toString().padStart(4, "0")}`
);
return this.createSuccessResponse({ codes: backupCodes });
}
async setupTotp(data) {
this.log("Mock setup TOTP with:", data);
if (this.currentState.status !== "valid" || !this.currentState.user) {
return this.createErrorResponse("Not authenticated", "401", 401);
}
const methodId = `mock-totp-mfa-${Date.now()}`;
this.mockMfaMethods.push({
id: methodId,
mfa_type: "totp",
verified: false,
name: data.name || "TOTP MFA",
created_at: (/* @__PURE__ */ new Date()).toISOString()
});
const response = {
secret: "MOCKSECRETKEY123456",
qr_code: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=",
method_id: methodId
};
return this.createSuccessResponse(response);
}
async verifyTotp(data) {
this.log("Mock verify TOTP with:", data);
if (!data.method_id || !data.code) {
return this.createErrorResponse("Method ID and code are required");
}
if (this.currentState.status !== "valid" || !this.currentState.user) {
return this.createErrorResponse("Not authenticated", "401", 401);
}
const method = this.mockMfaMethods.find((m) => m.id === data.method_id);
if (method) {
method.verified = true;
method.last_used_at = (/* @__PURE__ */ new Date()).toISOString();
} else {
return this.createErrorResponse("MFA method not found", "404", 404);
}
const backupCodes = Array.from(
{ length: 10 },
(_, i) => `MOCK-${i.toString().padStart(4, "0")}`
);
return this.createSuccessResponse({ codes: backupCodes });
}
};
export {
AuthavaClient,
MockAuthavaClient
};
//# sourceMappingURL=index.js.map