2 Commits

8 changed files with 57 additions and 40 deletions
+18 -7
View File
@@ -71,10 +71,10 @@ export async function checkAuth(token: string): Promise<CheckAuthResponse> {
}
/**
* Logs in a user with username and password
* Logs in a user with username and password (step-based auth).
*/
export async function login(request: LoginRequest): Promise<LoginResponse> {
const res = await fetch(`${API_BASE_URL}/login`, {
const res = await fetch(`${API_BASE_URL}/auth/steps/password`, {
method: "POST",
headers: getAuthHeaders(null, true),
body: JSON.stringify(request)
@@ -83,17 +83,28 @@ export async function login(request: LoginRequest): Promise<LoginResponse> {
const error = await res.json().catch(() => ({ detail: "Login failed" }));
throw new Error(error.detail || "Login failed");
}
return await res.json();
const data = await res.json();
if (data.status === "needs_register") {
throw new Error("Account not found");
}
return data;
}
/**
* Registers a new user
* Registers a new user (step-based auth; Yandex proof required when enabled on server).
*/
export async function register(request: RegisterRequest): Promise<LoginResponse> {
const res = await fetch(`${API_BASE_URL}/register`, {
export async function register(request: RegisterRequest & { registration_proof?: string }): Promise<LoginResponse> {
const res = await fetch(`${API_BASE_URL}/auth/steps/register/confirm`, {
method: "POST",
headers: getAuthHeaders(null, true),
body: JSON.stringify(request)
body: JSON.stringify({
display_name: request.display_name,
username: request.username,
password: request.password,
confirm_password: request.confirm_password,
bio: request.bio,
registration_proof: request.registration_proof,
})
});
if (!res.ok) {
const error = await res.json().catch(() => ({ detail: "Registration failed" }));
+2 -2
View File
@@ -5,7 +5,7 @@ import { request } from "@/core/websocket";
import type { DmEnvelope, User } from "@/core/types";
import { ub64 } from "@/utils/utils";
import { fetchUserPublicKey } from "../crypto/identity";
import { fetchUsers, searchUsers } from "../user/search";
import { searchUsers } from "../user/search";
import { deriveWrappingKey, importAesGcmKey, aesGcmDecrypt } from "@fromchat/protocol";
import tweetnacl from "tweetnacl";
@@ -320,4 +320,4 @@ export async function editMessage(
}
// Re-export user functions for convenience
export { fetchUsers, searchUsers, fetchUserPublicKey };
export { searchUsers, fetchUserPublicKey };
+2 -2
View File
@@ -3,7 +3,7 @@ import { getAuthHeaders } from "./account";
import { request } from "@/core/websocket";
import type { DmEnvelope, User } from "@/core/types";
import { fetchUserPublicKey } from "./crypto";
import { fetchUsers, searchUsers } from "./users";
import { searchUsers } from "./users";
/**
* Decrypt a DM envelope using client-side MEK unwrapping.
@@ -25,7 +25,7 @@ export async function fetchDMHistory(userId: number, token: string, limit: numbe
}
// Re-export user functions for convenience
export { fetchUsers, searchUsers, fetchUserPublicKey };
export { searchUsers, fetchUserPublicKey };
/**
* Send DM via WebSocket using transport encryption.
+2 -2
View File
@@ -3,7 +3,7 @@ import { getAuthHeaders } from "./account";
import { request } from "@/core/websocket";
import type { DmEnvelope, User } from "@/core/types";
import { fetchUserPublicKey } from "./crypto";
import { fetchUsers, searchUsers } from "./users";
import { searchUsers } from "./users";
export async function decryptDm(envelope: DmEnvelope): Promise<string> {
const { decrypt } = await import("./chats/dm");
@@ -20,7 +20,7 @@ export async function fetchDMHistory(userId: number, token: string, limit: numbe
}
// Re-export user functions for convenience
export { fetchUsers, searchUsers, fetchUserPublicKey };
export { searchUsers, fetchUserPublicKey };
export async function sendDMViaWebSocket(recipientId: number, recipientPublicKeyB64: string, plaintext: string, authToken: string, replyToId?: number): Promise<void> {
const { send } = await import("./chats/dm");
+32 -7
View File
@@ -70,11 +70,25 @@ export async function checkAuth(token: string): Promise<CheckAuthResponse> {
return await res.json();
}
export interface RegisterConfirmRequest {
display_name: string;
username: string;
password: string;
confirm_password: string;
bio?: string;
registration_proof?: string;
}
export interface AuthPasswordNeedsRegister {
status: "needs_register";
yandex_required: boolean;
}
/**
* Logs in a user with username and password
* Logs in a user with username and password (step-based auth).
*/
export async function login(request: LoginRequest): Promise<LoginResponse> {
const res = await fetch(`${API_BASE_URL}/login`, {
const res = await fetch(`${API_BASE_URL}/auth/steps/password`, {
method: "POST",
headers: getAuthHeaders(null, true),
body: JSON.stringify(request)
@@ -83,17 +97,28 @@ export async function login(request: LoginRequest): Promise<LoginResponse> {
const error = await res.json().catch(() => ({ detail: "Login failed" }));
throw new Error(error.detail || "Login failed");
}
return await res.json();
const data = await res.json();
if (data.status === "needs_register") {
throw new Error("Account not found");
}
return data;
}
/**
* Registers a new user
* Registers a new user (step-based auth; Yandex proof required when enabled on server).
*/
export async function register(request: RegisterRequest): Promise<LoginResponse> {
const res = await fetch(`${API_BASE_URL}/register`, {
export async function register(request: RegisterRequest & { registration_proof?: string }): Promise<LoginResponse> {
const res = await fetch(`${API_BASE_URL}/auth/steps/register/confirm`, {
method: "POST",
headers: getAuthHeaders(null, true),
body: JSON.stringify(request)
body: JSON.stringify({
display_name: request.display_name,
username: request.username,
password: request.password,
confirm_password: request.confirm_password,
bio: request.bio,
registration_proof: request.registration_proof,
})
});
if (!res.ok) {
const error = await res.json().catch(() => ({ detail: "Registration failed" }));
-10
View File
@@ -2,16 +2,6 @@ import { API_BASE_URL } from "@/core/config";
import { getAuthHeaders } from "./auth";
import type { User } from "@/core/types";
/**
* Fetches a list of all users (excluding current user)
*/
export async function fetchUsers(token: string): Promise<User[]> {
const res = await fetch(`${API_BASE_URL}/users`, { headers: getAuthHeaders(token, true) });
if (!res.ok) return [];
const data = await res.json();
return data.users || [];
}
/**
* Searches for users by username query
*/
-10
View File
@@ -2,16 +2,6 @@ import { API_BASE_URL } from "@/core/config";
import { getAuthHeaders } from "./account";
import type { User } from "@/core/types";
/**
* Fetches a list of all users (excluding current user)
*/
export async function fetchUsers(token: string): Promise<User[]> {
const res = await fetch(`${API_BASE_URL}/users`, { headers: getAuthHeaders(token, true) });
if (!res.ok) return [];
const data = await res.json();
return data.users || [];
}
/**
* Searches for users by username query
*/
+1
View File
@@ -182,6 +182,7 @@ export interface RegisterRequest {
display_name: string;
password: string;
confirm_password: string;
bio?: string;
}
export interface UploadPublicKeyRequest {