diff --git a/README.md b/README.md
index c8f1cd6..e1b2020 100644
--- a/README.md
+++ b/README.md
@@ -33,4 +33,8 @@ bun run dev
- [ ] Proper invite page
- [ ] Proper error page for login without invite
- [ ] Support file provider (for wg-quick)
+- [ ] Fix accessibility issues (lighthouse)
+- [ ] Add title to profile page (lighthouse)
- [ ] wg-quick scripts (maybe?)
+- [ ] Get rid of api routes (maybe?)
+- [ ] Get rid of custom result types (maybe?)
diff --git a/src/lib/server/auth.ts b/src/lib/server/auth.ts
index f7a56f9..5caf886 100644
--- a/src/lib/server/auth.ts
+++ b/src/lib/server/auth.ts
@@ -3,7 +3,7 @@ import { sha256 } from '@oslojs/crypto/sha2';
import { encodeBase32LowerCaseNoPadding, encodeHexLowerCase } from '@oslojs/encoding';
import { db } from '$lib/server/db';
import * as table from '$lib/server/db/schema';
-import type { RequestEvent } from '@sveltejs/kit';
+import type { Cookies } from '@sveltejs/kit';
import { dev } from '$app/environment';
import { env } from '$env/dynamic/private';
@@ -22,14 +22,14 @@ export async function createSession(userId: string): Promise
{
const session: table.Session = {
id: sessionId,
userId,
- expiresAt: new Date(Date.now() + DAY_IN_MS * 30)
+ expiresAt: new Date(Date.now() + DAY_IN_MS * 30),
};
await db.insert(table.sessions).values(session);
return session;
}
-export function setSessionTokenCookie(event: RequestEvent, sessionId: string, expiresAt: Date) {
- event.cookies.set(sessionCookieName, sessionId, {
+export function setSessionTokenCookie(cookies: Cookies, sessionId: string, expiresAt: Date) {
+ cookies.set(sessionCookieName, sessionId, {
path: '/',
sameSite: 'lax',
httpOnly: true,
@@ -42,16 +42,21 @@ export async function invalidateSession(sessionId: string): Promise {
await db.delete(table.sessions).where(eq(table.sessions.id, sessionId));
}
-export function deleteSessionTokenCookie(event: RequestEvent) {
- event.cookies.delete(sessionCookieName, { path: '/' });
+export function deleteSessionTokenCookie(cookies: Cookies) {
+ cookies.delete(sessionCookieName, { path: '/' });
}
export async function validateSession(sessionId: string) {
const [result] = await db
.select({
// Adjust user table here to tweak returned data
- user: { id: table.users.id, authSource: table.users.authSource, username: table.users.username, name: table.users.name },
- session: table.sessions
+ user: {
+ id: table.users.id,
+ authSource: table.users.authSource,
+ username: table.users.username,
+ name: table.users.name,
+ },
+ session: table.sessions,
})
.from(table.sessions)
.innerJoin(table.users, eq(table.sessions.userId, table.users.id))
diff --git a/src/routes/auth/+page.server.ts b/src/routes/auth/+page.server.ts
index 7f23ea4..850472c 100644
--- a/src/routes/auth/+page.server.ts
+++ b/src/routes/auth/+page.server.ts
@@ -1,14 +1,14 @@
-import { fail, redirect } from "@sveltejs/kit";
-import { invalidateSession, deleteSessionTokenCookie } from "$lib/server/auth";
-import type { Actions } from "./$types";
+import { fail, redirect } from '@sveltejs/kit';
+import { invalidateSession, deleteSessionTokenCookie } from '$lib/server/auth';
+import type { Actions } from './$types';
export const actions: Actions = {
- logout: async (event) => {
- if (event.locals.session === null) {
+ logout: async ({ locals, cookies }) => {
+ if (locals.session === null) {
return fail(401);
}
- await invalidateSession(event.locals.session.id);
- deleteSessionTokenCookie(event);
- return redirect(302, "/");
- }
+ await invalidateSession(locals.session.id);
+ deleteSessionTokenCookie(cookies);
+ redirect(302, '/');
+ },
};
diff --git a/src/routes/auth/authentik/callback/+server.ts b/src/routes/auth/authentik/callback/+server.ts
index 4a8ffd2..8519772 100644
--- a/src/routes/auth/authentik/callback/+server.ts
+++ b/src/routes/auth/authentik/callback/+server.ts
@@ -48,7 +48,7 @@ export async function GET(event: RequestEvent): Promise {
if (existingUser) {
const session = await createSession(existingUser.id);
- setSessionTokenCookie(event, session.id, session.expiresAt);
+ setSessionTokenCookie(event.cookies, session.id, session.expiresAt);
return new Response(null, {
status: 302,
headers: {
@@ -67,7 +67,7 @@ export async function GET(event: RequestEvent): Promise {
try {
await db.insert(table.users).values(user);
const session = await createSession(user.id);
- setSessionTokenCookie(event, session.id, session.expiresAt);
+ setSessionTokenCookie(event.cookies, session.id, session.expiresAt);
} catch (e) {
console.error('failed to create user', e);
return new Response(null, {
diff --git a/src/routes/auth/google/callback/+page.server.ts b/src/routes/auth/google/callback/+page.server.ts
index 57a8fed..aad3664 100644
--- a/src/routes/auth/google/callback/+page.server.ts
+++ b/src/routes/auth/google/callback/+page.server.ts
@@ -1,15 +1,17 @@
-import { error } from '@sveltejs/kit';
-import * as arctic from 'arctic';
-import { google } from '$lib/server/oauth';
-import { db } from '$lib/server/db';
-import { eq } from 'drizzle-orm';
-import * as table from '$lib/server/db/schema';
import { createSession, isValidInviteToken, setSessionTokenCookie } from '$lib/server/auth';
+import { db } from '$lib/server/db';
+import * as table from '$lib/server/db/schema';
+import { google } from '$lib/server/oauth';
+import { error, redirect } from '@sveltejs/kit';
import type { OAuth2Tokens } from 'arctic';
+import * as arctic from 'arctic';
+import { eq } from 'drizzle-orm';
import { assertGuard } from 'typia';
import type { PageServerLoad } from './$types';
-export const load: PageServerLoad = async ({ url, cookies }) => {
+export const load: PageServerLoad = async (event) => {
+ const { url, cookies } = event;
+
const code = url.searchParams.get('code');
const state = url.searchParams.get('state');
const storedState = cookies.get('google_oauth_state') ?? null;
@@ -17,15 +19,12 @@ export const load: PageServerLoad = async ({ url, cookies }) => {
if (code === null || state === null || storedState === null || codeVerifier === null) {
error(400, 'Missing url parameters');
- return;
}
const stateGeneratedToken = state.slice(0, storedState.length);
const stateInviteToken = state.slice(storedState.length);
if (stateGeneratedToken !== storedState) {
- return new Response(null, {
- status: 400,
- });
+ error(400, 'Invalid state in url');
}
let tokens: OAuth2Tokens;
@@ -34,20 +33,14 @@ export const load: PageServerLoad = async ({ url, cookies }) => {
} catch (e) {
if (e instanceof arctic.OAuth2RequestError) {
console.debug('Arctic: OAuth: invalid authorization code, credentials, or redirect URI', e);
- return new Response(null, {
- status: 400,
- });
+ throw error(400, 'Invalid authorization code');
}
if (e instanceof arctic.ArcticFetchError) {
console.debug('Arctic: failed to call `fetch()`', e);
- return new Response(null, {
- status: 400,
- });
+ error(400, 'Failed to validate authorization code');
}
-
- return new Response(null, {
- status: 500,
- });
+ console.error('Unxepcted error validating authorization code', code, e);
+ error(500);
}
const idToken = tokens.idToken();
@@ -66,22 +59,14 @@ export const load: PageServerLoad = async ({ url, cookies }) => {
if (existingUser) {
const session = await createSession(existingUser.id);
- setSessionTokenCookie(event, session.id, session.expiresAt);
- return new Response(null, {
- status: 302,
- headers: {
- Location: '/',
- },
- });
+ setSessionTokenCookie(cookies, session.id, session.expiresAt);
+ redirect(302, '/');
}
if (!isValidInviteToken(stateInviteToken)) {
const message =
stateInviteToken.length === 0 ? 'sign up with an invite link first' : 'invalid invite link';
-
- return new Response('Not Authorized: ' + message, {
- status: 403,
- });
+ error(403, 'Not Authorized: ' + message);
}
const user: table.User = {
@@ -97,12 +82,7 @@ export const load: PageServerLoad = async ({ url, cookies }) => {
const session = await createSession(user.id);
- setSessionTokenCookie(event, session.id, session.expiresAt);
+ setSessionTokenCookie(cookies, session.id, session.expiresAt);
- return new Response(null, {
- status: 302,
- headers: {
- Location: '/',
- },
- });
+ redirect(302, '/');
};
diff --git a/src/routes/auth/google/callback/+server.ts b/src/routes/auth/google/callback/+server.ts
deleted file mode 100644
index f389092..0000000
--- a/src/routes/auth/google/callback/+server.ts
+++ /dev/null
@@ -1,109 +0,0 @@
-import type { RequestHandler } from './$types';
-import * as arctic from 'arctic';
-import { google } from '$lib/server/oauth';
-import { db } from '$lib/server/db';
-import { eq } from 'drizzle-orm';
-import * as table from '$lib/server/db/schema';
-import { createSession, isValidInviteToken, setSessionTokenCookie } from '$lib/server/auth';
-import type { OAuth2Tokens } from 'arctic';
-import { assertGuard } from 'typia';
-
-export const GET: RequestHandler = async (event) => {
- const { url, cookies } = event;
- const code = url.searchParams.get('code');
- const state = url.searchParams.get('state');
- const storedState = cookies.get('google_oauth_state') ?? null;
- const codeVerifier = cookies.get('google_code_verifier') ?? null;
-
- if (code === null || state === null || storedState === null || codeVerifier === null) {
- return new Response(null, {
- status: 400,
- });
- }
-
- const stateGeneratedToken = state.slice(0, storedState.length);
- const stateInviteToken = state.slice(storedState.length);
- if (stateGeneratedToken !== storedState) {
- return new Response(null, {
- status: 400,
- });
- }
-
- let tokens: OAuth2Tokens;
- try {
- tokens = await google.validateAuthorizationCode(code, codeVerifier);
- } catch (e) {
- if (e instanceof arctic.OAuth2RequestError) {
- console.debug('Arctic: OAuth: invalid authorization code, credentials, or redirect URI', e);
- return new Response(null, {
- status: 400,
- });
- }
- if (e instanceof arctic.ArcticFetchError) {
- console.debug('Arctic: failed to call `fetch()`', e);
- return new Response(null, {
- status: 400,
- });
- }
-
- return new Response(null, {
- status: 500,
- });
- }
-
- const idToken = tokens.idToken();
- const claims = arctic.decodeIdToken(idToken);
-
- console.log('claims', claims);
-
- assertGuard<{
- sub: string;
- email: string;
- name: string;
- }>(claims);
-
- const userId = claims.sub;
- const existingUser = await db.query.users.findFirst({ where: eq(table.users.id, userId) });
-
- if (existingUser) {
- const session = await createSession(existingUser.id);
- setSessionTokenCookie(event, session.id, session.expiresAt);
- return new Response(null, {
- status: 302,
- headers: {
- Location: '/',
- },
- });
- }
-
- if (!isValidInviteToken(stateInviteToken)) {
- const message =
- stateInviteToken.length === 0 ? 'sign up with an invite link first' : 'invalid invite link';
-
- return new Response('Not Authorized: ' + message, {
- status: 403,
- });
- }
-
- const user: table.User = {
- id: userId,
- authSource: 'google',
- username: claims.email,
- name: claims.name,
- };
-
- // TODO: proper error handling, delete cookies
- await db.insert(table.users).values(user);
- console.log('created user', user, 'with invite token', stateInviteToken);
-
- const session = await createSession(user.id);
-
- setSessionTokenCookie(event, session.id, session.expiresAt);
-
- return new Response(null, {
- status: 302,
- headers: {
- Location: '/',
- },
- });
-};