Hoppa till huvudinnehållet
SamInCodes — till toppen
Alla projekt

Fallstudie · Backend-mikrotjänst

Auth Service

En tjänst för inloggning, så att mina appar inte gör säkerhetsfel på var sitt sätt

En fristående tjänst för autentisering – registrering och inloggning, inloggning med GitHub och Google, roller, verifiering av e-post och återställning av lösenord – som andra appar kan lämna över allt det till. Att bygga den lärde mig mest om de delar som ser färdiga ut men inte är det.

Roll
Eget projekt: API-design, säkerhet, tester och driftsättning
När
2026
Teknik
  • FastAPI
  • PostgreSQL
  • Redis
  • JWT
  • OAuth2
  • Docker
  • pytest
  • structlog
Auth Service

01.Problemet

Varje app jag byggt hade sin egen inloggningskod, lite olika varje gång och var och en ytterligare ett ställe för säkerhetsbuggar. Jag ville ha en enda tjänst som äger lösenord, token och OAuth, så att en app bara behöver ställa en fråga: är den här token giltig, och vems är den?

Den frågan är GET /auth/validate. Allt annat – hur token utfärdas, roteras, spärras och begränsas – finns på ett ställe där det kan testas ordentligt.

02.Viktiga beslut

  1. 01Kortlivade åtkomsttoken, roterande refresh-token

    Åtkomsttoken är JWT som går ut efter 15 minuter. Refresh-token är slumpade strängar som bara sparas som SHA-256-hashar, så en kopia av databasen ger ingen tillgång till sessioner. Varje förnyelse spärrar den gamla token och utfärdar ett nytt par, och en återställning av lösenordet avslutar alla användarens sessioner.

    Avvägning: Korta åtkomsttoken gör att en stulen token inte är värd mycket, men apparna förnyar ofta, så förnyelsen måste vara snabb och pålitlig.

    Auth Service-dashboarden: profil med roller, den aktuella sessionen med nedräkning till att åtkomsttoken går ut, och tjänstens status
    Dashboarden i det inbyggda webbgränssnittet: roller, nedräkning för åtkomsttoken på 15 minuter, rotering av token och status för databasen och Redis.
  2. 02Redis för allt som ska gå ut

    Allt med en livslängd ligger i Redis med en utgångstid: räknaren för misslyckade inloggningar (fem misslyckanden låser kontot i 15 minuter), begränsningar per IP-adress för inloggning, registrering och lösenordsåterställning, engångstoken som skyddar inloggningen med GitHub och Google mot CSRF, och en spärrlista för åtkomsttoken som spärrats innan de gått ut.

  3. 03Gör det inte lätt att ta reda på vem som har ett konto

    Glömt lösenord och ny verifieringslänk svarar likadant oavsett om e-postadressen finns, och en misslyckad inloggning säger ”fel e-post eller lösenord” i båda fallen. Token för återställning och verifiering sparas hashade och fungerar exakt en gång.

    Avvägning: Registreringen säger fortfarande när en e-postadress redan finns: den vanliga kompromissen mellan integritet och ett tydligt felmeddelande.

  4. 04Roller som ett beroende, inte utspridda kontroller

    Användare har roller (admin, moderator, användare) som skapas av en migrering. En route anger vilken roll den kräver med ett require_role()-beroende, så kontrollen står bredvid routen i stället för inuti den. Administratörer hanterar användare och roller via API:t, och det lilla webbgränssnittet som följer med tjänsten har en adminvy ovanpå det.

    Tjänsten går också att följa från början: varje svar har ett X-Request-ID som kopplas till de strukturerade loggarna, viktiga händelser skriver granskningsposter, svar som begränsats säger när man kan försöka igen, och /health kontrollerar PostgreSQL och Redis och svarar 503 om någon av dem ligger nere.

    Adminvyn i Auth Service: statistik över användare och en sökbar användarlista med roller som kan slås på och av
    Adminvyn, bara för administratörer: sök användare, slå på och av roller och inaktivera konton.

03.Resultat

  • 75 tester och ett krav på 70 % testtäckning i CI, plus en Docker-image som byggs vid varje push
  • Driftsatt på Railway med PostgreSQL och Redis, med ett webbgränssnitt utan beroenden där man kan prova varje flöde
  • För en app räcker två anrop: validera en token och förnya en token

04.Vad jag skulle göra annorlunda

  • Testa mot samma databas som i produktion

    Testerna körs mot SQLite i minnet eftersom det går snabbt. Första driftsättningen mot riktig PostgreSQL misslyckades: migreringen som skapar standardrollerna skrev text till en UUID-kolumn, vilket SQLite tyst accepterar. I DeskFlow testade jag mot PostgreSQL från början, och det skulle jag göra här också.

  • Signera token med ett nyckelpar

    Token signeras med en delad hemlighet (HS256), så andra appar kan inte kontrollera en token själva: varje förfrågan kostar ett anrop till /auth/validate. Att signera med en privat nyckel (RS256) och publicera den publika nyckeln skulle låta apparna verifiera token lokalt och bara anropa tjänsten för att förnya.

  • Behandla en återanvänd refresh-token som ett angrepp

    En refresh-token som redan har använts avvisas, men det avslutar inte resten av sessionen, vilket är standardförsvaret när en token har stulits. På samma sätt spärrar ”logga ut överallt” alla refresh-token men stänger bara av den aktuella enhetens åtkomsttoken direkt; andra enheter fortsätter fungera tills deras token går ut, upp till 15 minuter senare. En tidsstämpel per användare för när token börjar gälla skulle avsluta dem direkt.

  • Flytta en riktig app till den

    Den byggdes för ApplyLuma och Rostid, men båda använder fortfarande sin egen inloggningskod. Att flytta över en av dem är det riktiga testet av API:t, och nästa steg.