Files
Snip/backend/internal/httpx/api/auth.go
T
2026-06-15 21:25:57 +07:00

89 lines
2.4 KiB
Go

package api
import (
"net/http"
"strings"
"time"
"github.com/snip/backend/internal/domain"
)
// /api/v1/auth/...
// GET providers
// GET {provider}/login
// GET {provider}/callback
// GET me
// POST logout
func (a *API) authRoutes(w http.ResponseWriter, r *http.Request) {
rest := pathTail(r.URL.Path, "/api/v1/auth/")
parts := strings.Split(strings.Trim(rest, "/"), "/")
switch {
case len(parts) == 1 && parts[0] == "providers":
writeJSON(w, http.StatusOK, map[string][]string{"providers": a.auth.Providers()})
case len(parts) == 1 && parts[0] == "me":
a.me(w, r)
case len(parts) == 1 && parts[0] == "logout":
a.clearCookie(w, sessionCookie)
w.WriteHeader(http.StatusNoContent)
case len(parts) == 2 && parts[1] == "login":
a.beginLogin(w, r, parts[0])
case len(parts) == 2 && parts[1] == "callback":
a.callback(w, r, parts[0])
default:
w.WriteHeader(http.StatusNotFound)
}
}
func (a *API) me(w http.ResponseWriter, r *http.Request) {
c, err := r.Cookie(sessionCookie)
if err != nil {
writeError(w, domain.ErrUnauthorized)
return
}
user, err := a.auth.Me(r.Context(), c.Value)
if err != nil {
writeError(w, domain.ErrUnauthorized)
return
}
writeJSON(w, http.StatusOK, userDTO{ID: user.ID, Email: user.Email, Name: user.Name})
}
func (a *API) beginLogin(w http.ResponseWriter, r *http.Request, provider string) {
state := randomState()
url, err := a.auth.AuthURL(provider, state)
if err != nil {
writeError(w, err)
return
}
// Double-submit: stash state in a short-lived cookie, compare on callback.
http.SetCookie(w, &http.Cookie{
Name: stateCookie, Value: state, Path: "/", HttpOnly: true,
Secure: a.cfg.CookieSecure, SameSite: http.SameSiteLaxMode,
Expires: time.Now().Add(10 * time.Minute),
})
http.Redirect(w, r, url, http.StatusFound)
}
func (a *API) callback(w http.ResponseWriter, r *http.Request, provider string) {
cookie, err := r.Cookie(stateCookie)
if err != nil || cookie.Value == "" || cookie.Value != r.URL.Query().Get("state") {
writeError(w, domain.ErrUnauthorized)
return
}
a.clearCookie(w, stateCookie)
code := r.URL.Query().Get("code")
if code == "" {
writeError(w, domain.ErrUnauthorized)
return
}
_, token, err := a.auth.Complete(r.Context(), provider, code)
if err != nil {
writeError(w, err)
return
}
a.setSession(w, token)
http.Redirect(w, r, a.cfg.PostLoginRedirect, http.StatusFound)
}