Auth-middleware verifisert og fullført (oppgave 2.2)

- Fikser audience-validering (AUTHENTIK_CLIENT_ID som forventet aud)
- Oppdaterer seed-data med reell Authentik sub for Vegard
- Fikser DATABASE_URL i .env: peker nå til synops-database (ikke sidelinja)
- Dokumenterer maskinrommet-miljøvariabler i produksjon.md
- Markerer oppgave 2.2 som ferdig i tasks.md

Verifisert på server med fem testcaser:
1. /health (public) → 200
2. /me uten token → 401 "Mangler Authorization-header"
3. /me med ugyldig token → 401 "Ugyldig token"
4. /me med gyldig JWT, ukjent sub → 401 "Ukjent brukeridentitet"
5. /me med gyldig JWT, kjent sub → 200 med node_id

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
vegard 2026-03-17 12:33:24 +01:00
parent 854ed27797
commit 8428fa45a0
5 changed files with 19 additions and 8 deletions

View file

@ -118,6 +118,12 @@ LIVEKIT_API_SECRET=<generer med: openssl rand -hex 32>
# === OpenRouter === # === OpenRouter ===
OPENROUTER_API_KEY=<fra openrouter.ai> OPENROUTER_API_KEY=<fra openrouter.ai>
# === Maskinrommet ===
DATABASE_URL=postgres://sidelinja:<POSTGRES_PASSWORD>@postgres:5432/synops
AUTHENTIK_ISSUER=https://auth.sidelinja.org/application/o/sidelinja/
AUTHENTIK_CLIENT_ID=<fra Authentik OIDC-provider>
AUTHENTIK_CLIENT_SECRET=<fra Authentik OIDC-provider>
# === Intern === # === Intern ===
# Ingen porter eksponeres utenom 80/443. Alt rutes internt via Docker-nettverket. # Ingen porter eksponeres utenom 80/443. Alt rutes internt via Docker-nettverket.
EOF EOF

View file

@ -27,16 +27,17 @@ pub struct JwkKey {
pub e: String, pub e: String,
} }
/// Cached JWKS keys fetched from Authentik at startup. /// Cached JWKS keys and OIDC config fetched from Authentik at startup.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct JwksKeys { pub struct JwksKeys {
pub keys: Vec<JwkKey>, pub keys: Vec<JwkKey>,
pub issuer: String, pub issuer: String,
pub audience: String,
} }
impl JwksKeys { impl JwksKeys {
/// Fetch JWKS from Authentik's OIDC discovery endpoint. /// Fetch JWKS from Authentik's OIDC discovery endpoint.
pub async fn fetch(issuer: &str) -> Result<Self, String> { pub async fn fetch(issuer: &str, audience: &str) -> Result<Self, String> {
let jwks_url = format!("{}jwks/", issuer.trim_end_matches('/').to_owned() + "/"); let jwks_url = format!("{}jwks/", issuer.trim_end_matches('/').to_owned() + "/");
tracing::info!("Henter JWKS fra {jwks_url}"); tracing::info!("Henter JWKS fra {jwks_url}");
@ -58,6 +59,7 @@ impl JwksKeys {
Ok(Self { Ok(Self {
keys: jwks.keys, keys: jwks.keys,
issuer: issuer.to_string(), issuer: issuer.to_string(),
audience: audience.to_string(),
}) })
} }
@ -168,9 +170,10 @@ where
AuthErrorKind::InvalidToken(e) AuthErrorKind::InvalidToken(e)
})?; })?;
// Validate JWT (signature, exp, iss) // Validate JWT (signature, exp, iss, aud)
let mut validation = Validation::new(Algorithm::RS256); let mut validation = Validation::new(Algorithm::RS256);
validation.set_issuer(&[&app_state.jwks.issuer]); validation.set_issuer(&[&app_state.jwks.issuer]);
validation.set_audience(&[&app_state.jwks.audience]);
let token_data = decode::<Claims>(token, &decoding_key, &validation).map_err(|e| { let token_data = decode::<Claims>(token, &decoding_key, &validation).map_err(|e| {
tracing::debug!("JWT-validering feilet: {e}"); tracing::debug!("JWT-validering feilet: {e}");

View file

@ -54,7 +54,10 @@ async fn main() {
let issuer = std::env::var("AUTHENTIK_ISSUER") let issuer = std::env::var("AUTHENTIK_ISSUER")
.unwrap_or_else(|_| "https://auth.sidelinja.org/application/o/sidelinja/".to_string()); .unwrap_or_else(|_| "https://auth.sidelinja.org/application/o/sidelinja/".to_string());
let jwks = JwksKeys::fetch(&issuer) let client_id = std::env::var("AUTHENTIK_CLIENT_ID")
.expect("AUTHENTIK_CLIENT_ID må være satt");
let jwks = JwksKeys::fetch(&issuer, &client_id)
.await .await
.expect("Kunne ikke hente JWKS fra Authentik"); .expect("Kunne ikke hente JWKS fra Authentik");

View file

@ -19,13 +19,13 @@ VALUES (
-- ============================================================================= -- =============================================================================
-- Auth-identitet (bro mellom Authentik-sesjon og Vegards node) -- Auth-identitet (bro mellom Authentik-sesjon og Vegards node)
-- authentik_sub oppdateres når Authentik konfigureres i oppgave 1.5. -- authentik_sub er Authentik sin OIDC sub-claim (SHA256-hash av intern UUID).
-- ============================================================================= -- =============================================================================
INSERT INTO auth_identities (node_id, authentik_sub, email) INSERT INTO auth_identities (node_id, authentik_sub, email)
VALUES ( VALUES (
'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11', 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11',
'placeholder-will-be-updated-in-task-1.5', '6af61f43c6647a237cbb381ee7788376a9bc20299c2c06281d9954d763e854f0',
'vnotnes@pm.me' 'vnotnes@pm.me'
); );

View file

@ -52,8 +52,7 @@ Uavhengige faser kan fortsatt plukkes.
## Fase 2: Maskinrommet — skjelett ## Fase 2: Maskinrommet — skjelett
- [x] 2.1 Rust-prosjekt: opprett `maskinrommet/` med axum, tokio, sqlx (PG), serde. Dockerfile. Kompilerer og starter. Ref: `docs/retninger/maskinrommet.md`. - [x] 2.1 Rust-prosjekt: opprett `maskinrommet/` med axum, tokio, sqlx (PG), serde. Dockerfile. Kompilerer og starter. Ref: `docs/retninger/maskinrommet.md`.
- [~] 2.2 Auth-middleware: valider Authentik JWT-tokens, slå opp `auth_identities` → node_id. Returner 401 for ugyldige tokens. - [x] 2.2 Auth-middleware: valider Authentik JWT-tokens, slå opp `auth_identities` → node_id. Returner 401 for ugyldige tokens.
> Påbegynt: 2026-03-17T12:20
- [ ] 2.3 SpacetimeDB-klient i maskinrommet: koble til STDB, skriv noder og edges via reducers. - [ ] 2.3 SpacetimeDB-klient i maskinrommet: koble til STDB, skriv noder og edges via reducers.
- [ ] 2.4 Skrivestien: `POST /intentions/create_node` — valider, skriv STDB (instant), spawn async PG-skriving. Returner node_id umiddelbart. - [ ] 2.4 Skrivestien: `POST /intentions/create_node` — valider, skriv STDB (instant), spawn async PG-skriving. Returner node_id umiddelbart.
- [ ] 2.5 Flere intensjoner: `create_edge`, `update_node`, `delete_node`. Validering av tilgang (created_by eller owner/admin-edge). - [ ] 2.5 Flere intensjoner: `create_edge`, `update_node`, `delete_node`. Validering av tilgang (created_by eller owner/admin-edge).