英文:
Validate AWS Cognito JWT in GO
问题
我正在尝试验证从AWS Cognito(托管UI)返回的JWT。我注意到一旦在Cognito中完成登录,它会尝试使用一些参数访问我的应用,例如"id_token"和"access_token"。通过jwt.io进行检查,看起来"id_token"是JWT。
作为一个测试,我在GO中编写了一个期望带有JWT令牌和访问令牌的请求体的POST函数(并从这个答案中实现):
func auth(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
keyset, err := jwk.Fetch(context.Background(), "https://cognito-idp.{Region}.amazonaws.com/{poolID}/.well-known/jwks.json")
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
json.NewEncoder(w).Encode(&model.ErrorResponse{
Response: model.Response{
Result: false,
},
StatusCd: "500",
StatusDesc: "Failed to fetch jwks. Authorization failed.",
Error: "errRes",
})
}
authRequest := &model.AuthRequest{}
json.NewDecoder(r.Body).Decode(&authRequest)
parsedToken, err := jwt.Parse(
[]byte(authRequest.Token), //This is the JWT
jwt.WithKeySet(keyset),
jwt.WithValidate(true),
jwt.WithIssuer("https://cognito-idp.{Region}.amazonaws.com/{poolID}"),
jwt.WithAudience("{XX APP CLIENT ID XX}"),
jwt.WithClaimValue("key", authRequest.Access), //This is the Access Token
)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
json.NewEncoder(w).Encode(&model.ErrorResponse{
Response: model.Response{
Result: false,
},
StatusCd: "500",
StatusDesc: "Failed token parse. Authorization failed.",
Error: "errRes",
})
}
result := parsedToken
json.NewEncoder(w).Encode(result)
}
我正在使用的包有:
"github.com/lestrrat-go/jwx/jwk"
"github.com/lestrrat-go/jwx/jwt"
显然,在令牌解析时失败了。我做错了什么,还有我应该如何处理parsedToken
?
我对此还不熟悉,所以不知道这是否是正确的方法,希望能得到一些指导。
英文:
I am trying validate JWT returned from a login from AWS Cognito (hosted UI). I noticed that once the login is done in cognito, it tries to access my app with some params like "id_token" and "access_token". Checked with jwt.io and looks like "id_token" is the jwt.
As a test, I wrote a post function in GO expecting a body with the jwt token and the access token (and implemented from this answer)
func auth(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
keyset, err := jwk.Fetch(context.Background(), "https://cognito-idp.{Region}.amazonaws.com/{poolID}/.well-known/jwks.json")
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
json.NewEncoder(w).Encode(&model.ErrorResponse{
Response: model.Response{
Result: false,
},
StatusCd: "500",
StatusDesc: "Failed to fetch jwks. Authorization failed.",
Error: "errRes",
})
}
authRequest := &model.AuthRequest{}
json.NewDecoder(r.Body).Decode(&authRequest)
parsedToken, err := jwt.Parse(
[]byte(authRequest.Token), //This is the JWT
jwt.WithKeySet(keyset),
jwt.WithValidate(true),
jwt.WithIssuer("https://cognito-idp.{Region}.amazonaws.com/{poolID}"),
jwt.WithAudience("{XX APP CLIENT ID XX}"),
jwt.WithClaimValue("key", authRequest.Access), //This is the Access Token
)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
json.NewEncoder(w).Encode(&model.ErrorResponse{
Response: model.Response{
Result: false,
},
StatusCd: "500",
StatusDesc: "Failed token parse. Authorization failed.",
Error: "errRes",
})
}
result := parsedToken
json.NewEncoder(w).Encode(result)
}
Packages I am using are
"github.com/lestrrat-go/jwx/jwk"
"github.com/lestrrat-go/jwx/jwt"
Obviously, it failed at the token parse. What am I doing wrong and also what should I do with the parsedToken
?
I am new to this so, I have no clue if this is the correct approach and would really like some guidance.
答案1
得分: 1
如果您正在使用github.com/golang-jwt/jwt
包(以前称为github.com/dgrijalva/jwt-go
),那么您可能会从这个示例中受益:
您可以在这里查看更多的JWKs Go示例:github.com/MicahParks/keyfunc/tree/master/examples。
package main
import (
"fmt"
"log"
"time"
"github.com/golang-jwt/jwt"
"github.com/MicahParks/keyfunc"
)
func main() {
// 从您的AWS区域和userPoolId获取JWKs URL。
//
// 在这里查看AWS文档:
// https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-tokens-verifying-a-jwt.html
regionID := "" // TODO 获取您的AWS Cognito实例的区域ID。
userPoolID := "" // TODO 获取您的AWS Cognito实例的用户池ID。
jwksURL := fmt.Sprintf("https://cognito-idp.%s.amazonaws.com/%s/.well-known/jwks.json", regionID, userPoolID)
// 创建keyfunc选项。使用一个记录日志的错误处理程序。当发现一个未知KID签名的JWT或在指定的间隔时间内刷新JWKs时。限制刷新速率。在初始JWKs刷新请求之后超时10秒。这个超时时间也用于创建keyfunc.Get的初始context.Context。
refreshInterval := time.Hour
refreshRateLimit := time.Minute * 5
refreshTimeout := time.Second * 10
refreshUnknownKID := true
options := keyfunc.Options{
RefreshErrorHandler: func(err error) {
log.Printf("jwt.KeyFunc出现错误\n错误:%s\n", err.Error())
},
RefreshInterval: &refreshInterval,
RefreshRateLimit: &refreshRateLimit,
RefreshTimeout: &refreshTimeout,
RefreshUnknownKID: &refreshUnknownKID,
}
// 从给定URL的资源创建JWKs。
jwks, err := keyfunc.Get(jwksURL, options)
if err != nil {
log.Fatalf("无法从给定URL的资源创建JWKs。\n错误:%s\n", err.Error())
}
// 获取要解析的JWT。
jwtB64 := "eyJraWQiOiJmNTVkOWE0ZSIsInR5cCI6IkpXVCIsImFsZyI6IlJTMjU2In0.eyJzdWIiOiJLZXNoYSIsImF1ZCI6IlRhc2h1YW4iLCJpc3MiOiJqd2tzLXNlcnZpY2UuYXBwc3BvdC5jb20iLCJleHAiOjE2MTkwMjUyMTEsImlhdCI6MTYxOTAyNTE3NywianRpIjoiMWY3MTgwNzAtZTBiOC00OGNmLTlmMDItMGE1M2ZiZWNhYWQwIn0.vetsI8W0c4Z-bs2YCVcPb9HsBm1BrMhxTBSQto1koG_lV-2nHwksz8vMuk7J7Q1sMa7WUkXxgthqu9RGVgtGO2xor6Ub0WBhZfIlFeaRGd6ZZKiapb-ASNK7EyRIeX20htRf9MzFGwpWjtrS5NIGvn1a7_x9WcXU9hlnkXaAWBTUJ2H73UbjDdVtlKFZGWM5VGANY4VG7gSMaJqCIKMxRPn2jnYbvPIYz81sjjbd-sc2-ePRjso7Rk6s382YdOm-lDUDl2APE-gqkLWdOJcj68fc6EBIociradX_ADytj-JYEI6v0-zI-8jSckYIGTUF5wjamcDfF5qyKpjsmdrZJA"
// 解析JWT。
token, err := jwt.Parse(jwtB64, jwks.KeyFunc)
if err != nil {
log.Fatalf("无法解析JWT。\n错误:%s\n", err.Error())
}
// 检查令牌是否有效。
if !token.Valid {
log.Fatalf("令牌无效。")
}
log.Println("令牌有效。")
}
英文:
If you're using the github.com/golang-jwt/jwt
package (formally known as github.com/dgrijalva/jwt-go
,) then you'd probably benefit from this example:
You can check out more JWKs Go examples here: github.com/MicahParks/keyfunc/tree/master/examples.
package main
import (
"fmt"
"log"
"time"
"github.com/golang-jwt/jwt"
"github.com/MicahParks/keyfunc"
)
func main() {
// Get the JWKs URL from your AWS region and userPoolId.
//
// See the AWS docs here:
// https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-tokens-verifying-a-jwt.html
regionID := "" // TODO Get the region ID for your AWS Cognito instance.
userPoolID := "" // TODO Get the user pool ID of your AWS Cognito instance.
jwksURL := fmt.Sprintf("https://cognito-idp.%s.amazonaws.com/%s/.well-known/jwks.json", regionID, userPoolID)
// Create the keyfunc options. Use an error handler that logs. Refresh the JWKs when a JWT signed by an unknown KID
// is found or at the specified interval. Rate limit these refreshes. Timeout the initial JWKs refresh request after
// 10 seconds. This timeout is also used to create the initial context.Context for keyfunc.Get.
refreshInterval := time.Hour
refreshRateLimit := time.Minute * 5
refreshTimeout := time.Second * 10
refreshUnknownKID := true
options := keyfunc.Options{
RefreshErrorHandler: func(err error) {
log.Printf("There was an error with the jwt.KeyFunc\nError:%s\n", err.Error())
},
RefreshInterval: &refreshInterval,
RefreshRateLimit: &refreshRateLimit,
RefreshTimeout: &refreshTimeout,
RefreshUnknownKID: &refreshUnknownKID,
}
// Create the JWKs from the resource at the given URL.
jwks, err := keyfunc.Get(jwksURL, options)
if err != nil {
log.Fatalf("Failed to create JWKs from resource at the given URL.\nError:%s\n", err.Error())
}
// Get a JWT to parse.
jwtB64 := "eyJraWQiOiJmNTVkOWE0ZSIsInR5cCI6IkpXVCIsImFsZyI6IlJTMjU2In0.eyJzdWIiOiJLZXNoYSIsImF1ZCI6IlRhc2h1YW4iLCJpc3MiOiJqd2tzLXNlcnZpY2UuYXBwc3BvdC5jb20iLCJleHAiOjE2MTkwMjUyMTEsImlhdCI6MTYxOTAyNTE3NywianRpIjoiMWY3MTgwNzAtZTBiOC00OGNmLTlmMDItMGE1M2ZiZWNhYWQwIn0.vetsI8W0c4Z-bs2YCVcPb9HsBm1BrMhxTBSQto1koG_lV-2nHwksz8vMuk7J7Q1sMa7WUkXxgthqu9RGVgtGO2xor6Ub0WBhZfIlFeaRGd6ZZKiapb-ASNK7EyRIeX20htRf9MzFGwpWjtrS5NIGvn1a7_x9WcXU9hlnkXaAWBTUJ2H73UbjDdVtlKFZGWM5VGANY4VG7gSMaJqCIKMxRPn2jnYbvPIYz81sjjbd-sc2-ePRjso7Rk6s382YdOm-lDUDl2APE-gqkLWdOJcj68fc6EBIociradX_ADytj-JYEI6v0-zI-8jSckYIGTUF5wjamcDfF5qyKpjsmdrZJA"
// Parse the JWT.
token, err := jwt.Parse(jwtB64, jwks.KeyFunc)
if err != nil {
log.Fatalf("Failed to parse the JWT.\nError:%s\n", err.Error())
}
// Check if the token is valid.
if !token.Valid {
log.Fatalf("The token is not valid.")
}
log.Println("The token is valid.")
}
答案2
得分: 0
我建议首先进行最小的检查--即,首先尝试仅进行解析而不进行验证,然后逐步添加验证:
- jwt.Parse([]byte(token)) // 可能因为JWS而失败
- jwt.Parse([]byte(token), jwt.WithKeySet(...)) // 应该没问题?
- jwt.Parse(..., jwt.WithValidation(true), ...) // 逐步添加条件
请注意,我不知道id_token中有什么内容,因为我从未使用过Cognito。如果它是一个原始的JWT,你不应该需要一个密钥集,而且(1)应该可以工作。
英文:
I would suggest to start out by doing the minimal checks -- i.e., first try just parsing without validation, then add validations one by one:
- jwt.Parse([]byte(token)) // probably fails because of JWS
- jwt.Parse([]byte(token), jwt.WithKeySet(...)) // should be OK?
- jwt.Parse(..., jwt.WithValidation(true), ...) // add conditions one by one
Please note that I have no idea what's in id_token, as I have never used Cognito If it's a raw JWT, you shouldn't need a key set, and (1) should work.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论