mirror of
https://github.com/TheRealOwenRees/chess-scribe-website.git
synced 2026-07-23 01:46:57 +00:00
basic login / logout lichess user
This commit is contained in:
+97
-14
@@ -1,31 +1,118 @@
|
||||
import { randomBytes } from "node:crypto";
|
||||
import { redirect } from "@tanstack/react-router";
|
||||
import { createServerFn } from "@tanstack/react-start";
|
||||
import { getCookie, setCookie, deleteCookie } from "@tanstack/react-start/server";
|
||||
import { env } from "#/env.ts";
|
||||
import { base64UrlEncode, sha256 } from "#/lib/encodeDecode.ts";
|
||||
|
||||
export const getToken = createServerFn({ method: "POST" }).handler(async () => {
|
||||
console.log("getTokens");
|
||||
});
|
||||
const createVerifier = () => base64UrlEncode(randomBytes(32));
|
||||
const createChallenge = (verifier: string) => base64UrlEncode(sha256(verifier));
|
||||
|
||||
export const verifyToken = createServerFn({ method: "POST" }).handler(
|
||||
const getRedirectUri = () =>
|
||||
process.env.NODE_ENV === "production"
|
||||
? `https://${env.LICHESS_CLIENT_ID}/callback`
|
||||
: "http://localhost:3000/callback";
|
||||
|
||||
// TODO fix and check
|
||||
export const getSession = createServerFn({ method: "GET" }).handler(
|
||||
async () => {
|
||||
console.log("verifyTokens");
|
||||
const session = getCookie("lichess-session");
|
||||
if (!session) return null;
|
||||
try {
|
||||
return JSON.parse(session) as { username: string };
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
export const login = createServerFn({ method: "POST" }).handler(async () => {
|
||||
const createVerifier = () => base64UrlEncode(randomBytes(32));
|
||||
const createChallenge = (verifier: string) =>
|
||||
base64UrlEncode(sha256(verifier));
|
||||
export const setSession = createServerFn({ method: "POST" })
|
||||
.inputValidator((data: { username: string; id: string }) => data)
|
||||
.handler(async ({ data }) => {
|
||||
setCookie(
|
||||
"lichess-session",
|
||||
JSON.stringify({ username: data.username, id: data.id }),
|
||||
{
|
||||
path: "/",
|
||||
httpOnly: true,
|
||||
secure: process.env.NODE_ENV === "production",
|
||||
sameSite: "lax",
|
||||
maxAge: 60 * 60 * 24 * 7,
|
||||
},
|
||||
);
|
||||
|
||||
setCookie("lichess-oauth-verifier", "", {
|
||||
path: "/",
|
||||
httpOnly: true,
|
||||
secure: process.env.NODE_ENV === "production",
|
||||
sameSite: "lax",
|
||||
maxAge: 0,
|
||||
});
|
||||
|
||||
return { username: data.username, id: data.id };
|
||||
});
|
||||
|
||||
export const getToken = createServerFn({ method: "POST" })
|
||||
.inputValidator((data: { code: string }) => data)
|
||||
.handler(async ({ data }) => {
|
||||
const verifier = getCookie("lichess-oauth-verifier");
|
||||
|
||||
if (!verifier)
|
||||
throw new Error("OAuth session expired. Please login again.");
|
||||
|
||||
const response = await fetch("https://lichess.org/api/token", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
grant_type: "authorization_code",
|
||||
client_id: env.LICHESS_CLIENT_ID || "",
|
||||
code: data.code,
|
||||
redirect_uri: getRedirectUri(),
|
||||
code_verifier: verifier,
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok)
|
||||
throw new Error(`Token exchange failed: ${await response.text()}`);
|
||||
|
||||
return response.json();
|
||||
});
|
||||
|
||||
export const getUser = createServerFn({ method: "GET" })
|
||||
.inputValidator((data: { access_token: string }) => data)
|
||||
.handler(async ({ data }) => {
|
||||
const response = await fetch("https://lichess.org/api/account", {
|
||||
headers: {
|
||||
Authorization: `Bearer ${data.access_token}`,
|
||||
},
|
||||
});
|
||||
|
||||
return response.json();
|
||||
});
|
||||
|
||||
export const logout = createServerFn().handler(async () => {
|
||||
deleteCookie("lichess-session", { path: "/" });
|
||||
deleteCookie("lichess-oauth-verifier", { path: "/" });
|
||||
});
|
||||
|
||||
export const login = createServerFn({ method: "GET" }).handler(async () => {
|
||||
const verifier = createVerifier();
|
||||
const challenge = createChallenge(verifier);
|
||||
|
||||
setCookie("lichess-oauth-verifier", verifier, {
|
||||
path: "/",
|
||||
httpOnly: true,
|
||||
secure: process.env.NODE_ENV === "production",
|
||||
sameSite: "lax",
|
||||
maxAge: 60 * 10,
|
||||
});
|
||||
|
||||
const params = new URLSearchParams({
|
||||
response_type: "code",
|
||||
client_id: env.LICHESS_CLIENT_ID || "",
|
||||
redirect_uri: `${process.env.NODE_ENV === "production" ? `https://${env.LICHESS_CLIENT_ID}/callback` : "http://localhost:3000/callback"}`,
|
||||
redirect_uri: getRedirectUri(),
|
||||
scope: "study:read",
|
||||
code_challenge_method: "S256",
|
||||
code_challenge: challenge,
|
||||
@@ -33,9 +120,5 @@ export const login = createServerFn({ method: "POST" }).handler(async () => {
|
||||
|
||||
throw redirect({
|
||||
href: `https://lichess.org/oauth?${params.toString()}`,
|
||||
statusCode: 302,
|
||||
headers: {
|
||||
"Set-Cookie": `lichess_oauth_verifier=${verifier}; Path=/; HttpOnly; Secure; SameSite=Lax; Max-Age=600`, // Expires in 10 mins
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user