package handlers import ( "net/http" "amnezia-share/internal/auth" "amnezia-share/internal/web" ) func registerOIDC(a *web.App, mux *http.ServeMux) { mux.HandleFunc("GET /oidc/callback", OIDCCallback(a)) } // OIDCCallback completes the Pocket ID authorization-code + PKCE flow, matching // or auto-provisioning the local admin account (oidc_callback.php). func OIDCCallback(a *web.App) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { ctx := r.Context() q := r.URL.Query() if errParam := q.Get("error"); errParam != "" { renderOIDCError(a, w, r, "Pocket ID вернул ошибку: "+errParam) return } state := q.Get("state") code := q.Get("code") wantState := a.Sessions.GetString(ctx, "oidc_state") verifier := a.Sessions.GetString(ctx, "oidc_verifier") a.Sessions.Remove(ctx, "oidc_state") a.Sessions.Remove(ctx, "oidc_verifier") if code == "" || wantState == "" || state != wantState { renderOIDCError(a, w, r, "Недействительный ответ от Pocket ID. Попробуйте войти ещё раз.") return } cfg := oidcConfigFromSettings(a, r) if !cfg.Configured() { renderOIDCError(a, w, r, "Вход через Pocket ID не настроен.") return } redirectURI := a.AbsoluteURL(r, "oidc/callback") info, err := auth.ExchangeAndUserinfo(ctx, cfg, code, redirectURI, verifier) if err != nil { renderOIDCError(a, w, r, err.Error()) return } admin, err := a.Auth.FindOrCreateAdminFromOIDC(ctx, info) if err != nil { renderOIDCError(a, w, r, err.Error()) return } _ = a.Sessions.RenewToken(ctx) a.Sessions.Put(ctx, web.SessionAdminID, admin.ID) redirectTo(w, r, a, "admin") } } func renderOIDCError(a *web.App, w http.ResponseWriter, r *http.Request, msg string) { a.Render(w, r, "page_login", loginView{Error: msg, PocketEnabled: oidcConfigFromSettings(a, r).Configured()}) }