package api import ( "context" "crypto/sha256" "database/sql" "encoding/hex" "net/http" "strings" "time" "git.ryuvia.com/niklas/terdut-server/internal/models" ) type contextKey string const ctxUser contextKey = "user" func AuthMiddleware(db *sql.DB) func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { token, ok := strings.CutPrefix(r.Header.Get("Authorization"), "Bearer ") if !ok || token == "" { respond(w, http.StatusUnauthorized, errResp("unauthorized")) return } h := sha256.Sum256([]byte(token)) hash := hex.EncodeToString(h[:]) var keyID, userID int64 err := db.QueryRowContext(r.Context(), "SELECT id, user_id FROM api_keys WHERE key_hash = ?", hash, ).Scan(&keyID, &userID) if err != nil { respond(w, http.StatusUnauthorized, errResp("unauthorized")) return } // best-effort; don't fail the request if this update fails db.ExecContext(r.Context(), "UPDATE api_keys SET last_used_at = ? WHERE id = ?", time.Now().Unix(), keyID) var u models.User var createdUnix int64 if err := db.QueryRowContext(r.Context(), "SELECT id, username, email, created_at FROM users WHERE id = ?", userID, ).Scan(&u.ID, &u.Username, &u.Email, &createdUnix); err != nil { respond(w, http.StatusUnauthorized, errResp("unauthorized")) return } u.CreatedAt = time.Unix(createdUnix, 0).UTC() next.ServeHTTP(w, r.WithContext(context.WithValue(r.Context(), ctxUser, u))) }) } } func userFromContext(ctx context.Context) (models.User, bool) { u, ok := ctx.Value(ctxUser).(models.User) return u, ok }