| package auth |
|
|
| import ( |
| "encoding/json" |
| "net/http" |
| "os" |
| "strings" |
| "time" |
|
|
| authn "ds2api/internal/auth" |
| ) |
|
|
| func (h *Handler) requireAdmin(next http.Handler) http.Handler { |
| return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
| if err := authn.VerifyAdminRequestWithStore(r, h.Store); err != nil { |
| writeJSON(w, http.StatusUnauthorized, map[string]any{"detail": err.Error()}) |
| return |
| } |
| next.ServeHTTP(w, r) |
| }) |
| } |
|
|
| func (h *Handler) login(w http.ResponseWriter, r *http.Request) { |
| var req map[string]any |
| _ = json.NewDecoder(r.Body).Decode(&req) |
| adminKey, _ := req["admin_key"].(string) |
| expireHours := intFrom(req["expire_hours"]) |
| if !authn.VerifyAdminCredential(adminKey, h.Store) { |
| writeJSON(w, http.StatusUnauthorized, map[string]any{"detail": "Invalid admin key"}) |
| return |
| } |
| token, err := authn.CreateJWTWithStore(expireHours, h.Store) |
| if err != nil { |
| writeJSON(w, http.StatusInternalServerError, map[string]any{"detail": err.Error()}) |
| return |
| } |
| if expireHours <= 0 { |
| expireHours = h.Store.AdminJWTExpireHours() |
| } |
| writeJSON(w, http.StatusOK, map[string]any{"success": true, "token": token, "expires_in": expireHours * 3600}) |
| } |
|
|
| func (h *Handler) verify(w http.ResponseWriter, r *http.Request) { |
| header := strings.TrimSpace(r.Header.Get("Authorization")) |
| if !strings.HasPrefix(strings.ToLower(header), "bearer ") { |
| writeJSON(w, http.StatusUnauthorized, map[string]any{"detail": "No credentials provided"}) |
| return |
| } |
| token := strings.TrimSpace(header[7:]) |
| payload, err := authn.VerifyJWTWithStore(token, h.Store) |
| if err != nil { |
| writeJSON(w, http.StatusUnauthorized, map[string]any{"detail": err.Error()}) |
| return |
| } |
| exp, _ := payload["exp"].(float64) |
| remaining := int64(exp) - time.Now().Unix() |
| if remaining < 0 { |
| remaining = 0 |
| } |
| writeJSON(w, http.StatusOK, map[string]any{"valid": true, "expires_at": int64(exp), "remaining_seconds": remaining}) |
| } |
|
|
| func (h *Handler) getVercelConfig(w http.ResponseWriter, _ *http.Request) { |
| saved := h.Store.Snapshot().Vercel |
| token, tokenSource := firstConfiguredValue( |
| [2]string{"env", os.Getenv("VERCEL_TOKEN")}, |
| [2]string{"config", saved.Token}, |
| ) |
| projectID, _ := firstConfiguredValue( |
| [2]string{"env", os.Getenv("VERCEL_PROJECT_ID")}, |
| [2]string{"config", saved.ProjectID}, |
| ) |
| teamID, _ := firstConfiguredValue( |
| [2]string{"env", os.Getenv("VERCEL_TEAM_ID")}, |
| [2]string{"config", saved.TeamID}, |
| ) |
| writeJSON(w, http.StatusOK, map[string]any{ |
| "has_token": token != "", |
| "token_preview": maskSecretPreview(token), |
| "token_source": nilIfEmpty(tokenSource), |
| "project_id": projectID, |
| "team_id": nilIfEmpty(teamID), |
| }) |
| } |
|
|
| func firstConfiguredValue(values ...[2]string) (string, string) { |
| for _, pair := range values { |
| value := strings.TrimSpace(pair[1]) |
| if value != "" { |
| return value, strings.TrimSpace(pair[0]) |
| } |
| } |
| return "", "" |
| } |
|
|