package main import ( "context" "encoding/hex" "flag" "net/http" "time" "github.com/google/uuid" "github.com/o1egl/paseto/v2" "golang.org/x/crypto/ed25519" "golang.org/x/net/trace" ) var ( // Paseto pasetoPublicKey = flag.String("paseto-public-key", "", "Paseto public key (see tools/paseto-key-gen.go)") pasetoPrivateKey = flag.String("paseto-private-key", "", "Paseto private key") ) type GoTraceMiddleware struct{ next http.Handler } func (gtm GoTraceMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) { ctx := r.Context() tr := trace.New("http", r.URL.Path) ctx = trace.NewContext(ctx, tr) r = r.WithContext(ctx) gtm.next.ServeHTTP(w, r) tr.Finish() } type PasetoMiddleware struct { next http.Handler pubKey ed25519.PublicKey privKey ed25519.PrivateKey v2 paseto.V2 } func MakeMiddleware(pub, priv string, next http.Handler) (*PasetoMiddleware, error) { var result PasetoMiddleware result.next = next publicKey, err := hex.DecodeString(pub) if err != nil { return nil, err } result.pubKey = ed25519.PublicKey(publicKey) privateKey, err := hex.DecodeString(priv) if err != nil { return nil, err } result.privKey = ed25519.PrivateKey(privateKey) return &result, nil } func (pm PasetoMiddleware) CreateToken(data [][2]string, expiration time.Time) (string, error) { claims := paseto.JSONToken{ Expiration: expiration, Jti: uuid.New().String(), Subject: "Within", IssuedAt: time.Now(), } for _, datum := range data { claims.Set(datum[0], datum[1]) } tok, err := pm.v2.Sign(pm.privKey, claims, nil) if err != nil { return "", err } return tok, nil } var publicRoutes map[string]bool func init() { publicRoutes = map[string]bool{ "/webhooks/": true, "/static/": true, "/static/main.js": true, "/static/gruvbox.css": true, "/static/favicon.ico": true, "/static/favicon.png": true, "/debug/requests": true, "/debug/events": true, "/sign-in": true, } } type ctxKey int const ( tokenKey ctxKey = iota ) func (pm PasetoMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) { tok := r.Header.Get("Authorization") var newJsonToken paseto.JSONToken var newFooter string var err error if r.URL.EscapedPath() == "/.within/botinfo" || r.URL.EscapedPath() == "/" { goto ok } for k := range publicRoutes { if r.URL.EscapedPath() == k { goto ok } } err = pm.v2.Verify(tok, pm.pubKey, &newJsonToken, &newFooter) if err != nil { http.Error(w, "Not allowed", http.StatusForbidden) return } r = r.WithContext(context.WithValue(r.Context(), tokenKey, newJsonToken)) ok: pm.next.ServeHTTP(w, r) }