英文:
Verify Auth0 ID Token - which secret do I need to use
问题
我正在使用Auth0为用户生成JWT ID令牌。现在我想在我的Go后端中验证这个ID令牌。我只想从这个令牌中提取用户的身份,并检查它是否是由我的应用程序在Auth0中创建的。
我正在使用chi路由器和jwtauth中间件。这是jwx的一个轻量级封装。
文档上说我应该这样做:
tokenAuth = jwtauth.New("RS256", []byte("secret"), nil)
我尝试了几个作为"secret"的值,比如我的Auth0应用程序的客户端密钥或签名证书。但是它们都不起作用。我仔细检查了是否使用了正确的签名算法。
结果总是显示token is unauthorized
。
在处理程序中,我尝试获取令牌:
token, _, err := jwtauth.FromContext(r.Context())
fmt.Println("token", token)
fmt.Println("error", err)
英文:
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)
答案1
得分: 2
我现在已经创建了一个中间件来完成这个任务。它非常简单直接,我认为没有必要使用jwtauth中间件。
最好的方法似乎是使用JSON Web Key Set (JWKS)。它包含了验证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
}
英文:
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
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论