~mna/webparts-auth0

5cee1ba719e129e39baeede788e585f26f961dcc — Martin Angers 1 year, 7 months ago
initial commit
6 files changed, 194 insertions(+), 0 deletions(-)

A .gitignore
A .golangci.toml
A README.md
A auth0.go
A go.mod
A go.sum
A  => .gitignore +6 -0
@@ 1,6 @@
# environment files (e.g. managed by direnv) and other secrets
/.env*

# output files for different tools, e.g. code coverage
/*.out


A  => .golangci.toml +30 -0
@@ 1,30 @@
[linters]
  disable-all = true
  enable = [
    "deadcode",
    "errcheck",
    "gochecknoinits",
    "gochecknoglobals",
    "gofmt",
    "golint",
    "gosec",
    "gosimple",
    "govet",
    "ineffassign",
    "interfacer",
    "misspell",
    "nakedret",
    "prealloc",
    "staticcheck",
    "structcheck",
    "typecheck",
    "unconvert",
    "unparam",
    "unused",
    "varcheck",
  ]

[issues]
  # regexps of issue texts to exclude
  exclude = [
  ]

A  => README.md +17 -0
@@ 1,17 @@
# webparts-auth0

[![GoDoc](https://godoc.org/git.sr.ht/~mna/webparts-auth0?status.svg)](https://godoc.org/git.sr.ht/~mna/webparts-auth0)

This repository provides an implementation of HTTP handlers for user authentication
and account management based on auth0.

## idea

Provide handlers for typical authentication scenarios (login, logout, 2FA, password reset,
email verification, etc.) as well as middleware for others (authorization based on authentication,
role, 2FA upgrade, etc.).

## see also

* webparts: https://git.sr.ht/~mna/webparts
* auth0: https://auth0.com/docs/quickstart/webapp

A  => auth0.go +96 -0
@@ 1,96 @@
package auth0

import (
	"context"
	"net/http"

	"git.sr.ht/~mna/webparts/http/httpssn"
	"github.com/coreos/go-oidc"
	"golang.org/x/oauth2"
)

// Config configures the auth0 handlers and middleware.
type Config struct {
	// ClientID is the auth0 client ID. See your auth0 dashboard:
	// https://manage.auth0.com/dashboard
	ClientID string
	// Domain is the domain for your auth0 application. See your auth0 dashboard:
	// https://manage.auth0.com/dashboard
	Domain string
	// ClientSecret is the client secret for your auth0 application. See your auth0
	// dashboard: https://manage.auth0.com/dashboard
	ClientSecret string
	// OauthCallbackURL is the URL that oauth redirects to after the user's
	// login.
	OauthCallbackURL string

	// ErrorFunc is the function to call when an error occurs in the auth flow.
	// If nil, http.Error is used with an http.StatusInternalServerError code
	// and the error's message.
	ErrorFunc func(w http.ResponseWriter, r *http.Request, err error)

	// SessionStore is the HTTP session store to use for auth0 authentication.
	// It stores the login state value as user profile information.
	SessionStore httpssn.Store
}

type Endpoints struct {
	provider *oidc.Provider
	config   oauth2.Config
	store    httpssn.Store
	errfn    func(w http.ResponseWriter, r *http.Request, err error)
}

func New(ctx context.Context, conf *Config) (*Endpoints, error) {
	provider, err := oidc.NewProvider(ctx, "https://"+conf.Domain)
	if err != nil {
		return nil, err
	}

	oc := oauth2.Config{
		ClientID:     conf.ClientID,
		ClientSecret: conf.ClientSecret,
		RedirectURL:  conf.OauthCallbackURL,
		Endpoint:     provider.Endpoint(),
		Scopes:       []string{oidc.ScopeOpenID, "profile"}, // TODO: what is this profile?
	}

	return &Endpoints{
		provider: provider,
		config:   oc,
		store:    conf.SessionStore,
		errfn:    errorFunc(conf.ErrorFunc),
	}, nil
}

func errorFunc(fn func(w http.ResponseWriter, r *http.Request, err error)) func(w http.ResponseWriter, r *http.Request, err error) {
	if fn != nil {
		return fn
	}
	return func(w http.ResponseWriter, r *http.Request, err error) {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

func (e *Endpoints) Login(w http.ResponseWriter, r *http.Request) {
}

func (e *Endpoints) Logout(w http.ResponseWriter, r *http.Request) {
}

func (e *Endpoints) LoginCallback(w http.ResponseWriter, r *http.Request) {
}

func (e *Endpoints) RequireAuth(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// TODO: check auth
		h.ServeHTTP(w, r)
	})
}

func (e *Endpoints) RequireRole(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// TODO: check role
		h.ServeHTTP(w, r)
	})
}

A  => go.mod +12 -0
@@ 1,12 @@
module git.sr.ht/~mna/webparts-auth0

go 1.13

require (
	git.sr.ht/~mna/webparts v0.0.0-20191029021002-192cf10606f8
	github.com/coreos/go-oidc v2.1.0+incompatible
	github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect
	golang.org/x/crypto v0.0.0-20191029031824-8986dd9e96cf // indirect
	golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
	gopkg.in/square/go-jose.v2 v2.4.0 // indirect
)

A  => go.sum +33 -0
@@ 1,33 @@
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
git.sr.ht/~mna/webparts v0.0.0-20191029021002-192cf10606f8 h1:BOZLZLCJNZR/fpugRNTVmOR/fUK9iiJ8v6Y2QUwbGsA=
git.sr.ht/~mna/webparts v0.0.0-20191029021002-192cf10606f8/go.mod h1:FOnYHb2lCpY5fycBxEF4BqtFDiTxW4AB0CJeoKXzwWo=
github.com/coreos/go-oidc v2.1.0+incompatible h1:sdJrfw8akMnCuUlaZU3tE/uYXFgfqom8DBE9so9EBsM=
github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 h1:J9b7z+QKAmPf4YLrFg6oQUotqHQeUNWwkvo7jZp1GLU=
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191029031824-8986dd9e96cf h1:fnPsqIDRbCSgumaMCRpoIoF2s4qxv0xSSS0BVZUE/ss=
golang.org/x/crypto v0.0.0-20191029031824-8986dd9e96cf/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e h1:bRhVy7zSSasaqNksaRZiA5EEI+Ei4I1nO5Jh72wfHlg=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/square/go-jose.v2 v2.4.0 h1:0kXPskUMGAXXWJlP05ktEMOV0vmzFQUWw6d+aZJQU8A=
gopkg.in/square/go-jose.v2 v2.4.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=