验证 Auth0 ID Token – 我需要使用哪个密钥?

huangapple go评论67阅读模式
英文:

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
}

huangapple
  • 本文由 发表于 2022年6月15日 15:36:43
  • 转载请务必保留本文链接:https://go.coder-hub.com/72627512.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定