summaryrefslogtreecommitdiff
path: root/cmd/ui/main.go
blob: c224ecb491d2c2b662048bafa3018fd94ecc12b3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
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)
}