add initial auth0 module
This commit is contained in:
127
auth/auth0/handlers.go
Normal file
127
auth/auth0/handlers.go
Normal file
@@ -0,0 +1,127 @@
|
||||
package auth0
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/coreos/go-oidc/v3/oidc"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
const StateKey = "state"
|
||||
|
||||
func generateRandomState() (string, error) {
|
||||
b := make([]byte, 32)
|
||||
_, err := rand.Read(b)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return base64.RawURLEncoding.EncodeToString(b), err
|
||||
}
|
||||
|
||||
func HandleLogin(deps *deps) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
state, err := generateRandomState()
|
||||
if err != nil {
|
||||
deps.log.Error("unable to generate random state", "error", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
deps.log.Info("generated state", "state", state)
|
||||
|
||||
if err = deps.sessions.Put(r.Context(), StateKey, state); err != nil {
|
||||
deps.log.Error("unable to store state in session", "error", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
http.Redirect(w, r, deps.auth.AuthCodeURL(state), http.StatusFound)
|
||||
}
|
||||
}
|
||||
|
||||
func HandleLogout(deps *deps) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
if err := deps.sessions.Put(r.Context(), "user", nil); err != nil {
|
||||
deps.log.Error("unable to remove user from session", "error", err)
|
||||
}
|
||||
|
||||
if err := deps.sessions.Put(r.Context(), StateKey, nil); err != nil {
|
||||
deps.log.Error("unable to remove state from session", "error", err)
|
||||
}
|
||||
|
||||
scheme := "http"
|
||||
if r.TLS != nil {
|
||||
scheme = "https"
|
||||
}
|
||||
|
||||
returnTo := scheme + "://" + r.Host
|
||||
|
||||
logout := deps.logoutBase.ResolveReference(&url.URL{})
|
||||
q := logout.Query()
|
||||
q.Add("returnTo", returnTo)
|
||||
q.Add("client_id", deps.auth.ClientID)
|
||||
logout.RawQuery = q.Encode()
|
||||
|
||||
http.Redirect(w, r, logout.String(), http.StatusFound)
|
||||
}
|
||||
}
|
||||
|
||||
type Authenticator interface {
|
||||
Exchange(context.Context, string) (*oauth2.Token, error)
|
||||
VerifyIDToken(context.Context, *oauth2.Token) (*oidc.IDToken, error)
|
||||
}
|
||||
|
||||
func HandleCallback(deps *deps) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
_, ok := deps.sessions.Get(r.Context(), StateKey).(string)
|
||||
if !ok {
|
||||
deps.log.Error("no state found in session")
|
||||
http.Error(w, "no state found in session", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if err := deps.sessions.Put(r.Context(), StateKey, nil); err != nil {
|
||||
deps.log.Error("unable to remove state from session", "error", err)
|
||||
}
|
||||
|
||||
token, err := deps.auth.Exchange(r.Context(), r.URL.Query().Get("code"))
|
||||
if err != nil {
|
||||
deps.log.Error("unable to exchange auth code for token", "error", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
idToken, err := deps.auth.VerifyIDToken(r.Context(), token)
|
||||
if err != nil {
|
||||
deps.log.Error("unable to verify ID token", "error", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
var profile map[string]any
|
||||
if err = idToken.Claims(&profile); err != nil {
|
||||
deps.log.Error("unable to decode ID token claims", "error", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if err = deps.sessions.Put(r.Context(), "user", profile); err != nil {
|
||||
deps.log.Error("unable to store user profile in session", "error", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if err = deps.sessions.Put(r.Context(), "access_token", token.AccessToken); err != nil {
|
||||
deps.log.Error("unable to store access token in session", "error", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
http.Redirect(w, r, "/", http.StatusFound)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user