Add internal registration handler for OAuth flowsUnverified
7cfd6ab parent: 9a4b0ff modified
docs/specs.md +9 -1 | @@ -916,6 +916,12 @@ The server renders HTML fragments that htmx swaps into the page. No JSON API nee | ||
| 916 | 916 | | `/library/{id}/delete` | POST | Delete an annotation | |
| 917 | 917 | | `/stats` | GET | Application metrics and performance data (Prometheus, public) | |
| 918 | 918 | | `/profile/{did}` | GET | Public profile: their feeds, likes, annotations | |
| 919 | +| `/auth/login` | GET | Login page | | |
| 920 | +| `/auth/register` | GET | Register with Eurosky (OAuth flow with hardcoded PDS) | | |
| 921 | +| `/auth/resolve` | GET | Resolve handle to DID | | |
| 922 | +| `/auth/start` | POST | Start OAuth authorization flow | | |
| 923 | +| `/auth/callback` | GET | OAuth callback | | |
| 924 | +| `/terms` | GET | Terms of service | | |
| 919 | 925 | |
| 920 | 926 | ### 8.2 htmx Patterns |
| 921 | 927 | |
| @@ -982,7 +988,7 @@ glean/ | ||
| 982 | 988 | │ │ └── cron.go # Background recomputation scheduler |
| 983 | 989 | │ ├── server/ |
| 984 | 990 | │ │ ├── server.go # HTTP server, router setup |
| 985 | -│ │ ├── auth_handler.go # OAuth login/callback | |
| 991 | +│ │ ├── auth_handler.go # OAuth login/callback/register | |
| 986 | 992 | │ │ ├── feeds_handler.go # Feed management handlers |
| 987 | 993 | │ │ ├── articles_handler.go # Article reading handlers |
| 988 | 994 | │ │ ├── annotations_handler.go # Annotation handlers |
| @@ -991,6 +997,7 @@ glean/ | ||
| 991 | 997 | │ │ ├── stats_handler.go # Stats handler (Prometheus metrics display) |
| 992 | 998 | │ │ ├── index_handler.go # Landing page handler |
| 993 | 999 | │ │ ├── profile_handler.go # Public profile handler |
| 1000 | +│ │ ├── terms_handler.go # Terms of service handler | |
| 994 | 1001 | │ │ ├── pagination.go # Pagination helpers |
| 995 | 1002 | │ │ ├── middleware.go # Auth, logging, CSRF middleware |
| 996 | 1003 | │ │ └── session.go # Session management |
| @@ -1010,6 +1017,7 @@ glean/ | ||
| 1010 | 1017 | │ ├── profile.html # User profile |
| 1011 | 1018 | │ ├── error.html # Error page |
| 1012 | 1019 | │ ├── 404.html # Not found page |
| 1020 | +│ ├── terms.html # Terms of service | |
| 1013 | 1021 | │ └── partials/ # Reusable template fragments |
| 1014 | 1022 | ├── static/ |
| 1015 | 1023 | │ ├── input.css # Tailwind input |
| @@ -916,6 +916,12 @@ The server renders HTML fragments that htmx swaps into the page. No JSON API nee | |||
| 916 | | `/library/{id}/delete` | POST | Delete an annotation | | 916 | | `/library/{id}/delete` | POST | Delete an annotation | |
| 917 | | `/stats` | GET | Application metrics and performance data (Prometheus, public) | | 917 | | `/stats` | GET | Application metrics and performance data (Prometheus, public) | |
| 918 | | `/profile/{did}` | GET | Public profile: their feeds, likes, annotations | | 918 | | `/profile/{did}` | GET | Public profile: their feeds, likes, annotations | |
| 919 | +| `/auth/login` | GET | Login page | | ||
| 920 | +| `/auth/register` | GET | Register with Eurosky (OAuth flow with hardcoded PDS) | | ||
| 921 | +| `/auth/resolve` | GET | Resolve handle to DID | | ||
| 922 | +| `/auth/start` | POST | Start OAuth authorization flow | | ||
| 923 | +| `/auth/callback` | GET | OAuth callback | | ||
| 924 | +| `/terms` | GET | Terms of service | | ||
| 919 | 925 | ||
| 920 | ### 8.2 htmx Patterns | 926 | ### 8.2 htmx Patterns |
| 921 | 927 | ||
| @@ -982,7 +988,7 @@ glean/ | |||
| 982 | │ │ └── cron.go # Background recomputation scheduler | 988 | │ │ └── cron.go # Background recomputation scheduler |
| 983 | │ ├── server/ | 989 | │ ├── server/ |
| 984 | │ │ ├── server.go # HTTP server, router setup | 990 | │ │ ├── server.go # HTTP server, router setup |
| 985 | -│ │ ├── auth_handler.go # OAuth login/callback | 991 | +│ │ ├── auth_handler.go # OAuth login/callback/register |
| 986 | │ │ ├── feeds_handler.go # Feed management handlers | 992 | │ │ ├── feeds_handler.go # Feed management handlers |
| 987 | │ │ ├── articles_handler.go # Article reading handlers | 993 | │ │ ├── articles_handler.go # Article reading handlers |
| 988 | │ │ ├── annotations_handler.go # Annotation handlers | 994 | │ │ ├── annotations_handler.go # Annotation handlers |
| @@ -991,6 +997,7 @@ glean/ | |||
| 991 | │ │ ├── stats_handler.go # Stats handler (Prometheus metrics display) | 997 | │ │ ├── stats_handler.go # Stats handler (Prometheus metrics display) |
| 992 | │ │ ├── index_handler.go # Landing page handler | 998 | │ │ ├── index_handler.go # Landing page handler |
| 993 | │ │ ├── profile_handler.go # Public profile handler | 999 | │ │ ├── profile_handler.go # Public profile handler |
| 1000 | +│ │ ├── terms_handler.go # Terms of service handler | ||
| 994 | │ │ ├── pagination.go # Pagination helpers | 1001 | │ │ ├── pagination.go # Pagination helpers |
| 995 | │ │ ├── middleware.go # Auth, logging, CSRF middleware | 1002 | │ │ ├── middleware.go # Auth, logging, CSRF middleware |
| 996 | │ │ └── session.go # Session management | 1003 | │ │ └── session.go # Session management |
| @@ -1010,6 +1017,7 @@ glean/ | |||
| 1010 | │ ├── profile.html # User profile | 1017 | │ ├── profile.html # User profile |
| 1011 | │ ├── error.html # Error page | 1018 | │ ├── error.html # Error page |
| 1012 | │ ├── 404.html # Not found page | 1019 | │ ├── 404.html # Not found page |
| 1020 | +│ ├── terms.html # Terms of service | ||
| 1013 | │ └── partials/ # Reusable template fragments | 1021 | │ └── partials/ # Reusable template fragments |
| 1014 | ├── static/ | 1022 | ├── static/ |
| 1015 | │ ├── input.css # Tailwind input | 1023 | │ ├── input.css # Tailwind input |
modified
internal/server/auth_handler.go +11 -0 | @@ -17,6 +17,17 @@ func (s *Server) handleAuthLogin(w http.ResponseWriter, r *http.Request) { | ||
| 17 | 17 | s.render(w, r, "login.html", map[string]any{}) |
| 18 | 18 | } |
| 19 | 19 | |
| 20 | +func (s *Server) handleAuthRegister(w http.ResponseWriter, r *http.Request) { | |
| 21 | + // hardcoded to Eurosky PDS for sign-up because it is a good public one. | |
| 22 | + authURL, err := s.oauth.StartAuthFlow(r.Context(), "https://eurosky.social") | |
| 23 | + if err != nil { | |
| 24 | + s.logger.Error("failed to start register OAuth flow", "error", err) | |
| 25 | + s.renderError(w, r, http.StatusInternalServerError, "Registration failed", "Could not connect to Eurosky. Please try again.") | |
| 26 | + return | |
| 27 | + } | |
| 28 | + http.Redirect(w, r, authURL, http.StatusSeeOther) | |
| 29 | +} | |
| 30 | + | |
| 20 | 31 | func (s *Server) handleAuthResolve(w http.ResponseWriter, r *http.Request) { |
| 21 | 32 | q := strings.TrimPrefix(r.URL.Query().Get("q"), "@") |
| 22 | 33 | if q == "" { |
| @@ -17,6 +17,17 @@ func (s *Server) handleAuthLogin(w http.ResponseWriter, r *http.Request) { | |||
| 17 | s.render(w, r, "login.html", map[string]any{}) | 17 | s.render(w, r, "login.html", map[string]any{}) |
| 18 | } | 18 | } |
| 19 | 19 | ||
| 20 | +func (s *Server) handleAuthRegister(w http.ResponseWriter, r *http.Request) { | ||
| 21 | + // hardcoded to Eurosky PDS for sign-up because it is a good public one. | ||
| 22 | + authURL, err := s.oauth.StartAuthFlow(r.Context(), "https://eurosky.social") | ||
| 23 | + if err != nil { | ||
| 24 | + s.logger.Error("failed to start register OAuth flow", "error", err) | ||
| 25 | + s.renderError(w, r, http.StatusInternalServerError, "Registration failed", "Could not connect to Eurosky. Please try again.") | ||
| 26 | + return | ||
| 27 | + } | ||
| 28 | + http.Redirect(w, r, authURL, http.StatusSeeOther) | ||
| 29 | +} | ||
| 30 | + | ||
| 20 | func (s *Server) handleAuthResolve(w http.ResponseWriter, r *http.Request) { | 31 | func (s *Server) handleAuthResolve(w http.ResponseWriter, r *http.Request) { |
| 21 | q := strings.TrimPrefix(r.URL.Query().Get("q"), "@") | 32 | q := strings.TrimPrefix(r.URL.Query().Get("q"), "@") |
| 22 | if q == "" { | 33 | if q == "" { |
modified
internal/server/server.go +1 -0 | @@ -204,6 +204,7 @@ func (s *Server) setupRoutes() { | ||
| 204 | 204 | }) |
| 205 | 205 | |
| 206 | 206 | s.router.Get("/auth/login", s.handleAuthLogin) |
| 207 | + s.router.Get("/auth/register", s.handleAuthRegister) | |
| 207 | 208 | s.router.Get("/auth/resolve", s.handleAuthResolve) |
| 208 | 209 | s.router.Post("/auth/start", s.handleAuthStart) |
| 209 | 210 | s.router.Get("/auth/callback", s.handleAuthCallback) |
| @@ -204,6 +204,7 @@ func (s *Server) setupRoutes() { | |||
| 204 | }) | 204 | }) |
| 205 | 205 | ||
| 206 | s.router.Get("/auth/login", s.handleAuthLogin) | 206 | s.router.Get("/auth/login", s.handleAuthLogin) |
| 207 | + s.router.Get("/auth/register", s.handleAuthRegister) | ||
| 207 | s.router.Get("/auth/resolve", s.handleAuthResolve) | 208 | s.router.Get("/auth/resolve", s.handleAuthResolve) |
| 208 | s.router.Post("/auth/start", s.handleAuthStart) | 209 | s.router.Post("/auth/start", s.handleAuthStart) |
| 209 | s.router.Get("/auth/callback", s.handleAuthCallback) | 210 | s.router.Get("/auth/callback", s.handleAuthCallback) |
modified
internal/tmpl/login.html +1 -1 | @@ -33,7 +33,7 @@ | ||
| 33 | 33 | <span>New here?</span> |
| 34 | 34 | </div> |
| 35 | 35 | |
| 36 | - <a href="https://portal.eurosky.tech/create-account" target="_blank" rel="noopener noreferrer" | |
| 36 | + <a href="/auth/register" | |
| 37 | 37 | class="w-full flex items-center justify-center gap-3 border border-spot-outline rounded-pill px-4 py-3.5 text-sm font-bold uppercase tracking-button hover:bg-spot-hover-50 transition"> |
| 38 | 38 | <svg class="h-5 w-auto" fill="currentColor" viewBox="77.86 113.9 129.15 129.15" xmlns="http://www.w3.org/2000/svg"><path d="M148.846 144.562C148.846 159.75 161.158 172.062 176.346 172.062H207.012V185.865H176.346C161.158 185.865 148.846 198.177 148.846 213.365V243.045H136.029V213.365C136.029 198.177 123.717 185.865 108.529 185.865H77.8633V172.062H108.529C123.717 172.062 136.029 159.75 136.029 144.562V113.896H148.846V144.562Z"/></svg> |
| 39 | 39 | Register with Eurosky |
| @@ -33,7 +33,7 @@ | |||
| 33 | <span>New here?</span> | 33 | <span>New here?</span> |
| 34 | </div> | 34 | </div> |
| 35 | 35 | ||
| 36 | - <a href="https://portal.eurosky.tech/create-account" target="_blank" rel="noopener noreferrer" | 36 | + <a href="/auth/register" |
| 37 | class="w-full flex items-center justify-center gap-3 border border-spot-outline rounded-pill px-4 py-3.5 text-sm font-bold uppercase tracking-button hover:bg-spot-hover-50 transition"> | 37 | class="w-full flex items-center justify-center gap-3 border border-spot-outline rounded-pill px-4 py-3.5 text-sm font-bold uppercase tracking-button hover:bg-spot-hover-50 transition"> |
| 38 | <svg class="h-5 w-auto" fill="currentColor" viewBox="77.86 113.9 129.15 129.15" xmlns="http://www.w3.org/2000/svg"><path d="M148.846 144.562C148.846 159.75 161.158 172.062 176.346 172.062H207.012V185.865H176.346C161.158 185.865 148.846 198.177 148.846 213.365V243.045H136.029V213.365C136.029 198.177 123.717 185.865 108.529 185.865H77.8633V172.062H108.529C123.717 172.062 136.029 159.75 136.029 144.562V113.896H148.846V144.562Z"/></svg> | 38 | <svg class="h-5 w-auto" fill="currentColor" viewBox="77.86 113.9 129.15 129.15" xmlns="http://www.w3.org/2000/svg"><path d="M148.846 144.562C148.846 159.75 161.158 172.062 176.346 172.062H207.012V185.865H176.346C161.158 185.865 148.846 198.177 148.846 213.365V243.045H136.029V213.365C136.029 198.177 123.717 185.865 108.529 185.865H77.8633V172.062H108.529C123.717 172.062 136.029 159.75 136.029 144.562V113.896H148.846V144.562Z"/></svg> |
| 39 | Register with Eurosky | 39 | Register with Eurosky |