Search code examples
gosecurityjwttinygo

How to decode a JWT token with TinyGo


We have a JWT token that we need to decode, the issue is that we are using TinyGo and some libraries are not supported, How can it be done for TinyGo / core Go libraries which is already supported? I want to print the "name" value:

I'm not able to get the name, any idea?

func main() {
    token := `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c`
    base64String := base64.StdEncoding.EncodeToString([]byte(token))
    decodedData, err := base64.StdEncoding.DecodeString(base64String)
    if err != nil {
        panic(err)
    }

    name := decodedData["name"]

    fmt.Println(name)

}

The decoded token is:

PAYLOAD
{
  "sub": "1234567890",
  "name": "John Doe",
  "iat": 1516239022
}

See this example token from https://jwt.io


Solution

  • Decoding and getting the name part is easy. But this does not ensure the token is valid, meaning the owner of the token is truly what the name says!

    JWT tokens just contain the base64 encoded forms of a header, payload and signature parts, connected with a .. So just split the token by ., decode the base64 string and you may use json.Unmarshal() to convert the header and playload parts to maps or structs.

    You must verify the signature to ensure the name is valid. If you don't perform signature verification, a token may easily be forged to pose as anyone. Signature verification is exactly what JWT libs do (besides parsing and generating tokens). How to do that, check the sources of JWT libs. I also believe there are open-source libs that process JWT tokens that also work with tiny-go.

    Example code to decode the parts and print the name:

    token := `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c`
    for i, part := range strings.Split(token, ".") {
        fmt.Printf("[%d] part: %s\n", i, part)
        decoded, err := base64.RawURLEncoding.DecodeString(part)
        if err != nil {
            panic(err)
        }
        fmt.Println("decoded:", string(decoded))
        if i != 1 {
            continue // i == 1 is the payload
        }
    
        var m map[string]interface{}
        if err := json.Unmarshal(decoded, &m); err != nil {
            fmt.Println("json decoding failed:", err)
            continue
        }
        if name, ok := m["name"]; ok {
            fmt.Println("name:", name)
        }
    }
    

    Which outputs (try it on the Go Playground):

    [0] part: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
    decoded: {"alg":"HS256","typ":"JWT"}
    [1] part: eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ
    decoded: {"sub":"1234567890","name":"John Doe","iat":1516239022}
    name: John Doe
    [2] part: SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
    decoded: I�J�IHNJ(]�O���lj~�:N�%_�u,×