2

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)
    }
Grokify
  • 15,092
  • 6
  • 60
  • 81
  • I think it is because your token is still valid: it is not expired, it was not refreshment, it was not revoked. It have the same claims, the same issuer and secret key. – ceth Aug 31 '19 at 07:01
  • What differences did you expect? Nothing changes inside the loop. – jps Aug 31 '19 at 08:00
  • @demas - Not because of that if I send this separate it will return different token(before expire). – isaman kumara Aug 31 '19 at 08:03
  • @jps - it should be different token. I added for loop for testing purpose. when we send 10 request one by one. getting same result. – isaman kumara Aug 31 '19 at 08:07
  • You only show your test code here, and seeing this test code I don't know what difference you expect, because nothing changes in the loop. In real scenario, at least the expiration time should be different, when the requests are not handled withing the same second. But the expiration time is a unix timestamp in seconds, so if you handle 10 requests in a second, the result doesn't differ. Edit your question to make it clear what you expect, your test code doesn't help. – jps Aug 31 '19 at 08:13
  • I want to get different tokens. eg : same person login in to system using different browser. so I want to keep many tokens. @jps. I think as you told mili second doesn't effect with the token.Anyway I get it your you point-out. – isaman kumara Aug 31 '19 at 08:44
  • usually you should also have a user ID or similar in the payload, to get different tokens for different users. And for the same user, it's not likely that he logs in on two browsers in the same second. But even if you get two identical tokens for the same user using two browsers, I don't see a problem with it. – jps Aug 31 '19 at 09:03

2 Answers2

5

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.

Community
  • 1
  • 1
David Maze
  • 130,717
  • 29
  • 175
  • 215
0

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.

ceth
  • 44,198
  • 62
  • 180
  • 289