Split Auth0 code from rest of JWT

This commit is contained in:
Mathieu Leplatre 2017-11-06 12:57:43 +01:00
Родитель e4a98e2d2c
Коммит 0e0926677b
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 767B105F81A15CDD
5 изменённых файлов: 91 добавлений и 72 удалений

45
doorman/auth0.go Normal file
Просмотреть файл

@ -0,0 +1,45 @@
package doorman
import (
"fmt"
"net/http"
"strings"
auth0 "github.com/auth0-community/go-auth0"
log "github.com/sirupsen/logrus"
jose "gopkg.in/square/go-jose.v2"
)
// Auth0Validator is the implementation of JWTValidator for Auth0.
type Auth0Validator struct {
Issuer string
validator *auth0.JWTValidator
}
// Initialize will fetch Auth0 public keys and instantiate a validator.
func (v *Auth0Validator) Initialize() error {
if !strings.HasSuffix(v.Issuer, "auth0.com/") {
return fmt.Errorf("issuer %q not supported or has bad format", v.Issuer)
}
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)
return nil
}
// ExtractClaims validates the token from request, and returns the JWT claims.
func (v *Auth0Validator) ExtractClaims(request *http.Request) (*Claims, error) {
token, err := v.validator.ValidateRequest(request)
claims := Claims{}
err = v.validator.Claims(request, token, &claims)
if err != nil {
return nil, err
}
return &claims, nil
}

43
doorman/auth0_test.go Normal file
Просмотреть файл

@ -0,0 +1,43 @@
package doorman
import (
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestAuth0Initialize(t *testing.T) {
validator := Auth0Validator{"demo.oath-zero.com/", nil}
err := validator.Initialize()
assert.NotNil(t, err)
}
func TestAuth0ExtractClaims(t *testing.T) {
var err error
validator := Auth0Validator{"https://minimal-demo-iam.auth0.com/", nil}
validator.Initialize()
r, _ := http.NewRequest("GET", "/", nil)
_, err = validator.ExtractClaims(r)
require.NotNil(t, err)
assert.Equal(t, "Token not found", err.Error())
r.Header.Set("Authorization", "Basic abc")
_, err = validator.ExtractClaims(r)
require.NotNil(t, err)
assert.Equal(t, "Token not found", err.Error())
r.Header.Set("Authorization", "Bearer abc zy")
_, err = validator.ExtractClaims(r)
require.NotNil(t, err)
assert.Equal(t, "square/go-jose: compact JWS format must have three parts", err.Error())
r.Header.Set("Authorization", "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik5EZzFOemczTlRFeVEwVTFNMEZCTnpCQlFqa3hOVVk1UTBVMU9USXpOalEzUXpVek5UWkRNQSJ9.eyJpc3MiOiJodHRwczovL21pbmltYWwtZGVtby1pYW0uYXV0aDAuY29tLyIsInN1YiI6Imdvb2dsZS1vYXV0aDJ8MTA0MTAyMzA2MTExMzUwNTc2NjI4IiwiYXVkIjpbImh0dHA6Ly9taW5pbWFsLWRlbW8taWFtLmxvY2FsaG9zdDo4MDAwIiwiaHR0cHM6Ly9taW5pbWFsLWRlbW8taWFtLmF1dGgwLmNvbS91c2VyaW5mbyJdLCJpYXQiOjE1MDY2MDQzMTMsImV4cCI6MTUwNjYxMTUxMywiYXpwIjoiV1lSWXBKeVM1RG5EeXhMVFJWR0NRR0NXR28yS05RTE4iLCJzY29wZSI6Im9wZW5pZCBwcm9maWxlIn0.JmfQajLJ6UMU8sGwv-4FyN0hAPjlLnixoVXAJwn9-985Y4jnMNiG22RWAk5qsdhxVKjIsyQFGA2oHuKELfcrI-LEHX3dxePxx9jSGUdC1wzk3p2q3YCRwIV3DUFEtBVeml8gdB9V7tVBE6XDivfq7RphiC8c5zz28_vlB2iPPaAwfucJLc1d5t83xlBaSYU9-hWDet3HbgjQg4zvFat6C2-CuKkCuQEG92tsOdoD8RIJtlWmLiMVUhCFgr3pGa7_ZNiKmMFkgZiDsX2qqD107CfOLG3IutcLGCqlpHxOuVltGZNp3QCXwtjIoZSV-5IXssXKLYuz-75GpfEAmUB5fg")
claims, err := validator.ExtractClaims(r)
require.Nil(t, err)
assert.Equal(t, "google-oauth2|104102306111350576628", claims.Subject)
}

Просмотреть файл

@ -22,6 +22,7 @@ func ContextMiddleware(doorman *Doorman) gin.HandlerFunc {
func SetupRoutes(r *gin.Engine, doorman *Doorman) {
r.Use(ContextMiddleware(doorman))
if doorman.JWTIssuer != "" {
// XXX: currently only Auth0 is supported.
validator := &Auth0Validator{
Issuer: doorman.JWTIssuer,
}

Просмотреть файл

@ -3,12 +3,8 @@ package doorman
import (
"fmt"
"net/http"
"strings"
auth0 "github.com/auth0-community/go-auth0"
"github.com/gin-gonic/gin"
log "github.com/sirupsen/logrus"
jose "gopkg.in/square/go-jose.v2"
jwt "gopkg.in/square/go-jose.v2/jwt"
)
@ -29,40 +25,6 @@ type JWTValidator interface {
ExtractClaims(*http.Request) (*Claims, error)
}
// Auth0Validator is the implementation of JWTValidator for Auth0.
type Auth0Validator struct {
Issuer string
validator *auth0.JWTValidator
}
// Initialize will fetch Auth0 public keys and instantiate a validator.
func (v *Auth0Validator) Initialize() error {
if !strings.HasSuffix(v.Issuer, "auth0.com/") {
return fmt.Errorf("issuer %q not supported or has bad format", v.Issuer)
}
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)
return nil
}
// ExtractClaims validates the token from request, and returns the JWT claims.
func (v *Auth0Validator) ExtractClaims(request *http.Request) (*Claims, error) {
token, err := v.validator.ValidateRequest(request)
claims := Claims{}
err = v.validator.Claims(request, token, &claims)
if err != nil {
return nil, err
}
return &claims, nil
}
// VerifyJWTMiddleware makes sure a valid JWT is provided.
func VerifyJWTMiddleware(validator JWTValidator) gin.HandlerFunc {
validator.Initialize()
@ -99,11 +61,13 @@ func VerifyJWTMiddleware(validator JWTValidator) gin.HandlerFunc {
var principals Principals
userid := fmt.Sprintf("userid:%s", claims.Subject)
principals = append(principals, userid)
// Main email (no alias)
if claims.Email != "" {
email := fmt.Sprintf("email:%s", claims.Email)
principals = append(principals, email)
}
// Groups
for _, group := range claims.Groups {
prefixed := fmt.Sprintf("group:%s", group)

Просмотреть файл

@ -15,40 +15,6 @@ import (
// TestMain defined in doorman_test.go
// func TestMain(m *testing.M) {}
func TestAuth0Initialize(t *testing.T) {
validator := Auth0Validator{"demo.oath-zero.com/", nil}
err := validator.Initialize()
assert.NotNil(t, err)
}
func TestAuth0ExtractClaims(t *testing.T) {
var err error
validator := Auth0Validator{"https://minimal-demo-iam.auth0.com/", nil}
validator.Initialize()
r, _ := http.NewRequest("GET", "/", nil)
_, err = validator.ExtractClaims(r)
require.NotNil(t, err)
assert.Equal(t, "Token not found", err.Error())
r.Header.Set("Authorization", "Basic abc")
_, err = validator.ExtractClaims(r)
require.NotNil(t, err)
assert.Equal(t, "Token not found", err.Error())
r.Header.Set("Authorization", "Bearer abc zy")
_, err = validator.ExtractClaims(r)
require.NotNil(t, err)
assert.Equal(t, "square/go-jose: compact JWS format must have three parts", err.Error())
r.Header.Set("Authorization", "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik5EZzFOemczTlRFeVEwVTFNMEZCTnpCQlFqa3hOVVk1UTBVMU9USXpOalEzUXpVek5UWkRNQSJ9.eyJpc3MiOiJodHRwczovL21pbmltYWwtZGVtby1pYW0uYXV0aDAuY29tLyIsInN1YiI6Imdvb2dsZS1vYXV0aDJ8MTA0MTAyMzA2MTExMzUwNTc2NjI4IiwiYXVkIjpbImh0dHA6Ly9taW5pbWFsLWRlbW8taWFtLmxvY2FsaG9zdDo4MDAwIiwiaHR0cHM6Ly9taW5pbWFsLWRlbW8taWFtLmF1dGgwLmNvbS91c2VyaW5mbyJdLCJpYXQiOjE1MDY2MDQzMTMsImV4cCI6MTUwNjYxMTUxMywiYXpwIjoiV1lSWXBKeVM1RG5EeXhMVFJWR0NRR0NXR28yS05RTE4iLCJzY29wZSI6Im9wZW5pZCBwcm9maWxlIn0.JmfQajLJ6UMU8sGwv-4FyN0hAPjlLnixoVXAJwn9-985Y4jnMNiG22RWAk5qsdhxVKjIsyQFGA2oHuKELfcrI-LEHX3dxePxx9jSGUdC1wzk3p2q3YCRwIV3DUFEtBVeml8gdB9V7tVBE6XDivfq7RphiC8c5zz28_vlB2iPPaAwfucJLc1d5t83xlBaSYU9-hWDet3HbgjQg4zvFat6C2-CuKkCuQEG92tsOdoD8RIJtlWmLiMVUhCFgr3pGa7_ZNiKmMFkgZiDsX2qqD107CfOLG3IutcLGCqlpHxOuVltGZNp3QCXwtjIoZSV-5IXssXKLYuz-75GpfEAmUB5fg")
claims, err := validator.ExtractClaims(r)
require.Nil(t, err)
assert.Equal(t, "google-oauth2|104102306111350576628", claims.Subject)
}
type TestValidator struct {
mock.Mock
}