When create JWT token inside loop getting same token in jwt-go

1.5k Views Asked by At

I am creating jwt tokens using jwt-go library. Later wrote a script to load test. I have noticed when I send the many concurrent request getting same token. To check more about this I created token inside for loop and result is same.

The library that I use is https://github.com/dgrijalva/jwt-go, go version is 1.12.9.

expirationTime := time.Now().Add(time.Duration(60) * time.Minute)

    for i := 1; i < 5; i++ {
        claims := &jwt.StandardClaims{
            ExpiresAt: expirationTime.Unix(),
            Issuer:"telescope",
        }
        _token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
        var jwtKey = []byte("secret_key")
        auth_token, _ := _token.SignedString(jwtKey)
        fmt.Println(auth_token)
    }
2

There are 2 best solutions below

0
On BEST ANSWER

A JWT contains three parts: a mostly-fixed header, a set of claims, and a signature. RFC 7519 has the actual details. If the header is fixed and the claims are identical between two tokens, then the signature will be identical too, and you can easily get duplicated tokens. The two timestamp claims "iat" and "exp" are only at a second granularity, so if you issue multiple tokens with your code during the same second you will get identical results (even if you move the expirationTime calculation inside the loop).

The jwt-go library exports the StandardClaims listed in RFC 7519 §4.1 as a structure, which is what you're using in your code. Digging through the library code, there's nothing especially subtle here: StandardClaims uses ordinary "encoding/json" annotations, and then when a token is written out, the claims are JSON encoded and then base64-encoded. So given a fixed input, you'll get a fixed output.

If you want every token to be "different" in some way, the standard "jti" claim is a place to provide a unique ID. This isn't part of the StandardClaims, so you need to create your own custom claim type that includes it.

type UniqueClaims struct {
    jwt.StandardClaims
    TokenId string `json:"jti,omitempty"`
}

Then when you create the claims structure, you need to generate a unique TokenId yourself.

import (
    "crypto/rand"
    "encoding/base64"
)

bits := make([]byte, 12)
_, err := rand.Read(bits)
if err != nil {
    panic(err)
}
claims := UniqueClaims{
    StandardClaims: jwt.StandardClaims{...},
    TokenId: base64.StdEncoding.EncodeToString(bits),
}

https://play.golang.org/p/zDnkamwsCi- has a complete example; every time you run it you will get a different token, even if you run it multiple times in the same second. You can base64 decode the middle part of the token by hand to see the claims, or use a tool like the https://jwt.io/ debugger to decode it.

1
On

I changed your code:

  • Moved calculation of expirationTime in the loop
  • Added 1 sec delay on each step of loop

    for i := 1; i < 5; i++ {
    
        expirationTime := time.Now().Add(time.Duration(60) * time.Minute)
    
        claims := &jwt.StandardClaims{
            ExpiresAt: expirationTime.Unix(),
            Issuer:    "telescope",
        }
        _token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
        var jwtKey = []byte("secret_key")
        auth_token, _ := _token.SignedString(jwtKey)
        fmt.Println(auth_token)
    
        time.Sleep(time.Duration(1) * time.Second)
    }
    

In this case we get different tokens:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NjcyNDcwNDgsImlzcyI6InRlbGVzY29wZSJ9.G7wV-zsCYjysLEdgYAq_92JGDPsgqqOz9lZxdh5gcX8
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NjcyNDcwNDksImlzcyI6InRlbGVzY29wZSJ9.yPNV20EN3XJbGiHhe-wGTdiluJyVHXj3nIqEsfwDZ0Q
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NjcyNDcwNTAsImlzcyI6InRlbGVzY29wZSJ9.W3xFXEiVwh8xK47dZinpXFpKuvUl1LFUAiaLZZzZ2L0
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NjcyNDcwNTEsImlzcyI6InRlbGVzY29wZSJ9.wYUbzdXm_VQGdFH9RynAVVouW9h6KI1tHRFJ0Y322i4

Sorry, I am not big expert in JWT and I hope somebody who is explain us this behavior from RFC point of view.

I want to get different tokens. eg : same person login in to system using different browser. so I want to keep many tokens.

It is the same user and we can get him the same token. If we want to give it another one we need to revoke previous one or the client must refresh it.