add a google registration call back site

This commit is contained in:
2025-03-15 11:02:39 +01:00
parent ccb377e6f6
commit 34bacf8b93
3 changed files with 267 additions and 81 deletions

View File

@@ -0,0 +1,177 @@
import { google } from "$lib/server/oauth";
import { ObjectParser } from "@pilcrowjs/object-parser";
import { createUser, getUserFromGoogleId } from "$lib/server/user";
import { DB } from "$lib/server/db";
import { Date as neoDate } from 'neo4j-driver';
import { createSession, generateSessionToken, setSessionTokenCookie } from "$lib/server/session";
import { decodeIdToken } from "arctic";
import { missing_field, last_name, first_name, mothers_first_name, mothers_last_name, born, failed_to_create_user } from "$lib/paraglide/messages";
import type { PageServerLoad, Actions, RequestEvent, PageData } from "./$types";
import type { OAuth2Tokens } from "arctic";
import type { PersonProperties } from '$lib/model';
import { error, redirect, fail } from "@sveltejs/kit";
const StorageLimit = 200 * 1024 * 1024;
export const load: PageServerLoad = async (event: RequestEvent) => {
const storedState = event.cookies.get("google_oauth_state") ?? null;
const codeVerifier = event.cookies.get("google_code_verifier") ?? null;
const code = event.url.searchParams.get("code");
const state = event.url.searchParams.get("state");
if (storedState === null || codeVerifier === null || code === null || state === null) {
return error(400, { message: "Please restart the process." })
}
if (storedState !== state) {
return error(400, { message: "Please restart the process." })
}
let tokens: OAuth2Tokens;
try {
tokens = await google.validateAuthorizationCode(code, codeVerifier);
} catch (e) {
return error(400, { message: "Failed to validate authorization code with " + e });
}
// if (!event.platform || !event.platform.env || !event.platform.env.GH_SESSIONS) {
// return error(500, { message: "Server configuration error. GH_SESSIONS KeyValue store missing" });
// }
const claims = decodeIdToken(tokens.idToken());
const claimsParser = new ObjectParser(claims);
const sub = claimsParser.getString("sub");
const family_name = claimsParser.getString("family_name");
const first_name = claimsParser.getString("given_name");
const email = claimsParser.getString("email");
const dbSession = DB.session();
const existingUser = await getUserFromGoogleId(dbSession, sub);
dbSession.close();
let eUser = existingUser.records.pop();
if (eUser !== null && eUser?.get('elementId') !== undefined) {
const sessionToken = generateSessionToken();
// const session = await createSession(sessionToken, eUser.get('elementId'), event.platform.env.GH_SESSIONS);
// setSessionTokenCookie(event, sessionToken, session.expiresAt);
return redirect(302, "/");
}
let personP: PersonProperties = {
google_id: sub,
first_name: first_name,
last_name: family_name,
email: email,
allow_admin_access: false,
limit: StorageLimit,
verified: false,
};
return {
props: personP
};
}
export const actions: Actions = {
register: register
};
async function register(event: RequestEvent) {
const data = await event.request.formData();
// if (!event.platform || !event.platform.env || !event.platform.env.GH_SESSIONS) {
// return fail(500, { message: "Server configuration error. GH_SESSIONS KeyValue store missing" });
// }
const google_id = data.get('google_id')
if (google_id === null) {
return fail(400, {
message: missing_field({
field: "google_id"
})
});
}
const first_name_f = data.get('first_name')
if (first_name_f === null) {
return fail(400, {
message: missing_field({
field: first_name()
})
});
}
const last_name_f = data.get('last_name')
if (last_name_f === null) {
return fail(400, {
message:
missing_field({
field: last_name()
})
});
}
const email = data.get('email')
if (email === null) {
return fail(400, {
message:
missing_field({
field: "Email"
})
});
}
const birth_date = data.get('birth_date');
if (birth_date === null) {
return fail(400, {
message:
missing_field({
field: born()
})
});
}
const mothers_first_name_f = data.get('mothers_first_name');
if (mothers_first_name_f === null) {
return fail(400, {
message:
missing_field({
field: mothers_first_name()
})
});
}
const mothers_last_name_f = data.get('mothers_last_name');
if (mothers_last_name_f === null) {
return fail(400, {
message:
missing_field({
field: mothers_last_name()
})
});
}
const parsed_date = new Date(birth_date as string);
let personP: PersonProperties = {
google_id: google_id as string,
first_name: first_name_f as string,
last_name: last_name_f as string,
email: email as string,
born: new neoDate(parsed_date.getFullYear(), parsed_date.getUTCMonth(), parsed_date.getUTCDate()),
mothers_first_name: mothers_first_name_f as string,
mothers_last_name: mothers_last_name_f as string,
allow_admin_access: false,
limit: StorageLimit,
verified: false,
};
const dbSession = DB.session();
const user = (await createUser(dbSession, personP)).records.pop();
if (user === null || user === undefined) {
dbSession.close();
return fail(500, { message: failed_to_create_user() });
}
dbSession.close();
const sessionToken = generateSessionToken();
// const session = await createSession(sessionToken, user.get('elementId'), event.platform.env.GH_SESSIONS);
// setSessionTokenCookie(event, sessionToken, session.expiresAt);
return redirect(302, "/");
}

View File

@@ -0,0 +1,90 @@
<script lang="ts">
import type { PageProps } from './$types';
import {
register,
title,
family_tree,
welcome,
site_intro,
born,
mothers_first_name,
mothers_last_name,
last_name,
first_name,
email,
allow_family_tree_admin_access,
} from '$lib/paraglide/messages';
import FamilyTree from '../../highresolution_icon_no_background_croped.png';
let { data, form }: PageProps = $props();
import Pikaday from 'pikaday';
let birth_date: HTMLInputElement;
$effect(() => {
if (birth_date) {
const picker = new Pikaday({
field: birth_date
});
return () => picker.destroy();
}
});
</script>
<svelte:head>
<title>{title({ page: register() })}</title>
</svelte:head>
<div class="hero bg-base-200 min-h-screen">
<div class="hero-content flex-col lg:flex-row-reverse">
<div class="text-center lg:text-left">
<figure class="top-margin-10 px-10 pt-10">
<img src={FamilyTree} alt={family_tree()} class="rounded-xl" />
</figure>
<h1 class="text-5xl font-bold">{welcome()}</h1>
<p class="py-6">
{site_intro()}
</p>
</div>
<div class="card bg-base-100 w-full max-w-sm shrink-0 shadow-2xl">
<div class="card-body">
<form method="POST" action="/register">
<fieldset class="fieldset">
{#if form?.message}
<div role="alert" class="alert alert-error">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 shrink-0 stroke-current" fill="none" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span>{form.message}</span>
</div>
{/if }
<label class="fieldset-label" for="email">Email</label>
<input type="email" class="input" placeholder="Email" value="{data.props.email}" />
<input type="text" class="hidden" id="google_id" placeholder="Google ID" value="{data.props.google_id}" />
<label class="fieldset-label" for="first_name">{first_name()}</label>
<input type="text" class="input" id="first_name" placeholder="{first_name()}" value="{data.props.first_name}" />
<label class="fieldset-label" for="last_name">{last_name()}</label>
<input type="text" class="input" id="last_name" placeholder="{last_name()}" value="{data.props.last_name}" />
<label class="fieldset-label" for="allow_admin_access">{allow_family_tree_admin_access()}</label>
<input type="checkbox" class="input" id="allow_admin_access" checked="{data.props.allow_admin_access}" />
<label class="fieldset-label" for="birth_date">{born()}</label>
<input type="text" class="input pika-single" id="birth_date" bind:this={birth_date} value={born()} />
<label class="fieldset-label" for="mothers_last_name">{mothers_last_name()}</label>
<input
type="text"
class="input"
id="mothers_last_name"
placeholder={mothers_last_name()}
/>
<label class="fieldset-label" for="mothers_first_name">{mothers_first_name()}</label>
<input
type="text"
class="input"
id="mothers_first_name"
placeholder={mothers_first_name()}
/>
<button class="btn btn-neutral mt-4">{register()}</button>
</fieldset>
</form>
</div>
</div>
</div>
</div>

View File

@@ -1,81 +0,0 @@
import { fail } from "@sveltejs/kit";
import { google } from "$lib/server/oauth";
import { ObjectParser } from "@pilcrowjs/object-parser";
import { createUser, getUserFromGoogleId } from "$lib/server/user";
import { driverInstance } from "$lib/server/db";
import { createSession, generateSessionToken, setSessionTokenCookie } from "$lib/server/session";
import { decodeIdToken } from "arctic";
import type { RequestEvent } from "./$types";
import type { OAuth2Tokens } from "arctic";
import { middle_name } from "$lib/paraglide/messages";
export async function GET(event: RequestEvent): Promise<Response> {
const storedState = event.cookies.get("google_oauth_state") ?? null;
const codeVerifier = event.cookies.get("google_code_verifier") ?? null;
const code = event.url.searchParams.get("code");
const state = event.url.searchParams.get("state");
if (storedState === null || codeVerifier === null || code === null || state === null) {
return new Response("Please restart the process.", {
status: 400
});
}
if (storedState !== state) {
return new Response("Please restart the process.", {
status: 400
});
}
let tokens: OAuth2Tokens;
try {
tokens = await google.validateAuthorizationCode(code, codeVerifier);
} catch (e) {
return new Response("Please restart the process.", {
status: 400
});
}
if (!event.platform || !event.platform.env || !event.platform.env.GH_SESSIONS) {
return new Response("Server configuration error. GH_SESSIONS KeyValue store missing", {
status: 500
});
}
const claims = decodeIdToken(tokens.idToken());
const claimsParser = new ObjectParser(claims);
const googleId = claimsParser.getString("sub");
const family_name = claimsParser.getString("family_name");
const first_name = claimsParser.getString("given_name");
const middle_name = claimsParser.getString("middle_name");
const picture = claimsParser.getString("picture");
const email = claimsParser.getString("email");
const existingUser = getUserFromGoogleId(googleId);
if (existingUser !== null) {
const sessionToken = generateSessionToken();
const session = await createSession(sessionToken, existingUser.id, event.platform.env.GH_SESSIONS);
setSessionTokenCookie(event, sessionToken, session.expiresAt);
return new Response(null, {
status: 302,
headers: {
Location: "/"
}
});
}
const dbSession = driverInstance.session()
const user = createUser(dbSession, googleId, email, first_name, family_name, middle_name);
dbSession.close();
const sessionToken = generateSessionToken();
const session = await createSession(sessionToken, user.id, event.platform.env.GH_SESSIONS);
setSessionTokenCookie(event, sessionToken, session.expiresAt);
return new Response(null, {
status: 302,
headers: {
Location: "/"
}
});
}