-
package older
import (
"encoding/json"
"errors"
"net/http"
"strings"
"github.com/dgrijalva/jwt-go"
)
type JSONWebKeys struct {
Kty string `json:"kty"`
Kid string `json:"kid"`
Use string `json:"use"`
N string `json:"n"`
E string `json:"e"`
X5c []string `json:"x5c"`
}
type Jwks struct {
Keys []JSONWebKeys `json:"keys"`
}
func GetSSOPemCert(token *jwt.Token, ssoJwksUri string) (string, error) {
cert := ""
resp, err := http.Get(ssoJwksUri)
if err != nil {
return cert, err
}
defer resp.Body.Close()
var jwks = Jwks{}
err = json.NewDecoder(resp.Body).Decode(&jwks)
if err != nil {
return cert, err
}
for k, _ := range jwks.Keys {
if token.Header["kid"] == jwks.Keys[k].Kid {
cert = "-----BEGIN CERTIFICATE-----\n" + jwks.Keys[k].X5c[0] + "\n-----END CERTIFICATE-----"
}
}
if cert == "" {
err := errors.New("unable to find appropriate key")
return cert, err
}
return cert, nil
}
func ValidateSSOToken(tokenStr string, ssoJwksUri string) (bool, error) {
token, _ := jwt.Parse(tokenStr, nil)
if token == nil {
return false, errors.New("invalid_token")
}
cert, err := GetSSOPemCert(token, ssoJwksUri)
if err != nil {
return false, err
}
key, err := jwt.ParseRSAPublicKeyFromPEM([]byte(cert))
if err != nil {
return false, err
}
parts := strings.Split(tokenStr, ".")
err = jwt.SigningMethodRS256.Verify(strings.Join(parts[0:2], "."), parts[2], key)
if err != nil {
return false, err
}
return true, nil
} and package newer
import (
"encoding/json"
"errors"
"net/http"
"strings"
"github.com/golang-jwt/jwt/v5"
)
type JSONWebKeys struct {
Kty string `json:"kty"`
Kid string `json:"kid"`
Use string `json:"use"`
N string `json:"n"`
E string `json:"e"`
X5c []string `json:"x5c"`
}
type Jwks struct {
Keys []JSONWebKeys `json:"keys"`
}
func GetSSOPemCert(token *jwt.Token, ssoJwksUri string) (string, error) {
cert := ""
resp, err := http.Get(ssoJwksUri)
if err != nil {
return cert, err
}
defer resp.Body.Close()
var jwks = Jwks{}
err = json.NewDecoder(resp.Body).Decode(&jwks)
if err != nil {
return cert, err
}
for k, _ := range jwks.Keys {
if token.Header["kid"] == jwks.Keys[k].Kid {
cert = "-----BEGIN CERTIFICATE-----\n" + jwks.Keys[k].X5c[0] + "\n-----END CERTIFICATE-----"
}
}
if cert == "" {
err := errors.New("unable to find appropriate key")
return cert, err
}
return cert, nil
}
func ValidateSSOToken(tokenStr string, ssoJwksUri string) (bool, error) {
token, _ := jwt.Parse(tokenStr, nil)
if token == nil {
return false, errors.New("invalid_token")
}
cert, err := GetSSOPemCert(token, ssoJwksUri)
if err != nil {
return false, err
}
key, err := jwt.ParseRSAPublicKeyFromPEM([]byte(cert))
if err != nil {
return false, err
}
parts := strings.Split(tokenStr, ".")
err = jwt.SigningMethodRS256.Verify(strings.Join(parts[0:2], "."), []byte(parts[2]), key)
if err != nil {
return false, err
}
return true, nil
} the older package and newer package use package abc_test
import (
"testing"
"github.com/ondbyte/abc/newer"
"github.com/ondbyte/abc/older"
"github.com/stretchr/testify/assert"
)
func TestIdtoken(t *testing.T) {
assert := assert.New(t)
idToken := `eyJhbGciOiJSUzI1NiIsImtpZCI6IjI1RTQyQzdGNUFENTAzNkJBNkRGQkU2MTlGNTI0NTQ3Q0JEN0IxRjZSUzI1NiIsIng1dCI6IkplUXNmMXJWQTJ1bTM3NWhuMUpGUjh2WHNmWSIsInR5cCI6ImF0K2p3dCJ9.eyJpc3MiOiJodHRwczovL2FoYXNzb2Rldi5oZWFydC5vcmciLCJuYmYiOjE3MDU2NzA0NjYsImlhdCI6MTcwNTY3MDQ2NiwiZXhwIjoxNzA1Njc0MDY2LCJzY29wZSI6WyJvcGVuaWQiLCJwcm9maWxlIl0sImFtciI6WyJwd2QiXSwiY2xpZW50X2lkIjoiQURIREVWIiwic3ViIjoiOGNmMWM2M2EtOTgwZC00OWJlLTllNjktYWQ5Zjg1Njk4NGZkIiwiYXV0aF90aW1lIjoxNzA1NjYxMTc4LCJpZHAiOiJsb2NhbCIsImNvdW50cnlvZlJlc2lkZW5jZSI6IntcIklTT1wiOlwiSE9OXCIsXCJDb2RlXCI6XCJITlwiLFwiTmFtZVwiOlwiSG9uZHVyYXNcIn0iLCJlbWFpbCI6InN1cGVyYWRtaW5AbWFpbC5jb20iLCJmaXJzdE5hbWUiOiJTdXBlciIsIlVJRCI6IjhjZjFjNjNhLTk4MGQtNDliZS05ZTY5LWFkOWY4NTY5ODRmZCIsImxhc3ROYW1lIjoiQWRtaW4iLCJwaG9uZU51bWJlciI6IisxNTY3NjU2Nzg5MCIsInBob3RvVVJMIjoiaHR0cHM6Ly9haGFzYWRldnNzby5ibG9iLmNvcmUud2luZG93cy5uZXQvcHJvZmlsZWltYWdlLzhjZjFjNjNhLTk4MGQtNDliZS05ZTY5LWFkOWY4NTY5ODRmZC5qcGciLCJyZWdpc3RyYXRpb25Tb3VyY2UiOiJBREgiLCJnaXZlbl9uYW1lMSI6IlN1cGVyIiwic2lkIjoiNjM0QzY1MjJENkY0NERFQUY4MzQwNDhBREExMTcwNEIifQ.o3U1xfJw7n9mpg5RzljWcMzY9AUrhl59S79vhGHxso_Uu3bsBs48cxoueVwLVXg6rgPDTM3vBuIlQ7uYRH-DLqQd1EL8t1CCX6VH8VoAZqVEHk7XVu7SdAaePTCMiE2yhVF3J_CPMb1VUtYf3WH2cpXsdePBFuscy5sc6TroR5VcoRIbYGBLCFKQYvMJ1iiu83s1-ExIjdZXs71yZX1ZzFGjJkK9tuwAhLBP9eMQEgVZ2nJ3b7n7Qj1omlZbjTEm8Hoq94hnoW9u5gO4rlnLurYDLdpJg7N55xP5_WfHTGVke8Ry3usrKlT6qDoRcIOJoWOTD8KZi66cJtxR4Yb18B72oIwxvRNC27sJSdDICxI0tzcRSRkBM9NO0wcVmx1fwD4eupqQ6teMNKcwWVVaLxzuVupwX5Jq8a_Fa9VA9XrlQv_kCr8rfnjPB7bNBckh4xBs4H_SmIQj4ig_EY8cs_ypZoxPU-Npr_Gn1uUJM8XOVc7RUO8FpWdjnu3fs2GVMgldtygsm6h4XIqqV15YtPfeDl7cs4yv5yOEmeNwuCf5ls75L0mpm7FrAI3YdIeZASWU4d8YgEvq79g0_o95VLSGVm_ZE6xs-5I2PRQatCd0K4RfT3-z6fitfeHb05wexmhu0TD51c6ll31d6C8BZQl-CayKBLN2_7fWkSftplE`
jwksUrl := "https://ahassodev.heart.org/.well-known/openid-configuration/jwks"
isOldValid, err1 := older.ValidateSSOToken(idToken, jwksUrl)
isNewValid, err2 := newer.ValidateSSOToken(idToken, jwksUrl)
assert.Equal(isOldValid, isNewValid)
assert.Equal(err1, err2)
} |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 2 replies
-
how do you build the |
Beta Was this translation helpful? Give feedback.
-
First of all: Thanks for upgrading from a "legacy" version to our current version. We know there are still a lot of users out there with the old library and the more we can switch over to the newer versions the better. There are several things to consider here.
Your code could look something like this: func ValidateSSOToken(tokenStr string, ssoJwksUri string) (bool, error) {
_, err := jwt.Parse(tokenStr, func(token *jwt.Token) (key any, err error) {
// You should probably check the token's signature alg here as well if it matches your
// expectations, or you can do that in GetSSOPemCert.
cert, err := GetSSOPemCert(token, ssoJwksUri)
if err != nil {
return nil, err
}
key, err = jwt.ParseRSAPublicKeyFromPEM([]byte(cert))
if err != nil {
return nil, err
}
return
})
// Maybe slightly better error handling, but you should get the point
if err != nil {
return false, err
}
return true, nil
} On a side note: If you are looking for a JWKS implementation, I suggest https://github.com/MicahParks/keyfunc, which provides an excellent library for this (I use it myself on a couple of projects). This directly integrates as a keyfunc into jwt.Parse -- but it introduces 1-2 dependencies in your code, in case you want/need to avoid this. |
Beta Was this translation helpful? Give feedback.
First of all: Thanks for upgrading from a "legacy" version to our current version. We know there are still a lot of users out there with the old library and the more we can switch over to the newer versions the better. There are several things to consider here.
We not only changed the signature on the
Verify
function from[]byte
tostring
, but also the "contents". Previously, it held the base64 content of the token signature, which each signature method needed to decode. This was not a good approach and we changed it so that base64-decoding now is done inParse
, andVerify
only gets the raw cryptographic signature. This is also why signature verification fails, if you supply it with the…