Hoppa till huvudinnehåll

Autentisering och Sessions 🔐

Ditt uppdrag 🎯

Lägg till användarsystem i din befintliga app:

  • Registrering av nya användare
  • Inloggning med användarnamn/lösenord
  • Hålla koll på vem som är inloggad
  • Skydda sidor så att bara inloggade användare kommer åt dem

Viktigt: Du får INTE bara kopiera lösningar! Du ska förstå VARFÖR säkerhet fungerar som den gör.

Från arrays till riktig säkerhet! 💪

Hittills har vi sparat data utan att tänka på vem som äger vad. Nu lär vi oss att identifiera användare och skydda deras data!

Fas 1: Förstå autentiseringsproblem genom experiment 🧠

Steg 1: Säkerhetsexperiment

Din uppgift 1: Testa dessa scenarios och reflektera

// Scenario 1: Vad händer om vi bara sparar username i cookie?
document.cookie = "username=alice";

// Scenario 2: Vad händer om någon ändrar cookie i browser?
// Öppna Developer Tools → Application → Cookies
// Ändra username till "admin" - vad kan gå fel?

// Scenario 3: Vad händer när browser stängs?
// Stäng och öppna browser - finns cookie kvar?

Reflektera:

  • Vilka säkerhetsproblem ser du med att bara spara username?
  • Hur skulle en hacker kunna utnyttja detta?
  • Vad händer om två personer använder samma dator?

Steg 2: Designa ditt system innan du kodar

Din uppgift 2: Svara på dessa frågor först

# Min autentiseringsdesign

## 1. Användardata - vad behöver vi spara?
- [ ] Username (måste vara unikt?)
- [ ] Password (i klartext eller krypterat?)
- [ ] Email (obligatorisk?)
- [ ] Registreringsdatum?
- [ ] Annat?

## 2. Session-hantering - hur vet vi vem som är inloggad?
- [ ] Spara user ID i cookie?
- [ ] Spara username i cookie?
- [ ] Något säkrare?
- [ ] Hur länge ska sessions vara aktiva?

## 3. Sidor som behöver skydd
Vilka sidor i min app ska bara inloggade se?
- [ ] /dashboard
- [ ] /profile
- [ ] /create-[något]
- [ ] Andra?

## 4. Användarflöden
Rita/beskriv:
- Vad händer när någon försöker registrera sig?
- Vad händer när någon loggar in?
- Vad händer när någon försöker komma åt skyddad sida?

Fas 2: Bygg User-modell steg för steg 👤

Steg 1: Experimentera med datamodell

Din uppgift 3: Först, tänk igenom problemen

// Vad är fel med denna User-modell?
model User {
id String @id @default(uuid())
username String
password String
email String
createdAt DateTime @default(now())
}

// Frågor att lösa:
// 1. Vad händer om två användare vill ha samma username?
// 2. Vad händer om två användare har samma email?
// 3. Är det säkert att spara password direkt?
// 4. Vilka constraints behöver vi?

Din uppgift 4: Fixa modellen själv

// schema.prisma - Din förbättrade version
model User {
id String @id @default(uuid())
username String @/* Vad ska stå här för att förhindra duplicates? */
password String // Detta kommer vi ändra senare för säkerhet!
email String? @/* Email är optional men måste vara unique om den finns */
createdAt DateTime @default(now())

// Hur kopplar du User till dina befintliga modeller?
// Exempel: Forum skapas av User, Message skrivs av User
// forums Forum[] @relation("ForumCreatedBy")
// messages Message[] @relation("MessageAuthor")
}

// Din uppgift: Uppdatera dina befintliga modeller
// Exempel för Forum:
model Forum {
// Befintliga fält...

// Lägg till relation till User
createdBy User @relation("ForumCreatedBy", fields: [/* vad? */], references: [/* vad? */])
createdById String
}

Testa din modell:

npx prisma db push
npx prisma studio

Experimentera: Försök skapa användare med duplicated username - vad händer?

Fas 3: Bygg registrering med guided discovery 📝

Steg 1: Planera registreringsflödet

Din uppgift 5: Tänk igenom innan du kodar

Registreringsflöde:
1. Användare fyller i formulär (username, password, email?)
2. Servern validerar data (vad ska valideras?)
3. Servern sparar användare (med krypterat lösenord - kommer senare)
4. Vad händer sedan? (logga in direkt? skicka till login?)

Steg 2: Skapa login-sidan med utmaningar

Din uppgift 6: Skapa src/routes/login/+page.svelte

<script>
export let form; // För att visa fel-meddelanden från servern
</script>

<h1>Login</h1>

<!-- Login-formulär -->
<!-- Din uppgift: Skapa formuläret -->
<form method="POST" action="?/login">
<div>
<label for="username">Användarnamn:</label>
<!-- Din input här - vilka attribut behöver den? -->
<input
id="username"
type="/* vilken typ? */"
name="/* vad ska servern få? */"
required
placeholder="/* hjälptext för användaren */"
/>
</div>

<!-- Din uppgift: Lägg till password-fält på samma sätt -->


<button type="submit">Logga in</button>
</form>

<h2>Ny användare?</h2>

<!-- Register-formulär -->
<!-- Din uppgift: Skapa register-formuläret -->
<!-- Tips: använd action="?/register" -->


<!-- Visa fel-meddelanden om de finns -->
{#if form?.error}
<div class="error">
<!-- Hur visar du felmeddelandet? -->
</div>
{/if}

<style>
/* Din uppgift: Lägg till styling */
/* Tips: formulär behöver struktur och spacing */

</style>

Steg 3: Server-logik med progressiva utmaningar

Din uppgift 7: Skapa src/routes/login/+page.server.ts

import type { Actions } from './$types';
import { prisma } from '$lib';
import { fail, redirect } from '@sveltejs/kit';

export const actions: Actions = {
register: async ({ request, cookies }) => {
// Din uppgift: Få data från formuläret
const data = await request.formData();
const username = /* Hur får du username från formData? */;
const password = /* Samma för password */;

// Din uppgift: Validering - vad ska du kolla?
if (/* username är tomt eller undefined */) {
return fail(400, { error: 'Användarnamn krävs' });
}

// Lägg till fler valideringar:
// - Password för kort?
// - Username för kort?
// - Ogiltiga tecken?

// Din uppgift: Kolla om användaren redan finns
const existingUser = await prisma.user.findUnique({
where: { /* vad letar vi efter? */ }
});

if (existingUser) {
// Vad ska hända? Returnera fail() med lämpligt meddelande
}

// Din uppgift: Skapa användare
// VARNING: Detta sparar lösenord i klartext (osäkert!)
// Vi kommer fixa detta i senare modul
try {
const newUser = await prisma.user.create({
data: {
// Vad ska sparas?
}
});

// Din uppgift: Logga in användaren direkt
// För nu: spara user ID i cookie (enkelt men inte säkrast)
cookies.set('userId', /* vad? */, {
path: '/',
maxAge: /* hur länge? sekunder för en vecka? */,
secure: false, // true i production
httpOnly: true
});

// Vart ska användaren skickas efter registrering?
throw redirect(307, '/dashboard');

} catch (error) {
// Vad kan gå fel här? Hur hanterar du det?

}
},

login: async ({ request, cookies }) => {
// Din uppgift: Implementera login-logiken
// 1. Få username och password från formData
// 2. Hitta användare i databas
// 3. Jämför lösenord (för nu: direkt jämförelse)
// 4. Om korrekt: sätt cookie och redirect
// 5. Om fel: returnera error

// Din implementation här:

}
};

Debugging-utmaning: Testa att skapa användare och kolla i Prisma Studio. Sparas data som förväntat?

Fas 4: Skydda sidor med auth-kontroll 🛡️

Steg 1: Förstå vad som behöver skyddas

Din uppgift 8: Analysera din befintliga app

# Auth-analys för min app

## Publika sidor (alla får se):
- [ ] Hemsida/landing page
- [ ] Login/register
- [ ] Om-sida
- [ ] ?

## Skyddade sidor (bara inloggade):
- [ ] Dashboard
- [ ] Profil
- [ ] Skapa innehåll (forum, items, etc.)
- [ ] ?

## Semi-skyddade (olika för inloggade vs ej):
- [ ] Forum (alla kan läsa, bara inloggade kan skriva?)
- [ ] Marknadsplats (alla kan kolla, bara inloggade kan köpa?)
- [ ] ?

Steg 2: Skapa auth-hjälpfunktioner

Din uppgift 9: Skapa src/lib/auth.ts

import { prisma } from '$lib';
import { redirect } from '@sveltejs/kit';

// Din uppgift: Implementera denna funktion
export async function requireAuth(cookies: any) {
// 1. Få userId från cookies
const userId = /* hur får du cookie-värdet? */;

// 2. Om ingen cookie: redirect till login
if (!userId) {
// Hur gör du redirect?
}

// 3. Hitta användare i databas
const user = await prisma.user.findUnique({
where: { /* vad? */ }
});

// 4. Om användare inte finns: rensa cookie och redirect
if (!user) {
// Hur tar du bort en cookie?
cookies.delete('userId', { path: '/' });
// Redirect till login
}

// 5. Returnera användaren
return user;
}

// Bonus: Skapa en "optional auth" funktion
export async function getUser(cookies: any) {
// Din uppgift: Som requireAuth men utan redirect
// Returnera user eller null

}

Steg 3: Skydda specifika sidor

Din uppgift 10: Välj en befintlig sida att skydda

// Exempel: src/routes/dashboard/+page.server.ts
import { requireAuth } from '$lib/auth';
import type { PageServerLoad } from './$types';

export const load = (async ({ cookies }) => {
// Din uppgift: Använd requireAuth för att skydda sidan
const user = /* anropa rätt funktion */;

// Nu vet du att användaren är inloggad!
// Ladda användarspecifik data

return {
user: user,
// Annan data för dashboard
};
}) satisfies PageServerLoad;

Testa: Försök komma åt skyddad sida utan att vara inloggad. Vad händer?

Fas 5: Integrera auth i din app 🔗

Steg 1: Navigation med auth-status

Din uppgift 11: Uppdatera din layout för att visa inloggningsstatus

<!-- src/routes/+layout.svelte -->
<script>
export let data; // Kommer från +layout.server.ts
</script>

<nav class="main-nav">
<div class="nav-left">
<a href="/">Hem</a>
<!-- Din uppgift: Visa olika navigation beroende på om användaren är inloggad -->
{#if /* när ska dessa visas? */}
<a href="/dashboard">Dashboard</a>
<a href="/profile">Profil</a>
{/if}
</div>

<div class="nav-right">
<!-- Din uppgift: Implementera conditional navigation -->
{#if /* användare är inloggad */}
<span>Välkommen, {/* visa username */}!</span>
<!-- Logout-knapp som anropar logout action -->
<form method="POST" action="/login?/logout" style="display: inline;">
<button type="submit">Logga ut</button>
</form>
{:else}
<!-- Länk till login för ej inloggade -->

{/if}
</div>
</nav>

<main>
<slot />
</main>

<style>
/* Din styling här */
</style>

Steg 2: Layout server för global auth

Din uppgift 12: Skapa src/routes/+layout.server.ts

import { getUser } from '$lib/auth';
import type { LayoutServerLoad } from './$types';

export const load = (async ({ cookies }) => {
// Din uppgift: Använd getUser för att få auth-status
// (Inte requireAuth - det skulle redirecta på publika sidor!)

const user = /* din kod här */;

return {
user: user // Nu tillgängligt i alla komponenter via data.user
};
}) satisfies LayoutServerLoad;

Steg 3: Ägarskap på data

Din uppgift 13: Koppla innehåll till inloggade användare

// Exempel: I din create-action för forum/items/etc.
export const actions = {
create: async ({ request, cookies }) => {
// Din uppgift: Kräv auth för att skapa innehåll
const user = await requireAuth(cookies);

const data = await request.formData();
const name = data.get('name')?.toString();

// Validering...

// Din uppgift: Koppla det skapade innehållet till användaren
await prisma.forum.create({
data: {
name: name,
description: description,
createdById: /* vad? */
}
});

return { success: true };
}
};

Fas 6: Logout och förbättringar 🚪

Steg 1: Implementera logout

Din uppgift 14: Lägg till logout i login-actions

// I src/routes/login/+page.server.ts
export const actions: Actions = {
// Befintliga register och login actions...

logout: async ({ cookies }) => {
// Din uppgift: Implementera logout
// 1. Ta bort userId cookie
// 2. Redirect till lämplig sida

}
};

Steg 2: Förbättra användarupplevelse

Din uppgift 15: Lägg till progressive enhancement

<!-- I ditt login-formulär -->
<script>
import { enhance } from '$app/forms';
export let form;

let loading = false;
</script>

<form
method="POST"
action="?/login"
use:enhance={() => {
// Din uppgift: Vad ska hända när form submits?
loading = /* ? */;
return async ({ result, update }) => {
loading = /* ? */;
await update();
};
}}
>
<!-- Dina inputs... -->

<button type="submit" disabled={/* när? */}>
{/* Visa "Loggar in..." eller "Logga in" */}
</button>
</form>

Reflektion och debugging 🤔

Debugging-utmaningar

Din uppgift 16: Lös dessa vanliga problem

// Problem 1: Infinite redirect loops
// Symptom: Sidan laddar om konstant
// Debug: Var anropar du requireAuth? På publika sidor?

// Problem 2: Cookies försvinner
// Symptom: Användare loggas ut konstant
// Debug: Kolla cookie-inställningar (path, secure, maxAge)

// Problem 3: "Cannot read property of undefined"
// Symptom: Fel när du försöker komma åt data.user
// Debug: Kontrollera att +layout.server.ts returnerar user

Säkerhetsdiskussion

Reflektera över dessa frågor:

  1. Är det säkert att spara lösenord i klartext? (Spoiler: NEJ!)
  2. Vad händer om någon stjäl en cookie?
  3. Hur länge ska sessions vara aktiva?
  4. Vad händer om databas-kraschar under registrering?
Säkerhetsvarning! 🚨

Detta system sparar lösenord i KLARTEXT vilket är EXTREMT OSÄKERT för riktiga applikationer. I nästa modul kommer vi att kryptera lösenord ordentligt!

Testning och validering ✅

Din uppgift 17: Genomför detta test-scenario

# Auth Testing Checklist

## Registrering:
- [ ] Skapa användare med giltiga uppgifter
- [ ] Försök skapa användare med samma username (ska ge fel)
- [ ] Testa tomma fält (ska ge fel)
- [ ] Kontrollera i Prisma Studio att data sparas

## Inloggning:
- [ ] Logga in med rätt uppgifter
- [ ] Försök logga in med fel lösenord
- [ ] Försök logga in med icke-existerande användare

## Navigation:
- [ ] Kontrollera att navigation ändras för inloggade
- [ ] Testa att skyddade sidor kräver inloggning
- [ ] Verifiera att logout fungerar

## Edge cases:
- [ ] Ta bort cookies manuellt - vad händer?
- [ ] Försök komma åt /dashboard utan inloggning
- [ ] Logga in i en tab, försök komma åt skyddad sida i annan tab

Nästa steg 🚀

Du har nu grundläggande autentisering! I nästa modul kommer vi att:

  • Kryptera lösenord säkert med bcrypt/scrypt
  • Implementera säkra session tokens
  • Skydda mot vanliga attacker
  • Bygga production-ready säkerhet

Reflektion: Vad var svårast att förstå? Vilka säkerhetsproblem ser du med nuvarande lösning?

Du har byggt ditt första auth-system! 🔐

Även om det inte är production-ready än, förstår du nu grunderna i användarautentisering och session-hantering!