I am using Auth0 to generate a JWT ID Token for the users. Now I would like to verify this ID Token in my go backend. I just want to extract the identity of the user from this key and check if it was created by my application in Auth0.
I am using the chi router with the jwtauth middleware. That is a lightweight wrapper around jwx.
The documentation says I should do the following:
tokenAuth = jwtauth.New("RS256", []byte("secret"), nil)
I have tried several things as "secret", like my Auth0 Application's client secret or the Signing Certificate. But none of them seems to work. I double-checked if I am using the correct Signature Algorithm.
It always results in token is unauthorized
.
r.Route("/users", func(r chi.Router) {
r.Use(jwtauth.Verifier(tokenAuth))
//r.Use(jwtauth.Authenticator)
r.Post("/info", usersService.InfoHandler)
})
In the handler I try to get the token:
token, _, err := jwtauth.FromContext(r.Context())
fmt.Println("token", token)
fmt.Println("error", err)
CodePudding user response:
I have now created a middleware to do this. It's pretty straightforward forward and I think there is no need to use the jwtauth middleware.
The best thing seems to use the JSON Web Key Set (JWKS). It contains all information to verify a JWT.
package auth0
import (
"context"
"fmt"
"github.com/lestrrat-go/jwx/jwk"
"github.com/lestrrat-go/jwx/jwt"
log "github.com/sirupsen/logrus"
"net/http"
)
var (
ErrInvalidToken = fmt.Errorf("invalid token")
ErrNoToken = fmt.Errorf("no token found")
)
type middleware struct {
keySet jwk.Set
audience string
issuer string
}
type AuthUser struct {
ID string `json:"id"`
Email string `json:"email"`
}
type userKeyType string
const userKey = userKeyType("user")
type Middleware interface {
AuthenticateUser(next http.Handler) http.Handler
}
var _ Middleware = &middleware{}
func NewMiddleware(issuer string, audience string) (middleware, error) {
// TODO implement auto rotation/refresh
keySet, err := jwk.Fetch(context.Background(), fmt.Sprintf("%s.well-known/jwks.json", issuer))
if err != nil {
return middleware{}, err
}
return middleware{
keySet: keySet,
audience: audience,
issuer: issuer,
}, nil
}
func (m *middleware) AuthenticateUser(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
authHeader := r.Header.Get("Authorization")
if authHeader == "" {
log.Debug("no authorization header found")
http.Error(w, ErrNoToken.Error(), http.StatusForbidden)
return
}
bearerToken := authHeader[7:]
if bearerToken == "" {
log.Error("no bearer token found")
http.Error(w, ErrNoToken.Error(), http.StatusForbidden)
return
}
token, err := jwt.Parse([]byte(bearerToken), jwt.WithKeySet(m.keySet))
if err != nil {
log.Error("error parsing token")
http.Error(w, ErrInvalidToken.Error(), http.StatusForbidden)
return
}
if err := jwt.Validate(token,
jwt.WithAudience(m.audience),
jwt.WithIssuer(m.issuer)); err != nil {
log.Error("error validating token")
http.Error(w, ErrInvalidToken.Error(), http.StatusForbidden)
return
}
emailValue, ok := token.Get("email")
if !ok {
log.Error("error no email found")
http.Error(w, ErrInvalidToken.Error(), http.StatusForbidden)
return
}
email, ok := emailValue.(string)
if !ok {
log.Error("error email not a string")
http.Error(w, ErrInvalidToken.Error(), http.StatusForbidden)
return
}
if token.Subject() == "" && email == "" {
log.Error("error no subject or email found")
http.Error(w, ErrInvalidToken.Error(), http.StatusForbidden)
return
}
ctx := context.WithValue(r.Context(), userKey, AuthUser{
ID: token.Subject(),
Email: email,
})
next.ServeHTTP(w, r.WithContext(ctx))
})
}
func GetUserFromContext(ctx context.Context) (AuthUser, error) {
user, ok := ctx.Value(userKey).(AuthUser)
if !ok {
return AuthUser{}, fmt.Errorf("could not get user from context")
}
return user, nil
}