How to access below json values in go fiber? - validation

This is the code I wrote. When I used token.Claims method it returns below result. And I need to access email value also for check whether who is the user. When I used token.Claims.Email it throws an error.
func User(c *fiber.Ctx) error {
cookie := c.Cookies("jwt")
claims := jwt.MapClaims{}
token, _ := jwt.ParseWithClaims(cookie, claims, func(token *jwt.Token) (interface{}, error) {
return []byte(SecretKey), nil
})
return c.JSON(token.Claims)
}
This is the postman response. How to access below email address

Just by checking out your code I'm guessing that you are using this golang-jwt package.
After calling jwt.ParseWithClaims you should be able to access your claims over the already created jwt.MapClaims object named claims in your code.
Example without Fiber, only with golang-jwt, but logic stays the same:
package main
import (
"fmt"
"github.com/golang-jwt/jwt/v4"
)
// Created on https://jwt.io/
const tokenString = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InRlc3RAbWFpbC5jaCJ9.Iv6oZWIX7Rrtag4d6h3-eJ3xdXLwoZ9PbotcvbjOvhI"
// Just as an example, make sure to pick a stronger key
const key = "1234567890"
func main() {
claims := jwt.MapClaims{}
token, err := jwt.ParseWithClaims(tokenString, claims, keyFunc)
if err != nil {
panic(err)
}
fmt.Println(token.Valid)
email, ok := claims["email"].(string)
if !ok {
panic("Couldn't parse email as string")
}
fmt.Println(email)
}
func keyFunc(*jwt.Token) (interface{}, error) {
return []byte(key), nil
}

Related

How to iterate over the decoded claims of a Jwt token in Go?

I am referring to this post (How to decode a JWT token in Go?) to get the claim fields of a Jwt token. Here are the code that I used based on this post:
tokenString := "<YOUR TOKEN STRING>"
claims := jwt.MapClaims{}
token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) {
return []byte("<YOUR VERIFICATION KEY>"), nil
})
// ... error handling
// do something with decoded claims
for key, val := range claims {
fmt.Printf("Key: %v, value: %v\n", key, val)
}
I managed to get those claims and its values. However, in this Jwt token, there is a claim field called 'scope', and it has a string array values, like '['openId','username','email'].
Now I want to iterate over these values using loop.
for _, v := range claims["scope"]{
fmt.Println(v)
}
However, when I try to loop over this claims ["scope"], I got an error message saying that:
"cannot range over claims["scope"] (type interface {})".
How to iterate over these claims["scope"] values? Also, is it possible to convert it to other forms, like string array or json, to iterate over it?
Thanks.
There are a few ways you can handle this; the simplest is probably to let the library decode things for you (playground)
type MyCustomClaims struct {
Scope []string `json:"scope"`
jwt.StandardClaims
}
claims := MyCustomClaims{}
_, err := jwt.ParseWithClaims(tokenString, &claims, func(token *jwt.Token) (interface{}, error) {
return []byte(sharedKey), nil
})
if err != nil {
panic(err)
}
for _, s := range claims.Scope {
fmt.Printf("%s\n", s)
}
If you want to stick with the approach used in your question then you will need to use type assertions (playground - see the json docs for more info on this):
var claims jwt.MapClaims
_, err := jwt.ParseWithClaims(tokenString, &claims, func(token *jwt.Token) (interface{}, error) {
return []byte(sharedKey), nil
})
if err != nil {
panic(err)
}
scope := claims["scope"]
if scope == nil {
panic("no scope")
}
scopeSlc, ok := scope.([]any)
if !ok {
panic("scope not a slice")
}
for _, s := range scopeSlc {
sStr, ok := s.(string)
if !ok {
panic("slice member not a string")
}
fmt.Println(sStr)
}

How to validate API key in go-gin framework?

So I currently have a function that will take in a string APIKey to check it against my MongoDB collection. If nothing is found (not authenticated), it returns false - if a user is found, it returns true. My problem, however, is I'm unsure how to integrate this with a Gin POST route. Here is my code:
import (
"context"
"fmt"
"log"
"os"
"github.com/gin-gonic/gin"
_ "github.com/joho/godotenv/autoload"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
type User struct {
Name string
APIKey string
}
func validateAPIKey(users *mongo.Collection, APIKey string) bool {
var user User
filter := bson.D{primitive.E{Key: "APIKey", Value: APIKey}}
if err := users.FindOne(context.TODO(), filter).Decode(&user); err != nil {
fmt.Printf("Found 0 results for API Key: %s\n", APIKey)
return false
}
fmt.Printf("Found: %s\n", user.Name)
return true
}
func handleUpload(c *gin.Context) {
}
func main() {
r := gin.Default()
api := r.Group("/api")
v1 := api.Group("/v1")
v1.POST("/upload", handleUpload)
mongoURI := os.Getenv("MONGO_URI")
mongoOptions := options.Client().ApplyURI(mongoURI)
client, err := mongo.Connect(context.TODO(), mongoOptions)
if err != nil {
log.Fatal(err, "Unable to access MongoDB server, exiting...")
}
defer client.Disconnect(context.TODO())
// users := client.Database("sharex_api").Collection("authorized_users") // commented out when testing to ignore unused warnings
r.Run(":8085")
}
The validateAPIKey function works exactly as intended if tested alone, I am just unsure how I would run this function for a specific endpoint (in this case, /api/v1/upload) and pass in the users collection.
After a bit of searching, I found a resolution. I changed my validateAPIKey function to return git.HandlerFunc. Here's the code:
func validateAPIKey(users *mongo.Collection) gin.HandlerFunc {
return func(c *gin.Context) {
var user authorizedUser
APIKey := c.Request.Header.Get("X-API-Key")
filter := bson.D{primitive.E{Key: "APIKey", Value: APIKey}}
if err := users.FindOne(context.TODO(), filter).Decode(&user); err != nil {
fmt.Printf("Found 0 results for API Key: %s\n", APIKey)
c.JSON(http.StatusUnauthorized, gin.H{"status": 401, "message": "Authentication failed"})
return
}
return
}
}
For the route, I have the following:
v1.POST("/upload", validateAPIKey(users), handleUpload)

How to decode a JWT token in Go?

I am currently working on a Go application. I receive a JWT token from the client side and I need to decode that token and obtain the relevant information: user, name, etc.
I was checking the libraries that are available to handle JWT tokens and I came down to dgrijalva/jwt-go, but I don't see how to accomplish my goal in a simple way.
I have the token and I need to decode the info into a map or at least a json. How can I do it?
Function jwt.ParseWithClaims accept an interface of jwt.Claims as the second argument. Besides struct-based custom claims, the package also provides map-based claims, i.e. jwt.MapClaims.
So, you can simply decode the token into a MapClaims, e.g.
tokenString := "<YOUR TOKEN STRING>"
claims := jwt.MapClaims{}
token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) {
return []byte("<YOUR VERIFICATION KEY>"), nil
})
// ... error handling
// do something with decoded claims
for key, val := range claims {
fmt.Printf("Key: %v, value: %v\n", key, val)
}
Disclaimer: I am not affiliated to the library. I'm just a user, find it useful and would like to share.
It's 2019. I would like to suggest an alternate library that does pretty good job on JWT using JWS and/or JWE.
Here is the few examples on how to use the library:
import (
"gopkg.in/square/go-jose.v2/jwt"
"gopkg.in/square/go-jose.v2"
)
...
var claims map[string]interface{} // generic map to store parsed token
// decode JWT token without verifying the signature
token, _ := jwt.ParseSigned(tokenString)
_ = token.UnsafeClaimsWithoutVerification(&claims)
// decode JWT token and verify signature using JSON Web Keyset
token, _ := jwt.ParseSigned(tokenString)
jwks := &jose.JSONWebKeySet { // normally you can obtain this from an endpoint exposed by authorization server
Keys: []jose.JSONWebKey { // just an example
{
Key: publicKey,
Algorithm: jose.RS256, // should be the same as in the JWT token header
KeyID: "kid", // should be the same as in the JWT token header
},
},
}
_ = jwt.Claims(jwks, &claims)
Noted that, claims can be a struct that contains default JWT fields and also customized fields that are inside the token
E.g.:
import (
"github.com/mitchellh/mapstructure"
"gopkg.in/square/go-jose.v2/jwt"
)
...
type CustomClaims struct {
*jwt.Claims
// additional claims apart from standard claims
extra map[string]interface{}
}
func (cc *CustomClaims) UnmarshalJSON(b []byte) error {
var rawClaims map[string]interface{}
if err := json.Unmarshal(b, &rawClaims); err != nil {
return nil
}
var claims jwt.Claims
var decoderResult mapstructure.Metadata
decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
Result: &claims,
Metadata: &decoderResult,
TagName: "json",
})
if err != nil {
return err
}
if err := decoder.Decode(rawClaims); err != nil {
return err
}
cc.Claims = &claims
cc.extra = make(map[string]interface{})
for _, k := range decoderResult.Unused {
cc.extra[k] = rawClaims[k]
}
return nil
}
I also built a command line tool that uses the library to perform various encoding/decoding activities. It might be a useful reference on the usage of the library too.
Since both the question and answers mention the JWT library github.com/dgrijalva/jwt-go, please note that this library has been unmaintained for a long time now.
As of June 2021 there is a community fork golang-jwt/jwt, officially blessed by Dave Grijalva, the original author.
This also means that the library import path has changed. Note that the current major version v3 is not on Go modules, therefore you will still see v3.x.x+incompatible in your go.mod.
Edit: since August 2021 version v4 of golang-jwt/jwt is available. This finally supports Go modules. The new version is backward-compatible with previous versions, so in order to migrate simply replace the old import path with:
github.com/golang-jwt/jwt/v4
then update your modules as needed — see also the migration guide for details.
The fork most notably fixes an important security issue with the original library. Before the fix, the library didn't properly handle multiple aud in the JWT claims, making it actually not compliant with the JWT spec.
Apart from that, the main API is still the same. For example to parse a JWT with HMAC verification:
tokenString := /* raw JWT string*/
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, errors.New("unexpected signing method")
}
return []byte(/* your JWT secret*/), nil
})
if err != nil {
// handle err
}
// validate the essential claims
if !token.Valid {
// handle invalid tokebn
}
To parse a JWT with custom claims, you can define your own struct type and embed jwt.StandardClaims into it:
type MyClaims struct {
jwt.StandardClaims
MyField string `json:"my_field"`
}
tokenString := /* raw JWT string*/
// pass your custom claims to the parser function
token, err := jwt.ParseWithClaims(tokenString, &MyClaims{}, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, errors.New("unexpected signing method")
}
return []byte(/* your JWT secret*/), nil
})
// type-assert `Claims` into a variable of the appropriate type
myClaims := token.Claims.(*MyClaims)
A valid alternative to this library is lestrrat-go/jwx. The API is slightly different, but also very easy to use:
tokenString := /* raw JWT string*/
// parse and verify signature
tok, err := jwt.Parse(tokenString, jwt.WithVerify(jwa.HS256, []byte(/* your JWT secret */)))
if err != nil {
// handle err
}
// validate the essential claims
if err := jwt.Validate(tok); err != nil {
// handle err
}
If you want to get claims from jwt token without validation
import "github.com/dgrijalva/jwt-go"
...
token, err := jwt.Parse(tokenStr, nil)
if token == nil {
return nil, err
}
claims, _ := token.Claims.(jwt.MapClaims)
// claims are actually a map[string]interface{}
Note: code compares token with nil, not the err. The err will be keyFunc can't be nil.
Use github.com/dgrijalva/jwt-go go liabary for the implementation. we can extract JWT token information from the api request according to the following way.
When post the JWT token from the using post request. you must extract JWT information in routing section.
func RequireTokenAuthentication(inner http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
token, err := jwt.ParseFromRequest(
r,
func(token *jwt.Token) (interface{}, error) {
return VERIFICATION.PublicKey, nil
})
if err != nil || !token.Valid) {
log.Debug("Authentication failed " + err.Error())
w.WriteHeader(http.StatusForbidden)
return
} else {
r.Header.Set("username", token.Claims["username"].(string))
r.Header.Set("userid", strconv.FormatFloat((token.Claims["userid"]).(float64), 'f', 0, 64))
}
inner.ServeHTTP(w, r)
})
}
VERIFICATION.PublicKey : The key for verification(get public key from public.key file in your system)
Any Issue happen.Please let me know. I can give you help.

How to verify JWT signature with JWK in Go?

I have been searching for an example I can understand of how to validate the signature of a JWT with the Go Language.
This might be especially tricky since I am using Okta, and it uses JWKs, so it is not especially straight forward.
When I receive a JWT, I can decode it no problem. I just get stuck on how to verify the signature.
Below I have included a JWT and the JWK details. Can anyone provide signature validation with an example?
You can get to all of this information at https://oktaproxy.com/oidcgenerator.php — this site will generate a JWT from Okta, and the keys can be obtained at https://companyx.okta.com/oauth2/v1/keys.
Here is the JWT:
eyJhbGciOiJSUzI1NiIsImtpZCI6Ind5TXdLNEE2Q0w5UXcxMXVvZlZleVExMTlYeVgteHlreW1ra1h5Z1o1T00ifQ.eyJzdWIiOiIwMHUxOGVlaHUzNDlhUzJ5WDFkOCIsIm5hbWUiOiJva3RhcHJveHkgb2t0YXByb3h5IiwidmVyIjoxLCJpc3MiOiJodHRwczovL2NvbXBhbnl4Lm9rdGEuY29tIiwiYXVkIjoidlpWNkNwOHJuNWx4ck45YVo2ODgiLCJpYXQiOjE0ODEzODg0NTMsImV4cCI6MTQ4MTM5MjA1MywianRpIjoiSUQuWm9QdVdIR3IxNkR6a3RUbEdXMFI4b1lRaUhnVWg0aUotTHo3Z3BGcGItUSIsImFtciI6WyJwd2QiXSwiaWRwIjoiMDBveTc0YzBnd0hOWE1SSkJGUkkiLCJub25jZSI6Im4tMFM2X1d6QTJNaiIsInByZWZlcnJlZF91c2VybmFtZSI6Im9rdGFwcm94eUBva3RhLmNvbSIsImF1dGhfdGltZSI6MTQ4MTM4ODQ0MywiYXRfaGFzaCI6Im1YWlQtZjJJczhOQklIcV9CeE1ISFEifQ.OtVyCK0sE6Cuclg9VMD2AwLhqEyq2nv3a1bfxlzeS-bdu9KtYxcPSxJ6vxMcSSbMIIq9eEz9JFMU80zqgDPHBCjlOsC5SIPz7mm1Z3gCwq4zsFJ-2NIzYxA3p161ZRsPv_3bUyg9B_DPFyBoihgwWm6yrvrb4rmHXrDkjxpxCLPp3OeIpc_kb2t8r5HEQ5UBZPrsiScvuoVW13YwWpze59qBl_84n9xdmQ5pS7DklzkAVgqJT_NWBlb5uo6eW26HtJwHzss7xOIdQtcOtC1Gj3O82a55VJSQnsEEBeqG1ESb5Haq_hJgxYQnBssKydPCIxdZiye-0Ll9L8wWwpzwig
Here are the Keys:
{
"keys":[
{
"alg":"RS256",
"e":"AQAB",
"n":"ok6rvXu95337IxsDXrKzlIqw_I_zPDG8JyEw2CTOtNMoDi1QzpXQVMGj2snNEmvNYaCTmFf51I-EDgeFLLexr40jzBXlg72quV4aw4yiNuxkigW0gMA92OmaT2jMRIdDZM8mVokoxyPfLub2YnXHFq0XuUUgkX_TlutVhgGbyPN0M12teYZtMYo2AUzIRggONhHvnibHP0CPWDjCwSfp3On1Recn4DPxbn3DuGslF2myalmCtkujNcrhHLhwYPP-yZFb8e0XSNTcQvXaQxAqmnWH6NXcOtaeWMQe43PNTAyNinhndgI8ozG3Hz-1NzHssDH_yk6UYFSszhDbWAzyqw",
"kid":"wyMwK4A6CL9Qw11uofVeyQ119XyX-xykymkkXygZ5OM",
"kty":"RSA",
"use":"sig"
},
{
"alg":"RS256",
"e":"AQAB",
"n":"nXv6FSAcMjuanQ2hIIUb8Vkqe94t98kPh2T8-0j6-Jq8pOclgKdtVeIZcBE9F_XiuJvg4b6WVs-uvA-pS8mmMvQ21xU5Q_37Cojv8v_QlHWETHwEJdXXiY2Xq5LgXDSwEhhdDZHSMQYDuvhp_P6nl2LNqqUvJkjoFWcnn2btgSIUQROIaDdxtx7_2h4oUi5u11BGSF2SZZiEpDAKT08Htv3uwXdwDA6ll99fbi8X8RmH5oY_tIZTeIzu50qHxElPewoYO8QrJYsO9oFcCPMHGxYWjXQEa-QZYgo0wS9zRIkeJc5kshc4-9Uhv2DVIjk_-ofGlML9ieggGyillBKptw",
"kid":"GRF55Lbzgg4sANCmER-sm55eX_qUOpY8UTptDmDG_6U",
"kty":"RSA",
"use":"sig"
}
]
}
Below is an example of JWT decoding and verification. It uses both the jwt-go and jwk packages:
package main
import (
"errors"
"fmt"
"github.com/dgrijalva/jwt-go"
"github.com/lestrrat-go/jwx/jwk"
)
const token = `eyJhbGciOiJSUzI1NiIsImtpZCI6Ind5TXdLNEE2Q0w5UXcxMXVvZlZleVExMTlYeVgteHlreW1ra1h5Z1o1T00ifQ.eyJzdWIiOiIwMHUxOGVlaHUzNDlhUzJ5WDFkOCIsIm5hbWUiOiJva3RhcHJveHkgb2t0YXByb3h5IiwidmVyIjoxLCJpc3MiOiJodHRwczovL2NvbXBhbnl4Lm9rdGEuY29tIiwiYXVkIjoidlpWNkNwOHJuNWx4ck45YVo2ODgiLCJpYXQiOjE0ODEzODg0NTMsImV4cCI6MTQ4MTM5MjA1MywianRpIjoiSUQuWm9QdVdIR3IxNkR6a3RUbEdXMFI4b1lRaUhnVWg0aUotTHo3Z3BGcGItUSIsImFtciI6WyJwd2QiXSwiaWRwIjoiMDBveTc0YzBnd0hOWE1SSkJGUkkiLCJub25jZSI6Im4tMFM2X1d6QTJNaiIsInByZWZlcnJlZF91c2VybmFtZSI6Im9rdGFwcm94eUBva3RhLmNvbSIsImF1dGhfdGltZSI6MTQ4MTM4ODQ0MywiYXRfaGFzaCI6Im1YWlQtZjJJczhOQklIcV9CeE1ISFEifQ.OtVyCK0sE6Cuclg9VMD2AwLhqEyq2nv3a1bfxlzeS-bdu9KtYxcPSxJ6vxMcSSbMIIq9eEz9JFMU80zqgDPHBCjlOsC5SIPz7mm1Z3gCwq4zsFJ-2NIzYxA3p161ZRsPv_3bUyg9B_DPFyBoihgwWm6yrvrb4rmHXrDkjxpxCLPp3OeIpc_kb2t8r5HEQ5UBZPrsiScvuoVW13YwWpze59qBl_84n9xdmQ5pS7DklzkAVgqJT_NWBlb5uo6eW26HtJwHzss7xOIdQtcOtC1Gj3O82a55VJSQnsEEBeqG1ESb5Haq_hJgxYQnBssKydPCIxdZiye-0Ll9L8wWwpzwig`
const jwksURL = `https://companyx.okta.com/oauth2/v1/keys`
func getKey(token *jwt.Token) (interface{}, error) {
// TODO: cache response so we don't have to make a request every time
// we want to verify a JWT
set, err := jwk.FetchHTTP(jwksURL)
if err != nil {
return nil, err
}
keyID, ok := token.Header["kid"].(string)
if !ok {
return nil, errors.New("expecting JWT header to have string kid")
}
if key := set.LookupKeyID(keyID); len(key) == 1 {
return key[0].Materialize()
}
return nil, fmt.Errorf("unable to find key %q", keyID)
}
func main() {
token, err := jwt.Parse(token, getKey)
if err != nil {
panic(err)
}
claims := token.Claims.(jwt.MapClaims)
for key, value := range claims {
fmt.Printf("%s\t%v\n", key, value)
}
}
I've had a very similar use case recently so I read through some RFCs and authored this package: github.com/MicahParks/keyfunc.
It allows you to use the most popular JWT package, github.com/golang-jwt/jwt/v4, (formerly github.com/dgrijalva/jwt-go), to parse tokens. It can also automatically live reload the contents of your JWKS in a background goroutine.
Using your JWKS and JWT, here are two examples. The first will load the JWKS from a remote URL via HTTPS. The second will load it from static JSON.
From JWKS hosted via HTTPS
package main
import (
"context"
"log"
"time"
"github.com/golang-jwt/jwt/v4"
"github.com/MicahParks/keyfunc"
)
func main() {
// Get the JWKS URL.
//
// This is a sample JWKS service. Visit https://jwks-service.appspot.com/ and grab a token to test this example.
jwksURL := "https://jwks-service.appspot.com/.well-known/jwks.json"
// Create a context that, when cancelled, ends the JWKS background refresh goroutine.
ctx, cancel := context.WithCancel(context.Background())
// 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.
options := keyfunc.Options{
Ctx: ctx,
RefreshErrorHandler: func(err error) {
log.Printf("There was an error with the jwt.Keyfunc\nError: %s", err.Error())
},
RefreshInterval: time.Hour,
RefreshRateLimit: time.Minute * 5,
RefreshTimeout: time.Second * 10,
RefreshUnknownKID: true,
}
// 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", err.Error())
}
// Get a JWT to parse.
jwtB64 := "eyJraWQiOiJlZThkNjI2ZCIsInR5cCI6IkpXVCIsImFsZyI6IlJTMjU2In0.eyJzdWIiOiJXZWlkb25nIiwiYXVkIjoiVGFzaHVhbiIsImlzcyI6Imp3a3Mtc2VydmljZS5hcHBzcG90LmNvbSIsImlhdCI6MTYzMTM2OTk1NSwianRpIjoiNDY2M2E5MTAtZWU2MC00NzcwLTgxNjktY2I3NDdiMDljZjU0In0.LwD65d5h6U_2Xco81EClMa_1WIW4xXZl8o4b7WzY_7OgPD2tNlByxvGDzP7bKYA9Gj--1mi4Q4li4CAnKJkaHRYB17baC0H5P9lKMPuA6AnChTzLafY6yf-YadA7DmakCtIl7FNcFQQL2DXmh6gS9J6TluFoCIXj83MqETbDWpL28o3XAD_05UP8VLQzH2XzyqWKi97mOuvz-GsDp9mhBYQUgN3csNXt2v2l-bUPWe19SftNej0cxddyGu06tXUtaS6K0oe0TTbaqc3hmfEiu5G0J8U6ztTUMwXkBvaknE640NPgMQJqBaey0E4u0txYgyvMvvxfwtcOrDRYqYPBnA"
// Parse the JWT.
token, err := jwt.Parse(jwtB64, jwks.Keyfunc)
if err != nil {
log.Fatalf("Failed to parse the JWT.\nError: %s", err.Error())
}
// Check if the token is valid.
if !token.Valid {
log.Fatalf("The token is not valid.")
}
log.Println("The token is valid.")
// End the background refresh goroutine when it's no longer needed.
cancel()
// This will be ineffectual because the line above this canceled the parent context.Context.
// This method call is idempotent similar to context.CancelFunc.
jwks.EndBackground()
}
From JWKS as JSON
package main
import (
"encoding/json"
"log"
"github.com/golang-jwt/jwt/v4"
"github.com/MicahParks/keyfunc"
)
func main() {
// Get the JWKS as JSON.
jwksJSON := json.RawMessage(`{"keys":[{"kty":"RSA","e":"AQAB","kid":"ee8d626d","n":"gRda5b0pkgTytDuLrRnNSYhvfMIyM0ASq2ZggY4dVe12JV8N7lyXilyqLKleD-2lziivvzE8O8CdIC2vUf0tBD7VuMyldnZruSEZWCuKJPdgKgy9yPpShmD2NyhbwQIAbievGMJIp_JMwz8MkdY5pzhPECGNgCEtUAmsrrctP5V8HuxaxGt9bb-DdPXkYWXW3MPMSlVpGZ5GiIeTABxqYNG2MSoYeQ9x8O3y488jbassTqxExI_4w9MBQBJR9HIXjWrrrenCcDlMY71rzkbdj3mmcn9xMq2vB5OhfHyHTihbUPLSm83aFWSuW9lE7ogMc93XnrB8evIAk6VfsYlS9Q"},{"kty":"EC","crv":"P-256","kid":"711d48d1","x":"tfXCoBU-wXemeQCkME1gMZWK0-UECCHIkedASZR0t-Q","y":"9xzYtnKQdiQJHCtGwpZWF21eP1fy5x4wC822rCilmBw"},{"kty":"EC","crv":"P-384","kid":"d52c9829","x":"tFx6ev6eLs9sNfdyndn4OgbhV6gPFVn7Ul0VD5vwuplJLbIYeFLI6T42tTaE5_Q4","y":"A0gzB8TqxPX7xMzyHH_FXkYG2iROANH_kQxBovSeus6l_QSyqYlipWpBy9BhY9dz"},{"kty":"RSA","e":"AQAB","kid":"ecac72e5","n":"nLbnTvZAUxdmuAbDDUNAfha6mw0fri3UpV2w1PxilflBuSnXJhzo532-YQITogoanMjy_sQ8kHUhZYHVRR6vLZRBBbl-hP8XWiCe4wwioy7Ey3TiIUYfW-SD6I42XbLt5o-47IR0j5YDXxnX2UU7-UgR_kITBeLDfk0rSp4B0GUhPbP5IDItS0MHHDDS3lhvJomxgEfoNrp0K0Fz_s0K33hfOqc2hD1tSkX-3oDTQVRMF4Nxax3NNw8-ahw6HNMlXlwWfXodgRMvj9pcz8xUYa3C5IlPlZkMumeNCFx1qds6K_eYcU0ss91DdbhhE8amRX1FsnBJNMRUkA5i45xkOIx15rQN230zzh0p71jvtx7wYRr5pdMlwxV0T9Ck5PCmx-GzFazA2X6DJ0Xnn1-cXkRoZHFj_8Mba1dUrNz-NWEk83uW5KT-ZEbX7nzGXtayKWmGb873a8aYPqIsp6bQ_-eRBd8TDT2g9HuPyPr5VKa1p33xKaohz4DGy3t1Qpy3UWnbPXUlh5dLWPKz-TcS9FP5gFhWVo-ZhU03Pn6P34OxHmXGWyQao18dQGqzgD4e9vY3rLhfcjVZJYNlWY2InsNwbYS-DnienPf1ws-miLeXxNKG3tFydoQzHwyOxG6Wc-HBfzL_hOvxINKQamvPasaYWl1LWznMps6elKCgKDc"},{"kty":"EC","crv":"P-521","kid":"c570888f","x":"AHNpXq0J7rikNRlwhaMYDD8LGVAVJzNJ-jEPksUIn2LB2LCdNRzfAhgbxdQcWT9ktlc9M1EhmTLccEqfnWdGL9G1","y":"AfHPUW3GYzzqbTczcYR0nYMVMFVrYsUxv4uiuSNV_XRN3Jf8zeYbbOLJv4S3bUytO7qHY8bfZxPxR9nn3BBTf5ol"}]}`)
// Create the JWKS from the resource at the given URL.
jwks, err := keyfunc.NewJSON(jwksJSON)
if err != nil {
log.Fatalf("Failed to create JWKS from JSON.\nError: %s", err.Error())
}
// Get a JWT to parse.
jwtB64 := "eyJraWQiOiJlZThkNjI2ZCIsInR5cCI6IkpXVCIsImFsZyI6IlJTMjU2In0.eyJzdWIiOiJXZWlkb25nIiwiYXVkIjoiVGFzaHVhbiIsImlzcyI6Imp3a3Mtc2VydmljZS5hcHBzcG90LmNvbSIsImlhdCI6MTYzMTM2OTk1NSwianRpIjoiNDY2M2E5MTAtZWU2MC00NzcwLTgxNjktY2I3NDdiMDljZjU0In0.LwD65d5h6U_2Xco81EClMa_1WIW4xXZl8o4b7WzY_7OgPD2tNlByxvGDzP7bKYA9Gj--1mi4Q4li4CAnKJkaHRYB17baC0H5P9lKMPuA6AnChTzLafY6yf-YadA7DmakCtIl7FNcFQQL2DXmh6gS9J6TluFoCIXj83MqETbDWpL28o3XAD_05UP8VLQzH2XzyqWKi97mOuvz-GsDp9mhBYQUgN3csNXt2v2l-bUPWe19SftNej0cxddyGu06tXUtaS6K0oe0TTbaqc3hmfEiu5G0J8U6ztTUMwXkBvaknE640NPgMQJqBaey0E4u0txYgyvMvvxfwtcOrDRYqYPBnA"
// Parse the JWT.
token, err := jwt.Parse(jwtB64, jwks.Keyfunc)
if err != nil {
log.Fatalf("Failed to parse the JWT.\nError: %s", err.Error())
}
// Check if the token is valid.
if !token.Valid {
log.Fatalf("The token is not valid.")
}
log.Println("The token is valid.")
}
I came across a very similar use case where I wanted to verify/validate an access-token and extract fields(such as: iss, sub, aud, exp, iat, jti, etc..) from it after parsing/decoding. For my use case, I have used jwx and jwt-go lib.
I have tried the #tim-cooper example but it did not compile/work with the latest API version, so here is the code snippet which worked for me.
Code snippet
go.mod
module my-go-module
go 1.16
require (
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/lestrrat-go/jwx v1.0.4
)
Code
package main
import (
"errors"
"fmt"
"github.com/dgrijalva/jwt-go"
"github.com/lestrrat-go/jwx/jwa"
"github.com/lestrrat-go/jwx/jwk"
)
func main() {
jwksURL := "https://your-tenant.auth0.com/.well-known/jwks.json"
keySet, _ := jwk.Fetch(jwksURL)
var accessToken = "eyJhbGciOiJSUzI1NiIsImtpZCI6Ind5TXdLNEE2Q0w5UXcxMXVvZlZleVExMTlYeVgteHlreW1ra1h5Z1o1T00ifQ.eyJzdWIiOiIwMHUxOGVlaHUzNDlhUzJ5WDFkOCIsIm5hbWUiOiJva3RhcHJveHkgb2t0YXByb3h5IiwidmVyIjoxLCJpc3MiOiJodHRwczovL2NvbXBhbnl4Lm9rdGEuY29tIiwiYXVkIjoidlpWNkNwOHJuNWx4ck45YVo2ODgiLCJpYXQiOjE0ODEzODg0NTMsImV4cCI6MTQ4MTM5MjA1MywianRpIjoiSUQuWm9QdVdIR3IxNkR6a3RUbEdXMFI4b1lRaUhnVWg0aUotTHo3Z3BGcGItUSIsImFtciI6WyJwd2QiXSwiaWRwIjoiMDBveTc0YzBnd0hOWE1SSkJGUkkiLCJub25jZSI6Im4tMFM2X1d6QTJNaiIsInByZWZlcnJlZF91c2VybmFtZSI6Im9rdGFwcm94eUBva3RhLmNvbSIsImF1dGhfdGltZSI6MTQ4MTM4ODQ0MywiYXRfaGFzaCI6Im1YWlQtZjJJczhOQklIcV9CeE1ISFEifQ.OtVyCK0sE6Cuclg9VMD2AwLhqEyq2nv3a1bfxlzeS-bdu9KtYxcPSxJ6vxMcSSbMIIq9eEz9JFMU80zqgDPHBCjlOsC5SIPz7mm1Z3gCwq4zsFJ-2NIzYxA3p161ZRsPv_3bUyg9B_DPFyBoihgwWm6yrvrb4rmHXrDkjxpxCLPp3OeIpc_kb2t8r5HEQ5UBZPrsiScvuoVW13YwWpze59qBl_84n9xdmQ5pS7DklzkAVgqJT_NWBlb5uo6eW26HtJwHzss7xOIdQtcOtC1Gj3O82a55VJSQnsEEBeqG1ESb5Haq_hJgxYQnBssKydPCIxdZiye-0Ll9L8wWwpzwig"
token, err := verify(accessToken, keySet)
if err != nil {
fmt.Printf("Gor an error while verifiying access token: %v\n", err)
}
// Check if the token is valid.
if !token.Valid {
fmt.Println("The token is not valid.")
}
// Extract key value from the token and print them on console
claims := token.Claims.(jwt.MapClaims)
for key, value := range claims {
fmt.Printf("%s\t%v\n", key, value)
}
}
func verify(tokenString string, keySet *jwk.Set) (*jwt.Token, error) {
tkn, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
if token.Method.Alg() != jwa.RS256.String() {
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)
}
var raw interface{}
return raw, keys[0].Raw(&raw)
})
return tkn, err
}

Go and JWT - Simple authentication

I'm currently making an API (with go) and I'm working on the session part.
After research about what to use for session, I found JWT really interesting.
However I'm not really sure to understand how to use it after some tutorials.
So this is my idea:
func main() {
router := mux.NewRouter().StrictSlash(true)
router.HandleFunc("/login", login)
router.HandleFunc("/logout", logout)
router.HandleFunc("/register", register)
http.ListenAndServe(":8080", router)
}
After those requests handled, I create the differents functions.
func login(w http.ResponseWriter, r *http.Request) {
/*
Here I just have to search in my database (SQL, I know how to do it). If the user is registered, I create a token and give it to him, but how can I do it?
*/
}
func logout(w http.ResponseWriter, r *http.Request) {
/*
I get a token and stop/delete it?
*/
}
func register(w http.ResponseWriter, r *http.Request) {
/*
I search if the user isn't register and then, if it isn't, I create a user in the database (I know how to do it). I connect him but again, how to make a new token?
*/
}
Lot of tutorials on the web seems really hard but I just want something simple. I just want an handle package (code above) which work with a service package to have something like an engine token authentication.
A second point I'm not sure to understand is the saving of the token.
If a user connects himself, then what would be best? Each time the user runs their app, the app connects itself and get a new token from saved information (user/password) or the app just save the token forever? And what about the server, is the token managed and saved automatically with JWT or do I have to put it in my sql database?
Thank for your help !
EDIT 1
Thank you ! So after I read your answer, I encapsulated my code (token.go) like it
package services
import (
"fmt"
"github.com/dgrijalva/jwt-go"
"time"
"../models"
)
var tokenEncodeString string = "something"
func createToken(user models.User) (string, error) {
// create the token
token := jwt.New(jwt.SigningMethodHS256)
// set some claims
token.Claims["username"] = user.Username;
token.Claims["password"] = user.Password;
token.Claims["exp"] = time.Now().Add(time.Hour * 72).Unix()
//Sign and get the complete encoded token as string
return (token.SignedString(tokenEncodeString))
}
func parseToken(unparsedToken string) (bool, string) {
token, err := jwt.Parse(unparsedToken, func(token *jwt.Token) (interface{}, error) {
// Don't forget to validate the alg is what you expect:
if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
}
return myLookupKey(token.Header["kid"]), nil
})
if err == nil && token.Valid {
return true, unparsedToken
} else {
return false, ""
}
}
However, I got the following error: "token.go: undefined: myLookupKey"
I looked on internet and I found an encapsulated function which have this prototype:
func ExampleParse(myToken string, myLookupKey func(interface{}) (interface{}, error)) {
/* same code in my func parseToken() */
}
So what are the difference between my function and this one? How can I use this one?
Thanks !
Note:
This package github.com/dgrijalva/jwt-go is deprecated use this instead github.com/golang-jwt/jwt
To start, you need to import a JWT library in Golang (go get github.com/dgrijalva/jwt-go). You can find that library documentation in below link.
https://github.com/dgrijalva/jwt-go
Firstly, you need to create a token
// Create the token
token := jwt.New(jwt.SigningMethodHS256)
// Set some claims
token.Claims["foo"] = "bar"
token.Claims["exp"] = time.Now().Add(time.Hour * 72).Unix()
// Sign and get the complete encoded token as a string
tokenString, err := token.SignedString(mySigningKey)
Secondly, parse that token
token, err := jwt.Parse(myToken, func(token *jwt.Token) (interface{}, error) {
// Don't forget to validate the alg is what you expect:
if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
}
return myLookupKey(token.Header["kid"]), nil
})
if err == nil && token.Valid {
deliverGoodness("!")
} else {
deliverUtterRejection(":(")
}
Also, there are some examples for use JWT in GOlang like this https://github.com/slok/go-jwt-example
EDIT-1
package main
import (
"fmt"
"time"
"github.com/dgrijalva/jwt-go"
)
const (
mySigningKey = "WOW,MuchShibe,ToDogge"
)
func main() {
createdToken, err := ExampleNew([]byte(mySigningKey))
if err != nil {
fmt.Println("Creating token failed")
}
ExampleParse(createdToken, mySigningKey)
}
func ExampleNew(mySigningKey []byte) (string, error) {
// Create the token
token := jwt.New(jwt.SigningMethodHS256)
// Set some claims
token.Claims["foo"] = "bar"
token.Claims["exp"] = time.Now().Add(time.Hour * 72).Unix()
// Sign and get the complete encoded token as a string
tokenString, err := token.SignedString(mySigningKey)
return tokenString, err
}
func ExampleParse(myToken string, myKey string) {
token, err := jwt.Parse(myToken, func(token *jwt.Token) (interface{}, error) {
return []byte(myKey), nil
})
if err == nil && token.Valid {
fmt.Println("Your token is valid. I like your style.")
} else {
fmt.Println("This token is terrible! I cannot accept this.")
}
}
Just to make update of #massoud-afrashteh answer.
In version 3 of jwt-go setting clams should be
// Set some claims
claims := make(jwt.MapClaims)
claims["foo"] = "bar"
claims["exp"] = time.Now().Add(time.Hour * 72).Unix()
token.Claims = claims
Don't forget to run the command go get github.com/dgrijalva/jwt-go.
Another way to create more simple:
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
"foo": "bar",
"nbf": time.Date(2015, 10, 10, 12, 0, 0, 0, time.UTC).Unix(),
})
tokenString, err := token.SignedString([]byte("your key"))
fmt.Println(tokenString, err)
func GenerateToken(mySigningKey []byte, username string) (string, error) {
// Create the token
token := jwt.New(jwt.SigningMethodRS512)
claims := make(jwt.MapClaims)
claims[collections.PARAM_USER_NAME] = username
claims["exp"] = time.Now().Add(time.Hour * 72).Unix()
token.Claims = claims
return token.SignedString(mySigningKey)
}

Resources