Add Auth0 to supported OAuth2 providers

Auth0 is an OpenID Connect compliant OAuth2 provider, so we're able to
re-use the generic OAuth2 provider to implement it. The routes required
by Auth0 have been hardcoded for user convenience.

Also, Auth0 requires users to register a subdomain of auth0.com when
signing up. This must be provided to chronograf through the
`--auth0-domain` parameter (or `AUTH0_DOMAIN` ENV). This is **distinct**
from the `PUBLIC_URL`. For example, for a Chronograf hosted at
`http://www.example.com`, and an Auth0 domain of
`http://oceanic-airlines.auth0.com`, a client-id of `notpennysboat` and a
client-secret of `4-8-15-16-23-42`, the command line options would look
like:

```
chronograf \
  --auth0-domain=http://oceanic-airlines.auth0.com \
  --auth0-client-id=notpennysboat \
  --auth0-secret=4-8-15-16-23-24
  --public-url=http://www.example.com
  -t `uuidgen`
```
pull/10616/head
Tim Raymond 2017-06-19 16:27:55 -04:00
parent 34808ddb9d
commit 6c8e889b03
2 changed files with 78 additions and 1 deletions

47
oauth2/auth0.go Normal file
View File

@ -0,0 +1,47 @@
package oauth2
import (
"net/url"
"github.com/influxdata/chronograf"
)
type Auth0 struct {
Generic
}
func NewAuth0(auth0Domain, clientID, clientSecret, redirectURL string, logger chronograf.Logger) (Auth0, error) {
domain, err := url.Parse(auth0Domain)
if err != nil {
return Auth0{}, err
}
domain.Scheme = "https"
domain.Path = "/authorize"
authURL := domain.String()
domain.Path = "/oauth/token"
tokenURL := domain.String()
domain.Path = "/userinfo"
apiURL := domain.String()
return Auth0{
Generic: Generic{
PageName: "auth0",
ClientID: clientID,
ClientSecret: clientSecret,
RequiredScopes: []string{"openid"},
RedirectURL: redirectURL,
AuthURL: authURL,
TokenURL: tokenURL,
APIURL: apiURL,
Logger: logger,
},
}, nil
}

View File

@ -80,6 +80,10 @@ type Server struct {
StatusFeedURL string `long:"status-feed-url" description:"URL of a JSON Feed to display as a News Feed on the client Status page." default:"https://www.influxdata.com/feed/json" env:"STATUS_FEED_URL"`
Auth0Domain string `long:"auth0-domain" description:"Subdomain of auth0.com used for Auth0 OAuth2 authentication" env:"AUTH0_DOMAIN"`
Auth0ClientID string `long:"auth0-client-id" description:"Auth0 Client ID for OAuth2 support" env:"AUTH0_CLIENT_ID"`
Auth0ClientSecret string `long:"auth0-client-secret" description:"Auth0 Client Secret for OAuth2 support" env:"AUTH0_CLIENT_SECRET"`
ReportingDisabled bool `short:"r" long:"reporting-disabled" description:"Disable reporting of usage stats (os,arch,version,cluster_id,uptime) once every 24hr" env:"REPORTING_DISABLED"`
LogLevel string `short:"l" long:"log-level" value-name:"choice" choice:"debug" choice:"info" choice:"error" default:"info" description:"Set the logging level" env:"LOG_LEVEL"`
Basepath string `short:"p" long:"basepath" description:"A URL path prefix under which all chronograf routes will be mounted" env:"BASE_PATH"`
@ -113,6 +117,10 @@ func (s *Server) UseHeroku() bool {
return s.TokenSecret != "" && s.HerokuClientID != "" && s.HerokuSecret != ""
}
func (s *Server) UseAuth0() bool {
return s.Auth0ClientID != "" && s.Auth0ClientSecret != ""
}
// UseGenericOAuth2 validates the CLI parameters to enable generic oauth support
func (s *Server) UseGenericOAuth2() bool {
return s.TokenSecret != "" && s.GenericClientID != "" &&
@ -176,6 +184,27 @@ func (s *Server) genericOAuth(logger chronograf.Logger, auth oauth2.Authenticato
return &gen, genMux, s.UseGenericOAuth2
}
func (s *Server) auth0OAuth(logger chronograf.Logger, auth oauth2.Authenticator) (oauth2.Provider, oauth2.Mux, func() bool) {
redirectPath := path.Join(s.Basepath, "oauth", "auth0", "callback")
redirectURL, err := url.Parse(s.PublicURL)
if err != nil {
logger.Error("Error parsing public URL: err:", err)
return &oauth2.Auth0{}, &oauth2.AuthMux{}, func() bool { return false }
}
redirectURL.Path = redirectPath
auth0, err := oauth2.NewAuth0(s.Auth0Domain, s.Auth0ClientID, s.Auth0ClientSecret, redirectURL.String(), logger)
jwt := oauth2.NewJWT(s.TokenSecret)
genMux := oauth2.NewAuthMux(&auth0, auth, jwt, s.Basepath, logger)
if err != nil {
logger.Error("Error parsing Auth0 domain: err:", err)
return &auth0, genMux, func() bool { return false }
}
return &auth0, genMux, s.UseAuth0
}
func (s *Server) genericRedirectURL() string {
if s.PublicURL == "" {
return ""
@ -202,7 +231,7 @@ type BuildInfo struct {
}
func (s *Server) useAuth() bool {
return s.UseGithub() || s.UseGoogle() || s.UseHeroku() || s.UseGenericOAuth2()
return s.UseGithub() || s.UseGoogle() || s.UseHeroku() || s.UseGenericOAuth2() || s.UseAuth0()
}
func (s *Server) useTLS() bool {
@ -273,6 +302,7 @@ func (s *Server) Serve(ctx context.Context) error {
providerFuncs = append(providerFuncs, provide(s.googleOAuth(logger, auth)))
providerFuncs = append(providerFuncs, provide(s.herokuOAuth(logger, auth)))
providerFuncs = append(providerFuncs, provide(s.genericOAuth(logger, auth)))
providerFuncs = append(providerFuncs, provide(s.auth0OAuth(logger, auth)))
s.handler = NewMux(MuxOpts{
Develop: s.Develop,