+1
-1
dist/index.html
+1
-1
dist/index.html
···
5
5
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
6
6
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
7
<title>ATLast: Sync Your TikTok Follows → ATmosphere (Skylight, Bluesky, etc.)</title>
8
-
<script type="module" crossorigin src="/assets/index-WJfYS828.js"></script>
8
+
<script type="module" crossorigin src="/assets/index-DRBs5R5Z.js"></script>
9
9
<link rel="stylesheet" crossorigin href="/assets/index-Bs4vTtm8.css">
10
10
</head>
11
11
<body>
+33
-18
netlify/functions/logout.ts
+33
-18
netlify/functions/logout.ts
···
1
-
import { Handler, HandlerEvent, HandlerResponse } from '@netlify/functions';
2
-
import { userSessions } from './oauth-stores-db';
3
-
import cookie from 'cookie';
1
+
import { Handler, HandlerEvent, HandlerResponse } from "@netlify/functions";
2
+
import { userSessions } from "./oauth-stores-db";
3
+
import { getOAuthConfig } from "./oauth-config";
4
+
import cookie from "cookie";
4
5
5
-
export const handler: Handler = async (event: HandlerEvent): Promise<HandlerResponse> => {
6
+
export const handler: Handler = async (
7
+
event: HandlerEvent,
8
+
): Promise<HandlerResponse> => {
6
9
// Only allow POST for logout
7
-
if (event.httpMethod !== 'POST') {
10
+
if (event.httpMethod !== "POST") {
8
11
return {
9
12
statusCode: 405,
10
-
headers: { 'Content-Type': 'application/json' },
11
-
body: JSON.stringify({ error: 'Method not allowed' }),
13
+
headers: { "Content-Type": "application/json" },
14
+
body: JSON.stringify({ error: "Method not allowed" }),
12
15
};
13
16
}
14
17
15
18
try {
19
+
console.log("[logout] Starting logout process...");
20
+
console.log("[logout] Cookies received:", event.headers.cookie);
21
+
16
22
// Get session from cookie
17
-
const cookies = event.headers.cookie ? cookie.parse(event.headers.cookie) : {};
23
+
const cookies = event.headers.cookie
24
+
? cookie.parse(event.headers.cookie)
25
+
: {};
18
26
const sessionId = cookies.atlast_session;
27
+
console.log("[logout] Session ID from cookie:", sessionId);
19
28
20
29
if (sessionId) {
21
30
// Delete session from database
22
31
await userSessions.del(sessionId);
32
+
console.log("[logout] Deleted session from database");
23
33
}
24
34
25
-
// Clear the session cookie
26
-
const cookieFlags = 'HttpOnly; SameSite=Lax; Max-Age=0; Path=/; Secure';
35
+
// Clear the session cookie with matching flags from when it was set
36
+
const config = getOAuthConfig();
37
+
const isDev = config.clientType === "loopback";
38
+
39
+
const cookieFlags = isDev
40
+
? "HttpOnly; SameSite=Lax; Max-Age=0; Path=/"
41
+
: "HttpOnly; SameSite=Lax; Max-Age=0; Path=/; Secure";
27
42
28
43
return {
29
44
statusCode: 200,
30
45
headers: {
31
-
'Content-Type': 'application/json',
32
-
'Set-Cookie': `atlast_session=; ${cookieFlags}`,
46
+
"Content-Type": "application/json",
47
+
"Set-Cookie": `atlast_session=; ${cookieFlags}`,
33
48
},
34
49
body: JSON.stringify({ success: true }),
35
50
};
36
51
} catch (error) {
37
-
console.error('Logout error:', error);
52
+
console.error("Logout error:", error);
38
53
return {
39
54
statusCode: 500,
40
-
headers: { 'Content-Type': 'application/json' },
41
-
body: JSON.stringify({
42
-
error: 'Failed to logout',
43
-
details: error instanceof Error ? error.message : 'Unknown error'
55
+
headers: { "Content-Type": "application/json" },
56
+
body: JSON.stringify({
57
+
error: "Failed to logout",
58
+
details: error instanceof Error ? error.message : "Unknown error",
44
59
}),
45
60
};
46
61
}
47
-
};
62
+
};
+34
-23
src/hooks/useAuth.ts
+34
-23
src/hooks/useAuth.ts
···
1
-
import { useState, useEffect } from 'react';
2
-
import { apiClient } from '../lib/apiClient';
3
-
import type { AtprotoSession, AppStep } from '../types';
1
+
import { useState, useEffect } from "react";
2
+
import { apiClient } from "../lib/apiClient";
3
+
import type { AtprotoSession, AppStep } from "../types";
4
4
5
5
export function useAuth() {
6
6
const [session, setSession] = useState<AtprotoSession | null>(null);
7
-
const [currentStep, setCurrentStep] = useState<AppStep>('checking');
7
+
const [currentStep, setCurrentStep] = useState<AppStep>("checking");
8
8
const [statusMessage, setStatusMessage] = useState("");
9
9
10
10
useEffect(() => {
···
13
13
14
14
async function checkExistingSession() {
15
15
try {
16
+
console.log("[useAuth] Checking existing session...");
16
17
const params = new URLSearchParams(window.location.search);
17
-
const sessionId = params.get('session');
18
-
const error = params.get('error');
18
+
const sessionId = params.get("session");
19
+
const error = params.get("error");
19
20
20
21
if (error) {
22
+
console.log("[useAuth] Error in URL:", error);
21
23
setStatusMessage(`Login failed: ${error}`);
22
-
setCurrentStep('login');
23
-
window.history.replaceState({}, '', '/');
24
+
setCurrentStep("login");
25
+
window.history.replaceState({}, "", "/");
24
26
return;
25
27
}
26
28
27
29
// If we have a session parameter in URL, this is an OAuth callback
28
30
if (sessionId) {
29
-
setStatusMessage('Loading your session...');
30
-
31
+
console.log("[useAuth] Session ID in URL:", sessionId);
32
+
setStatusMessage("Loading your session...");
33
+
31
34
// Single call now gets both session AND profile data
32
35
const data = await apiClient.getSession();
33
36
setSession({
···
37
40
avatar: data.avatar,
38
41
description: data.description,
39
42
});
40
-
setCurrentStep('home');
43
+
setCurrentStep("home");
41
44
setStatusMessage(`Welcome back, ${data.handle}!`);
42
-
43
-
window.history.replaceState({}, '', '/');
45
+
46
+
window.history.replaceState({}, "", "/");
44
47
return;
45
48
}
46
49
47
50
// Otherwise, check if there's an existing session cookie
48
51
// Single call now gets both session AND profile data
52
+
console.log("[useAuth] Checking for existing session cookie...");
49
53
const data = await apiClient.getSession();
54
+
console.log("[useAuth] Found existing session:", data);
50
55
setSession({
51
56
did: data.did,
52
57
handle: data.handle,
···
54
59
avatar: data.avatar,
55
60
description: data.description,
56
61
});
57
-
setCurrentStep('home');
62
+
setCurrentStep("home");
58
63
setStatusMessage(`Welcome back, ${data.handle}!`);
59
64
} catch (error) {
60
-
console.error('Session check error:', error);
61
-
setCurrentStep('login');
65
+
console.log("[useAuth] No valid session found:", error);
66
+
setCurrentStep("login");
62
67
}
63
68
}
64
69
···
70
75
}
71
76
72
77
setStatusMessage("Starting authentication...");
73
-
78
+
74
79
const { url } = await apiClient.startOAuth(handle);
75
80
setStatusMessage("Redirecting to authentication...");
76
81
window.location.href = url;
···
78
83
79
84
async function logout() {
80
85
try {
81
-
setStatusMessage('Logging out...');
86
+
console.log("[useAuth] Cookies before logout:", document.cookie);
87
+
console.log("[useAuth] Starting logout...");
88
+
setStatusMessage("Logging out...");
82
89
await apiClient.logout();
90
+
console.log("[useAuth] Cookies after logout:", document.cookie);
91
+
92
+
apiClient.cache.clear(); // Clear client-side cache
93
+
console.log("[useAuth] Cache cleared");
83
94
setSession(null);
84
-
setCurrentStep('login');
85
-
setStatusMessage('Logged out successfully');
95
+
setCurrentStep("login");
96
+
setStatusMessage("Logged out successfully");
86
97
} catch (error) {
87
-
console.error('Logout error:', error);
88
-
setStatusMessage('Error logging out');
98
+
console.error("Logout error:", error);
99
+
setStatusMessage("Error logging out");
89
100
throw error;
90
101
}
91
102
}
···
99
110
login,
100
111
logout,
101
112
};
102
-
}
113
+
}