mirror of
https://github.com/ThisTine/Snip.git
synced 2026-08-18 23:18:47 +07:00
feat: first commit
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
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)
|
||||
}
|
||||
Reference in New Issue
Block a user