package main import ( "context" "encoding/base64" "encoding/json" "fmt" "html/template" "log" "net/http" "net/url" "os" "strings" "github.com/coreos/go-oidc/v3/oidc" "github.com/hashicorp/uuid" "github.com/joho/godotenv" "github.com/xlgmokha/api-auth0/pkg/x" "golang.org/x/oauth2" ) const ( SessionCookie string = "c0_session" ) func SessionFor(sessions map[string]*x.Session, r *http.Request, w http.ResponseWriter) *x.Session { cookie, err := r.Cookie(SessionCookie) var sessionId string if err != nil { sessionId = uuid.GenerateUUID() } else { sessionId = cookie.Value } http.SetCookie(w, &http.Cookie{Name: SessionCookie, Value: sessionId}) session, ok := sessions[sessionId] if !ok { session = &x.Session{} session.Reset() sessions[sessionId] = session } return session } func main() { sessions := map[string]*x.Session{} x.Check(godotenv.Load()) provider := x.Must(oidc.NewProvider(context.Background(), "https://"+os.Getenv("AUTH0_DOMAIN")+"/")) cfg := oauth2.Config{ ClientID: os.Getenv("AUTH0_CLIENT_ID"), ClientSecret: os.Getenv("AUTH0_CLIENT_SECRET"), RedirectURL: os.Getenv("AUTH0_CALLBACK_URL"), Endpoint: provider.Endpoint(), Scopes: []string{ "email", "profile", oidc.ScopeOfflineAccess, oidc.ScopeOpenID, }, } router := http.NewServeMux() router.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { session := SessionFor(sessions, r, w) tmpl := x.Must(template.New("index.html").Delims("<%=", "%>").ParseFiles("cmd/ui/index.html")) tmpl.Execute(w, session) session.Message = "" })) router.Handle("/login", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { session := SessionFor(sessions, r, w) session.OAuthState = uuid.GenerateUUID() url := cfg.AuthCodeURL(session.OAuthState, oauth2.SetAuthURLParam("audience", os.Getenv("AUTH0_AUDIENCE"))) http.Redirect(w, r, url, http.StatusTemporaryRedirect) })) router.Handle("/callback", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { session := SessionFor(sessions, r, w) state := r.URL.Query().Get("state") if state != session.OAuthState { session.Flash("Invalid state parameter", w, r) return } client := &http.Client{Transport: x.LoggingRoundTripper{http.DefaultTransport}} token := x.Must(cfg.Exchange(context.WithValue(r.Context(), oauth2.HTTPClient, client), r.URL.Query().Get("code"))) rawIDToken, ok := token.Extra("id_token").(string) if !ok { log.Panic("no id_token field in oauth2 token") } idToken := x.Must(provider. Verifier(&oidc.Config{ClientID: cfg.ClientID}). Verify(r.Context(), rawIDToken)) var profile map[string]interface{} idToken.Claims(&profile) parts := strings.Split(token.AccessToken, ".") if len(parts) == 3 { json.Unmarshal(x.Must(base64.RawURLEncoding.DecodeString(parts[1])), &session.AccessTokenClaims) } session.Token = token session.IdToken = idToken session.IdTokenRaw = token.Extra("id_token") session.Profile = profile http.Redirect(w, r, "/", http.StatusTemporaryRedirect) })) router.Handle("/logout", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { logoutURL := x.Must(url.Parse("https://" + os.Getenv("AUTH0_DOMAIN") + "/v2/logout")) returnTo := x.Must(url.Parse("http://" + r.Host)) parameters := url.Values{} parameters.Add("returnTo", returnTo.String()) parameters.Add("client_id", os.Getenv("AUTH0_CLIENT_ID")) logoutURL.RawQuery = parameters.Encode() http.Redirect(w, r, logoutURL.String(), http.StatusTemporaryRedirect) SessionFor(sessions, r, w).Reset() })) fmt.Println("listening localhost:3000") http.ListenAndServe("localhost:3000", router) }