From 23e34032f69276bf4b0384781d302550c01d5dde Mon Sep 17 00:00:00 2001 From: Mathieu Leplatre Date: Mon, 4 Dec 2017 18:12:04 +0100 Subject: [PATCH] First phase, using payload info (ref #55) --- doorman/jwt.go | 15 ++++++++--- doorman/jwt_auth0.go | 28 +++++++++++++-------- doorman/jwt_auth0_mozilla.go | 49 ++++++++++++++++++++++++++++++++++++ 3 files changed, 78 insertions(+), 14 deletions(-) create mode 100644 doorman/jwt_auth0_mozilla.go diff --git a/doorman/jwt.go b/doorman/jwt.go index fb40be4..6146a86 100644 --- a/doorman/jwt.go +++ b/doorman/jwt.go @@ -2,6 +2,7 @@ package doorman import ( "net/http" + "strings" jwt "gopkg.in/square/go-jose.v2/jwt" ) @@ -31,14 +32,22 @@ func NewJWTValidator(issuer string) (JWTValidator, error) { // Reuse JWT validators instances among configs if they are for the same issuer. v, ok := jwtValidators[issuer] if !ok { - // XXX: currently only Auth0 is supported. - v = &Auth0Validator{ - Issuer: issuer, + if strings.Contains(issuer, "mozilla.auth0.com") { + v = &MozillaAuth0Validator{ + Issuer: issuer, + } + } else { + // Fallback on basic Auth0. + // XXX: Here is where we can add other Identity providers. + v = &Auth0Validator{ + Issuer: issuer, + } } err := v.Initialize() if err != nil { return nil, err } + jwtValidators[issuer] = v } return v, nil } diff --git a/doorman/jwt_auth0.go b/doorman/jwt_auth0.go index 14dfd04..467c3b8 100644 --- a/doorman/jwt_auth0.go +++ b/doorman/jwt_auth0.go @@ -18,18 +18,11 @@ type Auth0Validator struct { // Initialize will fetch Auth0 public keys and instantiate a validator. func (v *Auth0Validator) Initialize() error { - if !strings.HasPrefix(v.Issuer, "https://") || !strings.HasSuffix(v.Issuer, "auth0.com/") { - return fmt.Errorf("issuer %q not supported or has bad format", v.Issuer) + validator, err := auth0Validator(v.Issuer) + if err != nil { + return err } - jwksURI := fmt.Sprintf("%s.well-known/jwks.json", v.Issuer) - log.Infof("JWT keys: %s", jwksURI) - - // Will check audience only when request comes in, leave empty for now. - audience := []string{} - - client := auth0.NewJWKClient(auth0.JWKClientOptions{URI: jwksURI}) - config := auth0.NewConfiguration(client, audience, v.Issuer, jose.RS256) - v.validator = auth0.NewValidator(config) + v.validator = validator return nil } @@ -43,3 +36,16 @@ func (v *Auth0Validator) ExtractClaims(request *http.Request) (*Claims, error) { } return &claims, nil } + +func auth0Validator(issuer string) (*auth0.JWTValidator, error) { + if !strings.HasPrefix(issuer, "https://") || !strings.HasSuffix(issuer, "auth0.com/") { + return nil, fmt.Errorf("issuer %q not supported or has bad format", issuer) + } + jwksURI := fmt.Sprintf("%s.well-known/jwks.json", issuer) + log.Infof("JWT keys: %s", jwksURI) + // Will check audience only when request comes in, leave empty for now. + audience := []string{} + client := auth0.NewJWKClient(auth0.JWKClientOptions{URI: jwksURI}) + config := auth0.NewConfiguration(client, audience, issuer, jose.RS256) + return auth0.NewValidator(config), nil +} diff --git a/doorman/jwt_auth0_mozilla.go b/doorman/jwt_auth0_mozilla.go new file mode 100644 index 0000000..e257711 --- /dev/null +++ b/doorman/jwt_auth0_mozilla.go @@ -0,0 +1,49 @@ +package doorman + +import ( + "net/http" + + auth0 "github.com/auth0-community/go-auth0" + jwt "gopkg.in/square/go-jose.v2/jwt" +) + +// MozillaClaims uses specific attributes for emails and groups +type MozillaClaims struct { + Subject string `json:"sub,omitempty"` + Audience jwt.Audience `json:"aud,omitempty"` + Emails []string `json:"https://sso.mozilla.com/claim/emails,omitempty"` + Groups []string `json:"https://sso.mozilla.com/claim/groups,omitempty"` +} + +// MozillaAuth0Validator is the implementation of JWTValidator for Auth0. +type MozillaAuth0Validator struct { + Issuer string + validator *auth0.JWTValidator +} + +// Initialize will fetch Auth0 public keys and instantiate a validator. +func (v *MozillaAuth0Validator) Initialize() error { + validator, err := auth0Validator(v.Issuer) + if err != nil { + return err + } + v.validator = validator + return nil +} + +// ExtractClaims validates the token from request, and returns the JWT claims. +func (v *MozillaAuth0Validator) ExtractClaims(request *http.Request) (*Claims, error) { + token, err := v.validator.ValidateRequest(request) + mozclaims := MozillaClaims{} + err = v.validator.Claims(request, token, &mozclaims) + if err != nil { + return nil, err + } + claims := Claims{ + Subject: mozclaims.Subject, + Audience: mozclaims.Audience, + Email: mozclaims.Emails[0], + Groups: mozclaims.Groups, + } + return &claims, nil +}