From dc5b0faeab2353f5f7605779b5d06b21c95173c5 Mon Sep 17 00:00:00 2001 From: brookjeynes Date: Wed, 29 Oct 2025 08:46:17 +1000 Subject: [PATCH] feat(oauth/handler): pass error codes back to login page Change-Id: wlwouvqurvkzyzumnqwmrxzwxmsvlwll Signed-off-by: brookjeynes --- internal/server/handlers/login.go | 2 ++ internal/server/oauth/handler.go | 23 +++++++++++++++-------- internal/server/views/login.templ | 23 +++++++++++++++++++++++ internal/server/views/views.go | 1 + 4 files changed, 41 insertions(+), 8 deletions(-) diff --git a/internal/server/handlers/login.go b/internal/server/handlers/login.go index 84e9aa6..a1fa0cd 100644 --- a/internal/server/handlers/login.go +++ b/internal/server/handlers/login.go @@ -19,8 +19,10 @@ func (h *Handler) Login(w http.ResponseWriter, r *http.Request) { switch r.Method { case http.MethodGet: returnURL := r.URL.Query().Get("return_url") + errorCode := r.URL.Query().Get("error") views.LoginPage(views.LoginPageParams{ ReturnUrl: returnURL, + ErrorCode: errorCode, }).Render(r.Context(), w) case http.MethodPost: handle := r.FormValue("handle") diff --git a/internal/server/oauth/handler.go b/internal/server/oauth/handler.go index 7db5107..be6a149 100644 --- a/internal/server/oauth/handler.go +++ b/internal/server/oauth/handler.go @@ -3,11 +3,13 @@ package oauth import ( "context" "encoding/json" + "errors" "fmt" "net/http" "time" comatproto "github.com/bluesky-social/indigo/api/atproto" + "github.com/bluesky-social/indigo/atproto/auth/oauth" lexutil "github.com/bluesky-social/indigo/lex/util" "github.com/go-chi/chi/v5" "github.com/lestrrat-go/jwx/v2/jwk" @@ -16,7 +18,6 @@ import ( "yoten.app/api/yoten" ph "yoten.app/internal/clients/posthog" "yoten.app/internal/db" - "yoten.app/internal/server/htmx" ) func (o *OAuth) Router() http.Handler { @@ -71,19 +72,25 @@ func (o *OAuth) jwks(w http.ResponseWriter, r *http.Request) { } func (o *OAuth) callback(w http.ResponseWriter, r *http.Request) { - l := o.Logger.With("handler", "callback") ctx := r.Context() + l := o.Logger.With("handler", "callback").With("query", r.URL.Query()) sessData, err := o.ClientApp.ProcessCallback(ctx, r.URL.Query()) if err != nil { - o.Logger.Error("failed to process callback", "err", err) - http.Error(w, err.Error(), http.StatusInternalServerError) + var callbackErr *oauth.AuthRequestCallbackError + if errors.As(err, &callbackErr) { + l.Debug("callback error", "err", callbackErr) + http.Redirect(w, r, fmt.Sprintf("/login?error=%s", callbackErr.ErrorCode), http.StatusFound) + return + } + l.Error("failed to process callback", "err", err) + http.Redirect(w, r, "/login?error=oauth", http.StatusFound) return } if err := o.SaveSession(w, r, sessData); err != nil { l.Error("failed to save session", "err", err) - http.Error(w, err.Error(), http.StatusInternalServerError) + http.Redirect(w, r, "/login?error=session", http.StatusFound) return } @@ -91,14 +98,14 @@ func (o *OAuth) callback(w http.ResponseWriter, r *http.Request) { resolved, err := o.IdResolver.ResolveIdent(context.Background(), did) if err != nil { l.Error("failed to resolve handle", "handle", resolved.Handle.String(), "err", err) - htmx.HxError(w, http.StatusBadRequest, fmt.Sprintf("'%s' is an invalid handle", resolved.Handle.String())) + http.Redirect(w, r, "/login?error=handle", http.StatusFound) return } client, err := o.AuthorizedClient(r) if err != nil { l.Error("failed to get authorized client", "err", err) - http.Error(w, err.Error(), http.StatusInternalServerError) + http.Redirect(w, r, "/login?error=client", http.StatusFound) return } @@ -128,7 +135,7 @@ func (o *OAuth) callback(w http.ResponseWriter, r *http.Request) { }) if err != nil { l.Error("failed to create profile record", "err", err) - htmx.HxError(w, http.StatusInternalServerError, "Failed to announce profile creation, try again later") + http.Redirect(w, r, "/login?error=profile-creation", http.StatusFound) return } diff --git a/internal/server/views/login.templ b/internal/server/views/login.templ index 234021c..238c423 100644 --- a/internal/server/views/login.templ +++ b/internal/server/views/login.templ @@ -44,6 +44,29 @@ templ LoginPage(params LoginPageParams) { + if params.ErrorCode != "" { +
+
+ +
Login error
+
+
+

+ switch (params.ErrorCode) { + case "access_denied": + You have not authorized the app. + case "session": + Server failed to create user session. + case "handle": + Server failed to validate your handle. + default: + Internal Server error. + } + Please try again. +

+
+
+ }

New to the AT Protocol?

diff --git a/internal/server/views/views.go b/internal/server/views/views.go index 71ea5d8..49a926e 100644 --- a/internal/server/views/views.go +++ b/internal/server/views/views.go @@ -18,6 +18,7 @@ type IndexPageParams struct { type LoginPageParams struct { ReturnUrl string + ErrorCode string } type ProfilePageParams struct { -- 2.43.0