Username i auth_identities: Authentik-synk ved login (oppgave 26.1)
Legger til username-kolonne i auth_identities med UNIQUE constraint. Ved innlogging sender SvelteKit preferred_username fra Authentik til maskinrommet POST /auth/sync, som oppdaterer kolonnen. Grunnlaget for epost-ruting i fase 26: vegard@synops.no → username-oppslag.
This commit is contained in:
parent
6b81569581
commit
25fc1a1b59
4 changed files with 88 additions and 3 deletions
|
|
@ -31,6 +31,29 @@ async function fetchNodeId(accessToken: string): Promise<string | null> {
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync user profile (username) to maskinrommet on sign-in.
|
||||
* Stores Authentik preferred_username in auth_identities.username.
|
||||
*/
|
||||
async function syncUsername(accessToken: string, username: string): Promise<void> {
|
||||
const url = env.MASKINROMMET_URL ?? 'https://api.sidelinja.org';
|
||||
try {
|
||||
const res = await fetch(`${url}/auth/sync`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ username })
|
||||
});
|
||||
if (!res.ok) {
|
||||
console.error(`[auth] /auth/sync returned ${res.status}: ${await res.text()}`);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('[auth] Failed to sync username to maskinrommet:', e);
|
||||
}
|
||||
}
|
||||
|
||||
export const { handle, signIn, signOut } = SvelteKitAuth({
|
||||
trustHost: true,
|
||||
providers: [
|
||||
|
|
@ -69,6 +92,11 @@ export const { handle, signIn, signOut } = SvelteKitAuth({
|
|||
if (!token.node_id) {
|
||||
token.node_id = await fetchNodeId(account.access_token);
|
||||
}
|
||||
// Sync username from Authentik preferred_username on each sign-in
|
||||
const p = profile as AuthentikProfile | undefined;
|
||||
if (p?.preferred_username) {
|
||||
await syncUsername(account.access_token, p.preferred_username);
|
||||
}
|
||||
}
|
||||
return token;
|
||||
},
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ pub mod user_usage;
|
|||
mod workspace;
|
||||
|
||||
use axum::{extract::State, http::StatusCode, middleware, routing::{get, post}, Json, Router};
|
||||
use serde::Serialize;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::postgres::PgPoolOptions;
|
||||
use sqlx::PgPool;
|
||||
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter};
|
||||
|
|
@ -72,6 +72,16 @@ struct MeResponse {
|
|||
authentik_sub: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct AuthSyncRequest {
|
||||
username: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct AuthSyncResponse {
|
||||
ok: bool,
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
// Strukturert logging: LOG_FORMAT=json for maskinlesbart, ellers human-readable.
|
||||
|
|
@ -164,6 +174,7 @@ async fn main() {
|
|||
let app = Router::new()
|
||||
.route("/health", get(health))
|
||||
.route("/me", get(me))
|
||||
.route("/auth/sync", post(auth_sync))
|
||||
// WebSocket-endepunkt for sanntid via PG LISTEN/NOTIFY (oppgave 22.1)
|
||||
.route("/ws", get(ws::ws_handler))
|
||||
.route("/intentions/create_node", post(intentions::create_node))
|
||||
|
|
@ -304,3 +315,39 @@ async fn me(user: AuthUser) -> Json<MeResponse> {
|
|||
authentik_sub: user.authentik_sub,
|
||||
})
|
||||
}
|
||||
|
||||
/// POST /auth/sync — synkroniser brukerprofil fra Authentik ved login.
|
||||
/// Kalles av SvelteKit auth-callback etter vellykket innlogging.
|
||||
/// Oppdaterer username i auth_identities fra Authentik preferred_username.
|
||||
async fn auth_sync(
|
||||
user: AuthUser,
|
||||
State(state): State<AppState>,
|
||||
Json(body): Json<AuthSyncRequest>,
|
||||
) -> Result<Json<AuthSyncResponse>, StatusCode> {
|
||||
let username = body.username.trim().to_lowercase();
|
||||
|
||||
if username.is_empty() {
|
||||
tracing::warn!("auth_sync: tomt brukernavn for node_id={}", user.node_id);
|
||||
return Err(StatusCode::BAD_REQUEST);
|
||||
}
|
||||
|
||||
sqlx::query(
|
||||
"UPDATE auth_identities SET username = $1 WHERE node_id = $2",
|
||||
)
|
||||
.bind(&username)
|
||||
.bind(user.node_id)
|
||||
.execute(&state.db)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
tracing::error!("auth_sync: kunne ikke oppdatere username: {e}");
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
})?;
|
||||
|
||||
tracing::info!(
|
||||
"auth_sync: username='{}' for node_id={}",
|
||||
username,
|
||||
user.node_id
|
||||
);
|
||||
|
||||
Ok(Json(AuthSyncResponse { ok: true }))
|
||||
}
|
||||
|
|
|
|||
11
migrations/027_auth_username.sql
Normal file
11
migrations/027_auth_username.sql
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
-- 027: Legg til username-kolonne i auth_identities
|
||||
-- Populeres fra Authentik preferred_username ved login.
|
||||
-- Brukes til epost-ruting (fase 26): vegard@synops.no → auth_identities.username = 'vegard'
|
||||
|
||||
ALTER TABLE auth_identities
|
||||
ADD COLUMN username TEXT UNIQUE;
|
||||
|
||||
-- Sett Vegards username fra seed-data
|
||||
UPDATE auth_identities
|
||||
SET username = 'vegard'
|
||||
WHERE email = 'vnotnes@pm.me';
|
||||
3
tasks.md
3
tasks.md
|
|
@ -345,8 +345,7 @@ Vaktmesteren kan sende epost (msmtp) og motta epost (Postfix → synops-mail).
|
|||
Brukernavn@domene ruter til brukerens innboks. Alle domener (synops.no,
|
||||
sidelinja.org, vegard.info) ruter til samme bruker basert på username.
|
||||
|
||||
- [~] 26.1 Username i auth_identities: legg til `username`-kolonne, populer fra Authentik `preferred_username` ved login. Unik constraint. Oppdater auth-callback i SvelteKit til å lagre username.
|
||||
> Påbegynt: 2026-03-18T19:00
|
||||
- [x] 26.1 Username i auth_identities: legg til `username`-kolonne, populer fra Authentik `preferred_username` ved login. Unik constraint. Oppdater auth-callback i SvelteKit til å lagre username.
|
||||
- [ ] 26.2 msmtp oppsett: konfigurer utgående epost via SMTP-relay. Avsender: `vaktmester@synops.no`. Tilgjengelig som `synops-mail --send --to <epost> --subject <emne>` CLI-verktøy.
|
||||
- [ ] 26.3 MX-records: sett opp MX for synops.no, sidelinja.org, vegard.info som peker til serveren.
|
||||
- [ ] 26.4 Postfix minimal: installer Postfix som lokal MTA kun for mottak. Ingen relay, ingen kø for utgående. Pipe innkommende epost til `synops-mail --receive`.
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue