"Log in with Google" JWT 发送到我的服务器时无效
"Log in with Google" JWT is invalid when sent to my server
设置:React 前端和 Golang 后端。
我的 React 前端成功从 Google:
获取令牌
<GoogleLogin
clientId="<client-id>.apps.googleusercontent.com"
onSuccess={response => responseGoogle(response)}
>
</GoogleLogin>
我有一个突变可以发送我需要的信息:
initiateTestMutation({
variables: {
idToken: response.getAuthResponse().id_token,
email: response.profileObj.email,
givenName: response.profileObj.givenName,
familyName: response.profileObj.familyName,
}
}
然后它发送一个令牌,我可以用 jwt.io 对其进行解码,但它显示“签名无效”。它包括我的正确信息,但同样无效。
在我的服务器端,我也尝试验证它但失败了。
// This is the token as a string
unencodedToken := *input.IDToken
fmt.Println(unencodedToken)
token, err := jwt.Parse(unencodedToken, func(token *jwt.Token) (interface{}, error){
return []byte("What goes here?"), nil
})
if err != nil {
fmt.Println("Could not decode the token")
fmt.Println(err)
}
if token.Valid {
fmt.Println("Valid token")
} else if ve, ok := err.(*jwt.ValidationError); ok {
if ve.Errors&jwt.ValidationErrorMalformed != 0 {
fmt.Println("That's not even a token")
} else if ve.Errors&(jwt.ValidationErrorExpired|jwt.ValidationErrorNotValidYet) != 0 {
// Token is either expired or not active yet
fmt.Println("Expired token")
} else {
fmt.Println("Couldn't handle this token:", err)
}
} else {
fmt.Println("Couldn't handle this token:", err)
}
其他信息:
- 这一切都是在本地完成的。
app.localhost
是请求 JWT 的域,它被添加为批准的来源
接近于:
我们的朋友 写了如何正确验证 JWT。
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodRS256); !ok {
return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
}
kid, ok := token.Header["kid"].(string)
if !ok {
return nil, errors.New("kid header not found")
}
keys := keySet.LookupKeyID(kid);
if len(keys) == 0 {
return nil, fmt.Errorf("key %v not found", kid)
}
// keys[0].Materialize() doesn't exist anymore
var raw interface{}
return raw, keys[0].Raw(&raw)
})
这是我的完整实现,以满足 Google 从此处提供的说明:https://developers.google.com/identity/sign-in/web/backend-auth
我可能比我需要的更频繁地验证事情,所以如果有人想编辑或评论,我会做出改变。
// Get the Key
unencodedToken := *input.IDToken
fetchedToken, err := jwk.FetchHTTP("https://www.googleapis.com/oauth2/v3/certs")
// Parse the token with standard claims
token, err := jwt.ParseWithClaims(unencodedToken, &jwt.StandardClaims{}, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
return nil, gqlerror.Errorf("Unexpected token signing method", token)
}
kid, ok := token.Header["kid"].(string)
if !ok {
fmt.Println("Could not find Key ID")
return nil, gqlerror.Errorf("Could not find key ID in token:", token)
}
keys := fetchedToken.LookupKeyID(kid)
if len(keys) == 0 {
fmt.Println("Could not find key in the signature")
return nil, gqlerror.Errorf("Could not find key in the signature: ", token)
}
var empty interface{}
return empty, keys[0].Raw(&empty)
})
if err != nil {
fmt.Println("Could not decode the token")
fmt.Println(err)
return nil, gqlerror.Errorf("Could not decode the token: ", token)
}
// Check if the token is valid
if token.Valid {
fmt.Println("Valid token")
} else if ve, ok := err.(*jwt.ValidationError); ok {
if ve.Errors&jwt.ValidationErrorMalformed != 0 {
fmt.Println("That's not even a token")
return nil, gqlerror.Errorf("Invalid Token")
} else if ve.Errors&(jwt.ValidationErrorExpired|jwt.ValidationErrorNotValidYet) != 0 {
// Token is either expired or not active yet
fmt.Println("Expired token")
return nil, gqlerror.Errorf("Expired Token:", token)
} else {
fmt.Println("Couldn't handle this token", token, err)
return nil, gqlerror.Errorf(err.Error())
}
} else {
fmt.Println("Couldn't handle this token", token, err)
return nil, gqlerror.Errorf(err.Error())
}
// Check if the claims are valid
err = token.Claims.Valid()
if err != nil {
fmt.Println("Failed validity check", err)
return nil, gqlerror.Errorf("Failed validity check on token", token, err.Error())
}
// Check the custom claims
if claims, ok := token.Claims.(*jwt.StandardClaims); ok && token.Valid {
audienceVerified := claims.VerifyAudience("773117128619-kfrd500nf8bfaq7anl7ee1ae7ucg5kp5.apps.googleusercontent.com", true)
if !audienceVerified {
// TODO Handle failure
fmt.Println("Audience unverified")
return nil, gqlerror.Errorf("Audience unverified on token", token)
}
}
if claims, ok := token.Claims.(*jwt.StandardClaims); ok && token.Valid {
netloc := claims.VerifyIssuer("accounts.google.com", true)
httpVersion := claims.VerifyIssuer("accounts.google.com", true)
if !netloc && !httpVersion {
// TODO Handle failure
fmt.Println("Can't verify issuer")
return nil, gqlerror.Errorf("Can't verify issuer on token", token)
}
}
设置:React 前端和 Golang 后端。
我的 React 前端成功从 Google:
获取令牌<GoogleLogin
clientId="<client-id>.apps.googleusercontent.com"
onSuccess={response => responseGoogle(response)}
>
</GoogleLogin>
我有一个突变可以发送我需要的信息:
initiateTestMutation({
variables: {
idToken: response.getAuthResponse().id_token,
email: response.profileObj.email,
givenName: response.profileObj.givenName,
familyName: response.profileObj.familyName,
}
}
然后它发送一个令牌,我可以用 jwt.io 对其进行解码,但它显示“签名无效”。它包括我的正确信息,但同样无效。
在我的服务器端,我也尝试验证它但失败了。
// This is the token as a string
unencodedToken := *input.IDToken
fmt.Println(unencodedToken)
token, err := jwt.Parse(unencodedToken, func(token *jwt.Token) (interface{}, error){
return []byte("What goes here?"), nil
})
if err != nil {
fmt.Println("Could not decode the token")
fmt.Println(err)
}
if token.Valid {
fmt.Println("Valid token")
} else if ve, ok := err.(*jwt.ValidationError); ok {
if ve.Errors&jwt.ValidationErrorMalformed != 0 {
fmt.Println("That's not even a token")
} else if ve.Errors&(jwt.ValidationErrorExpired|jwt.ValidationErrorNotValidYet) != 0 {
// Token is either expired or not active yet
fmt.Println("Expired token")
} else {
fmt.Println("Couldn't handle this token:", err)
}
} else {
fmt.Println("Couldn't handle this token:", err)
}
其他信息:
- 这一切都是在本地完成的。
app.localhost
是请求 JWT 的域,它被添加为批准的来源
接近于:
我们的朋友
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodRS256); !ok {
return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
}
kid, ok := token.Header["kid"].(string)
if !ok {
return nil, errors.New("kid header not found")
}
keys := keySet.LookupKeyID(kid);
if len(keys) == 0 {
return nil, fmt.Errorf("key %v not found", kid)
}
// keys[0].Materialize() doesn't exist anymore
var raw interface{}
return raw, keys[0].Raw(&raw)
})
这是我的完整实现,以满足 Google 从此处提供的说明:https://developers.google.com/identity/sign-in/web/backend-auth
我可能比我需要的更频繁地验证事情,所以如果有人想编辑或评论,我会做出改变。
// Get the Key
unencodedToken := *input.IDToken
fetchedToken, err := jwk.FetchHTTP("https://www.googleapis.com/oauth2/v3/certs")
// Parse the token with standard claims
token, err := jwt.ParseWithClaims(unencodedToken, &jwt.StandardClaims{}, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
return nil, gqlerror.Errorf("Unexpected token signing method", token)
}
kid, ok := token.Header["kid"].(string)
if !ok {
fmt.Println("Could not find Key ID")
return nil, gqlerror.Errorf("Could not find key ID in token:", token)
}
keys := fetchedToken.LookupKeyID(kid)
if len(keys) == 0 {
fmt.Println("Could not find key in the signature")
return nil, gqlerror.Errorf("Could not find key in the signature: ", token)
}
var empty interface{}
return empty, keys[0].Raw(&empty)
})
if err != nil {
fmt.Println("Could not decode the token")
fmt.Println(err)
return nil, gqlerror.Errorf("Could not decode the token: ", token)
}
// Check if the token is valid
if token.Valid {
fmt.Println("Valid token")
} else if ve, ok := err.(*jwt.ValidationError); ok {
if ve.Errors&jwt.ValidationErrorMalformed != 0 {
fmt.Println("That's not even a token")
return nil, gqlerror.Errorf("Invalid Token")
} else if ve.Errors&(jwt.ValidationErrorExpired|jwt.ValidationErrorNotValidYet) != 0 {
// Token is either expired or not active yet
fmt.Println("Expired token")
return nil, gqlerror.Errorf("Expired Token:", token)
} else {
fmt.Println("Couldn't handle this token", token, err)
return nil, gqlerror.Errorf(err.Error())
}
} else {
fmt.Println("Couldn't handle this token", token, err)
return nil, gqlerror.Errorf(err.Error())
}
// Check if the claims are valid
err = token.Claims.Valid()
if err != nil {
fmt.Println("Failed validity check", err)
return nil, gqlerror.Errorf("Failed validity check on token", token, err.Error())
}
// Check the custom claims
if claims, ok := token.Claims.(*jwt.StandardClaims); ok && token.Valid {
audienceVerified := claims.VerifyAudience("773117128619-kfrd500nf8bfaq7anl7ee1ae7ucg5kp5.apps.googleusercontent.com", true)
if !audienceVerified {
// TODO Handle failure
fmt.Println("Audience unverified")
return nil, gqlerror.Errorf("Audience unverified on token", token)
}
}
if claims, ok := token.Claims.(*jwt.StandardClaims); ok && token.Valid {
netloc := claims.VerifyIssuer("accounts.google.com", true)
httpVersion := claims.VerifyIssuer("accounts.google.com", true)
if !netloc && !httpVersion {
// TODO Handle failure
fmt.Println("Can't verify issuer")
return nil, gqlerror.Errorf("Can't verify issuer on token", token)
}
}