From c76b9df0be83b46b57871f939e078e78f6c647b8 Mon Sep 17 00:00:00 2001 From: Roman Abendroth Date: Fri, 10 Nov 2023 08:58:51 +0100 Subject: [PATCH 1/3] implement auth flow with session cookies --- .firebaserc | 2 +- src/hooks.server.ts | 11 +- src/routes/+layout.server.ts | 5 +- src/routes/+layout.svelte | 253 +++++++++-------- src/routes/app/+page.server.ts | 5 +- src/routes/app/create-plant/+page.server.ts | 4 +- src/routes/login/+page.server.ts | 26 +- src/routes/login/+page.svelte | 286 +++++++++----------- 8 files changed, 291 insertions(+), 301 deletions(-) diff --git a/.firebaserc b/.firebaserc index 3c0e57a..dbc8db4 100644 --- a/.firebaserc +++ b/.firebaserc @@ -1,5 +1,5 @@ { "projects": { - "default": "fir-playground-22c29" + "default": "plant-care-assistant-38fd3" } } diff --git a/src/hooks.server.ts b/src/hooks.server.ts index 55071e8..e82189f 100644 --- a/src/hooks.server.ts +++ b/src/hooks.server.ts @@ -1,24 +1,19 @@ import { redirect } from '@sveltejs/kit'; import { getAuth } from 'firebase-admin/auth'; import functions from 'firebase-functions'; -import { adminApp } from './lib/server/firebase'; +import { adminApp } from '$lib/server/firebase'; export async function handle({ resolve, event }) { event.locals.app = adminApp(); if (event.url.pathname.startsWith('/app')) { - // TODO: the client side cookie is seemingly stripped by firebase, so the idToken must be transferred another way (see login/+page.svelte). - // Using a session cookie is recommended by the docs: https://firebase.google.com/docs/auth/admin/manage-cookies?hl=en - const idToken = event.cookies.get('idToken'); - - functions.logger.info(`ID TOKEN: ${idToken}`); - + const idToken = event.cookies.get('__session'); if (!idToken) { throw redirect(302, '/login'); } try { - const decodedIdToken = await getAuth(event.locals.app).verifyIdToken(idToken); + const decodedIdToken = await getAuth(event.locals.app).verifySessionCookie(idToken, true); event.locals.uid = decodedIdToken.uid; } catch (e) { functions.logger.error(e); diff --git a/src/routes/+layout.server.ts b/src/routes/+layout.server.ts index 412aaa4..d932dfe 100644 --- a/src/routes/+layout.server.ts +++ b/src/routes/+layout.server.ts @@ -1,7 +1,6 @@ -import { redirect } from '@sveltejs/kit'; +import { redirect, type ServerLoadEvent } from '@sveltejs/kit'; - -export function load({ url }) { +export function load({ url }: ServerLoadEvent) { if (url.pathname === '/') { throw redirect(302, '/app'); } diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 1ceb714..e1b6da5 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -1,143 +1,134 @@
-
- -
- - - -
SvelteKit ♥ Firebase
+
+ +
+ + + +
SvelteKit ♥ Firebase
diff --git a/src/routes/app/+page.server.ts b/src/routes/app/+page.server.ts index 80a1b47..df88fb2 100644 --- a/src/routes/app/+page.server.ts +++ b/src/routes/app/+page.server.ts @@ -1,6 +1,7 @@ -import { PlantDto } from '../../lib/domain/Plant'; +import { PlantDto } from '$lib/domain/Plant'; +import type { ServerLoadEvent } from "@sveltejs/kit"; -export async function load({ locals }) { +export async function load({ locals }: ServerLoadEvent) { const db = locals.app.firestore(); const plantDocs = await db.collection('plants').where('owner', '==', locals.uid).get(); diff --git a/src/routes/app/create-plant/+page.server.ts b/src/routes/app/create-plant/+page.server.ts index 24abfad..e9e69c5 100644 --- a/src/routes/app/create-plant/+page.server.ts +++ b/src/routes/app/create-plant/+page.server.ts @@ -1,4 +1,4 @@ -import { error, fail, redirect } from '@sveltejs/kit'; +import { type Actions, error, fail, redirect } from '@sveltejs/kit'; import { getAuth } from 'firebase-admin/auth'; import { FieldValue, Timestamp, getFirestore } from 'firebase-admin/firestore'; import { DateTime } from 'luxon'; @@ -107,7 +107,7 @@ export const actions = { throw redirect(302, '/app'); } -}; +} satisfies Actions; function toFormattedErrors(formattedIssues: Record, issue: ZodIssue) { const field = issue.path.at(0); diff --git a/src/routes/login/+page.server.ts b/src/routes/login/+page.server.ts index f1f5c06..cbee5f2 100644 --- a/src/routes/login/+page.server.ts +++ b/src/routes/login/+page.server.ts @@ -1,7 +1,27 @@ -import { redirect } from '@sveltejs/kit'; +import { type Actions, redirect } from '@sveltejs/kit'; +import { adminApp } from "$lib/server/firebase"; export const actions = { - default() { + async login({request, cookies}) { + const formData = await request.formData(); + const idToken = formData.get('idToken'); + if (!idToken) { + throw redirect(302, '/login'); + } + + const sessionCookie = await adminApp().auth().createSessionCookie(idToken as string, {expiresIn: 60 * 60 * 24 * 5}); + cookies.set('__session', sessionCookie, { + maxAge: 60 * 60 * 24 * 5, + sameSite: 'lax', + path: '/', + secure: true, + httpOnly: true, + }); + throw redirect(302, '/app'); + }, + logout({cookies}) { + cookies.delete('__session'); + throw redirect(302, '/login'); } -}; +} satisfies Actions; diff --git a/src/routes/login/+page.svelte b/src/routes/login/+page.svelte index 3bac7b3..67efb7b 100644 --- a/src/routes/login/+page.svelte +++ b/src/routes/login/+page.svelte @@ -1,159 +1,143 @@
-
-
-
-

Sign in

-

Welcome to Plant Care Assistant

-
-
- - - {#if error.message != null} -

{error.message}

- {/if} - -
-
-
-
-

- Photo by feey - on - Unsplash -

+
+
+
+

Sign in

+

Welcome to Plant Care Assistant

+
+
{ + busy = true; + const email = formData.get("email"); + const password = formData.get("password"); + + try { + const userCred = await signInWithEmailAndPassword($firebase.auth, email, password) + const token = await userCred.user.getIdToken(); + formData.set("idToken", token); + } catch(e) { + console.error(e); + error.message = 'Invalid credentials.'; + cancel(); + } finally { + busy = false; + } + }} method="POST"> + + + {#if error.message != null} +

{error.message}

+ {/if} + +
+
+
+
+

+ Photo by feey + on + Unsplash +

From 73913e458c3788f59ac2ab63d6bf1e3c56c745b1 Mon Sep 17 00:00:00 2001 From: Roman Abendroth Date: Fri, 10 Nov 2023 10:19:40 +0100 Subject: [PATCH 2/3] display email if displayName is not set --- .gitignore | 3 ++- src/routes/app/+page.svelte | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index feb620f..8d58e7f 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,5 @@ node_modules vite.config.js.timestamp-* vite.config.ts.timestamp-* *.log -.firebase \ No newline at end of file +.firebase +.idea diff --git a/src/routes/app/+page.svelte b/src/routes/app/+page.svelte index ab81366..db26dfa 100644 --- a/src/routes/app/+page.svelte +++ b/src/routes/app/+page.svelte @@ -16,7 +16,7 @@
-

Hi {user.displayName},

+

Hi {user.displayName ?? user.email},

Take a look at your plants here!

From f1990cb7a95f9f4de0162c076fb4c49d88e61cc2 Mon Sep 17 00:00:00 2001 From: Roman Abendroth Date: Fri, 10 Nov 2023 11:32:06 +0100 Subject: [PATCH 3/3] remove google auth --- src/routes/login/+page.svelte | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/routes/login/+page.svelte b/src/routes/login/+page.svelte index 67efb7b..8586828 100644 --- a/src/routes/login/+page.svelte +++ b/src/routes/login/+page.svelte @@ -4,7 +4,6 @@ import { getContext } from 'svelte'; import { fade } from 'svelte/transition'; import type { FirebaseStore } from '../+layout.svelte'; - import { goto } from "$app/navigation"; const firebase = getContext('firebase'); @@ -13,8 +12,6 @@ }; let busy = false; - - // $: disabled = $firebase.auth == null;