auth0 is a package helping to authenticate using the Auth0 service.
go get github.com/auth0-community/go-auth0
Using HS256, the validation key is the secret you retrieve in the dashboard.
// Creates a configuration with the Auth0 information
secret, _ := base64.URLEncoding.DecodeString(os.Getenv("AUTH0_CLIENT_SECRET"))
secretProvider := auth0.NewKeyProvider(secret)
audience := os.Getenv("AUTH0_CLIENT_ID")
configuration := auth0.NewConfiguration(secretProvider, []string{audience}, "https://mydomain.eu.auth0.com/", jose.HS256)
validator := auth0.NewValidator(configuration, nil)
token, err := validator.ValidateRequest(r)
if err != nil {
fmt.Println("Token is not valid:", token)
}
Using RS256, the validation key is the certificate you find in advanced settings
// Extracted from https://github.com/square/go-jose/blob/master/utils.go
// LoadPublicKey loads a public key from PEM/DER-encoded data.
// You can download the Auth0 pem file from `applications -> your_app -> scroll down -> Advanced Settings -> certificates -> download`
func LoadPublicKey(data []byte) (interface{}, error) {
input := data
block, _ := pem.Decode(data)
if block != nil {
input = block.Bytes
}
// Try to load SubjectPublicKeyInfo
pub, err0 := x509.ParsePKIXPublicKey(input)
if err0 == nil {
return pub, nil
}
cert, err1 := x509.ParseCertificate(input)
if err1 == nil {
return cert.PublicKey, nil
}
return nil, fmt.Errorf("square/go-jose: parse error, got '%s' and '%s'", err0, err1)
}
// Create a configuration with the Auth0 information
pem, err := ioutil.ReadFile("path/to/your/cert.pem")
if err != nil {
panic(err)
}
secret, err := LoadPublicKey(sharedKey)
if err != nil {
panic(err)
}
secretProvider := auth0.NewKeyProvider(secret)
audience := os.Getenv("AUTH0_CLIENT_ID")
configuration := auth0.NewConfiguration(secretProvider, []string{audience}, "https://mydomain.eu.auth0.com/", jose.RS256)
validator := auth0.NewValidator(configuration, nil)
token, err := validator.ValidateRequest(r)
if err != nil {
fmt.Println("Token is not valid:", token)
}
client := NewJWKClient(JWKClientOptions{URI: "https://mydomain.eu.auth0.com/.well-known/jwks.json"}, nil)
audience := os.Getenv("AUTH0_CLIENT_ID")
configuration := NewConfiguration(client, []string{audience}, "https://mydomain.eu.auth0.com/", jose.RS256)
validator := NewValidator(configuration, nil)
token, err := validator.ValidateRequest(r)
if err != nil {
fmt.Println("Token is not valid:", token)
}
opts := JWKClientOptions{URI: "https://mydomain.eu.auth0.com/.well-known/jwks.json"}
// Creating key cacher with max age of 100sec and max size of 5 entries.
// Defaults to persistent key cacher if not specified when creating a client.
keyCacher := NewMemoryKeyCacher(time.Duration(100) * time.Second, 5)
client := NewJWKClientWithCache(opts, nil, keyCacher)
searchedKey, err := client.GetKey("KEY_ID")
if err != nil {
fmt.Println("Cannot get key because of", err)
}
Using Gin and the Auth0 Authorization Extension, you may want to implement the authentication auth like the following:
var auth.AdminGroup string = "my_admin_group"
// Access Control Helper function.
func shouldAccess(wantedGroups []string, groups []interface{}) bool {
/* Fill depending on your needs */
}
// Wrapping a Gin endpoint with Auth0 Groups.
func Auth0Groups(wantedGroups ...string) gin.HandlerFunc {
return gin.HandlerFunc(func(c *gin.Context) {
tok, err := validator.ValidateRequest(c.Request)
if err != nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid token"})
c.Abort()
log.Println("Invalid token:", err)
return
}
claims := map[string]interface{}{}
err = validator.Claims(c.Request, tok, &claims)
if err != nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid claims"})
c.Abort()
log.Println("Invalid claims:", err)
return
}
metadata, okMetadata := claims["app_metadata"].(map[string]interface{})
authorization, okAuthorization := metadata["authorization"].(map[string]interface{})
groups, hasGroups := authorization["groups"].([]interface{})
if !okMetadata || !okAuthorization || !hasGroups || !shouldAccess(wantedGroups, groups) {
c.JSON(http.StatusUnauthorized, gin.H{"error": "need more privileges"})
c.Abort()
log.Println("Need more provileges")
return
}
c.Next()
})
}
// Use it
r.PUT("/news", auth.Auth0Groups(auth.AdminGroup), api.GetNews)
For a sample usage, take a look inside the example
directory.