fix(auth): session secret'i response header'ından (X-Fallback-Cookies) oku
Sorun: - createEmailPasswordSession response body'sinde 'secret' alanı boş/redacted geliyor - Cookie'ye boş string yazılıyordu - requireUser() çağrısında account.get() 401 dönüyor → login'e geri redirect - Şifre doğru olsa bile bir döngüye giriyor Çözüm: - account.createEmailPasswordSession artık awRaw kullanıyor (Response objesi) - extractSessionFromHeaders helper'ı X-Fallback-Cookies (JSON) veya Set-Cookie header'ından gerçek session secret'i parse ediyor - Bu secret cookie'ye yazılıyor Browser SDK kaynağında 'cookieFallback' tam olarak bu mantığı kullanıyor — secret'i header'dan alıp localStorage'a yazıyor.
This commit is contained in:
+33
-3
@@ -42,7 +42,7 @@ type FetchOpts = {
|
||||
formData?: FormData;
|
||||
};
|
||||
|
||||
async function aw<T>(path: string, opts: FetchOpts = {}): Promise<T> {
|
||||
async function awRaw(path: string, opts: FetchOpts = {}): Promise<Response> {
|
||||
const url = new URL(ENDPOINT + path);
|
||||
if (opts.query) {
|
||||
for (const [k, v] of Object.entries(opts.query)) {
|
||||
@@ -86,7 +86,11 @@ async function aw<T>(path: string, opts: FetchOpts = {}): Promise<T> {
|
||||
data.type,
|
||||
);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
async function aw<T>(path: string, opts: FetchOpts = {}): Promise<T> {
|
||||
const res = await awRaw(path, opts);
|
||||
const ct = res.headers.get("content-type") ?? "";
|
||||
if (ct.includes("application/json")) {
|
||||
return (await res.json()) as T;
|
||||
@@ -94,6 +98,27 @@ async function aw<T>(path: string, opts: FetchOpts = {}): Promise<T> {
|
||||
return undefined as T;
|
||||
}
|
||||
|
||||
function extractSessionFromHeaders(res: Response): string | null {
|
||||
// Appwrite returns the session secret in X-Fallback-Cookies as JSON,
|
||||
// or in Set-Cookie header. The response body may have empty/redacted secret.
|
||||
const fallback = res.headers.get("x-fallback-cookies");
|
||||
if (fallback) {
|
||||
try {
|
||||
const parsed = JSON.parse(fallback) as Record<string, string>;
|
||||
const first = Object.values(parsed)[0];
|
||||
if (first) return first;
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
const setCookie = res.headers.get("set-cookie");
|
||||
if (setCookie) {
|
||||
const m = setCookie.match(/a_session_[^=]+=([^;]+)/);
|
||||
if (m?.[1]) return decodeURIComponent(m[1]);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// ─── Query helpers ───────────────────────────────────────────────
|
||||
|
||||
export const Q = {
|
||||
@@ -149,11 +174,16 @@ export interface AwUser {
|
||||
// ─── Account ─────────────────────────────────────────────────────
|
||||
|
||||
export const account = {
|
||||
createEmailPasswordSession(email: string, password: string) {
|
||||
return aw<AwSession>("/account/sessions/email", {
|
||||
async createEmailPasswordSession(email: string, password: string) {
|
||||
const res = await awRaw("/account/sessions/email", {
|
||||
method: "POST",
|
||||
body: { email, password },
|
||||
});
|
||||
const body = (await res.json()) as AwSession;
|
||||
// Appwrite redacts `secret` in the response body — read it from the
|
||||
// X-Fallback-Cookies header (or Set-Cookie) where the real secret lives.
|
||||
const secretFromHeader = extractSessionFromHeaders(res);
|
||||
return { ...body, secret: secretFromHeader || body.secret };
|
||||
},
|
||||
get(session: string) {
|
||||
return aw<AwUser>("/account", { session });
|
||||
|
||||
Reference in New Issue
Block a user