Monorepo for Tangled tangled.org

appview/oauth: handle failed login with error code

Close: #271
Signed-off-by: Seongmin Lee <git@boltless.me>

boltless.me 2d890dbb ef692dd7

verified
Changed files
+36 -4
appview
oauth
pages
templates
user
state
+13 -2
appview/oauth/handler.go
··· 4 4 "bytes" 5 5 "context" 6 6 "encoding/json" 7 + "errors" 7 8 "fmt" 8 9 "net/http" 9 10 "slices" 10 11 "time" 11 12 13 + "github.com/bluesky-social/indigo/atproto/auth/oauth" 12 14 "github.com/go-chi/chi/v5" 13 15 "github.com/lestrrat-go/jwx/v2/jwk" 14 16 "github.com/posthog/posthog-go" ··· 58 60 59 61 func (o *OAuth) callback(w http.ResponseWriter, r *http.Request) { 60 62 ctx := r.Context() 63 + l := o.Logger.With("query", r.URL.Query()) 61 64 62 65 sessData, err := o.ClientApp.ProcessCallback(ctx, r.URL.Query()) 63 66 if err != nil { 64 - http.Error(w, err.Error(), http.StatusInternalServerError) 67 + var callbackErr *oauth.AuthRequestCallbackError 68 + if errors.As(err, &callbackErr) { 69 + l.Debug("callback error", "err", callbackErr) 70 + http.Redirect(w, r, fmt.Sprintf("/login?error=%s", callbackErr.ErrorCode), http.StatusFound) 71 + return 72 + } 73 + l.Error("failed to process callback", "err", err) 74 + http.Redirect(w, r, "/login?error=oauth", http.StatusFound) 65 75 return 66 76 } 67 77 68 78 if err := o.SaveSession(w, r, sessData); err != nil { 69 - http.Error(w, err.Error(), http.StatusInternalServerError) 79 + l.Error("failed to save session", "data", sessData, "err", err) 80 + http.Redirect(w, r, "/login?error=session", http.StatusFound) 70 81 return 71 82 } 72 83
+1
appview/pages/pages.go
··· 221 221 222 222 type LoginParams struct { 223 223 ReturnUrl string 224 + ErrorCode string 224 225 } 225 226 226 227 func (p *Pages) Login(w io.Writer, params LoginParams) error {
+20 -2
appview/pages/templates/user/login.html
··· 13 13 <title>login &middot; tangled</title> 14 14 </head> 15 15 <body class="flex items-center justify-center min-h-screen"> 16 - <main class="max-w-md px-6 -mt-4"> 16 + <main class="max-w-md px-7 mt-4"> 17 17 <h1 class="flex place-content-center text-3xl font-semibold italic dark:text-white" > 18 18 {{ template "fragments/logotype" }} 19 19 </h1> ··· 21 21 tightly-knit social coding. 22 22 </h2> 23 23 <form 24 - class="mt-4 max-w-sm mx-auto" 24 + class="mt-4" 25 25 hx-post="/login" 26 26 hx-swap="none" 27 27 hx-disabled-elt="#login-button" ··· 56 56 <span>login</span> 57 57 </button> 58 58 </form> 59 + {{ if .ErrorCode }} 60 + <div class="flex gap-2 my-2 bg-red-50 dark:bg-red-900 border border-red-500 rounded drop-shadow-sm px-3 py-2 text-red-500 dark:text-red-300"> 61 + <span class="py-1">{{ i "circle-alert" "w-4 h-4" }}</span> 62 + <div> 63 + <h5 class="font-medium">Login error</h5> 64 + <p class="text-sm"> 65 + {{ if eq .ErrorCode "access_denied" }} 66 + You have not authorized the app. 67 + {{ else if eq .ErrorCode "session" }} 68 + Server failed to create user session. 69 + {{ else }} 70 + Internal Server error. 71 + {{ end }} 72 + Please try again. 73 + </p> 74 + </div> 75 + </div> 76 + {{ end }} 59 77 <p class="text-sm text-gray-500"> 60 78 Don't have an account? <a href="/signup" class="underline">Create an account</a> on Tangled now! 61 79 </p>
+2
appview/state/login.go
··· 14 14 switch r.Method { 15 15 case http.MethodGet: 16 16 returnURL := r.URL.Query().Get("return_url") 17 + errorCode := r.URL.Query().Get("error") 17 18 s.pages.Login(w, pages.LoginParams{ 18 19 ReturnUrl: returnURL, 20 + ErrorCode: errorCode, 19 21 }) 20 22 case http.MethodPost: 21 23 handle := r.FormValue("handle")