How to decode a JWT token in Go?
Asked Answered
U

6

48

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?

Unshaped answered 30/7, 2017 at 23:18 Comment(3)
github.com/dgrijalva/jwt-go/blob/master/example_test.goUnderrate
@Underrate I already saw it, but in that example I should have the struct with of the data that I will going to receive, in this case MyCustomClaims, but that can easily change from the other side and I don't want to be reestructuring that anytime that something is added or deletedUnshaped
Possible duplicate of Go Language and Verify JWTHeterosporous
T
57

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)
}
Troudeloup answered 31/7, 2017 at 6:8 Comment(3)
Be aware that if you are using custom claim based on jwt.StandardClaims (or standard claims directly), you should pass reference of the claims (jwt.ParseWithClaims(tokenString, &claims, ...). Maybe it saves someone else's couple minutes :)Morey
You can pass nil as third param KeyFunc if you don't need to verify token.Damiendamietta
Please note that dgrijalva/jwt-go has been unmaintained for a long time. See my answer for further detailsCockcrow
A
24

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.

Approve answered 14/6, 2019 at 14:40 Comment(0)
C
21

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
    }
Cockcrow answered 28/6, 2021 at 7:54 Comment(0)
D
10

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.

Damiendamietta answered 29/8, 2020 at 19:31 Comment(3)
doen't work in version 3.2.0, the error is keyFunc can't be nilBeekeeping
@You'reawesome that's the error, but the code doesn't compare err with nil, it compares the token.Damiendamietta
Note that this library ("github.com/dgrijalva/jwt-go") is discontinued; this fork is active: github.com/golang-jwt/jwtTranslunar
V
2

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.

Vitriform answered 31/7, 2017 at 7:21 Comment(3)
And if I dont have any VERIFICATION.PublicKey?Dalessandro
You might be able to find the Public/VerifyingKey on your server at an endpoint ending with .well-known/jwks.jsonGratulation
This library is discontinued; this fork is active: github.com/golang-jwt/jwtTranslunar
L
0

Another, probably better way to parse Claims without token signature validation using github.com/golang-jwt library

Again, this is applicable only in cases where you know the signature is valid (since it has already been or will be checked elsewhere in the stack) and you want to extract values from it. In my case it's an AWS lambda after API Gateway with JWT Authorizer, so I just want to get some data from the token.

import "github.com/golang-jwt/jwt/v5"

claims := jwt.MapClaims{}
_, _, err := jwt.NewParser().ParseUnverified(tokenString, claims)
if err != nil {
    return "", err
}
for key, val := range claims {
    fmt.Printf("Key: %v, value: %v\n", key, val)
}

I think this one is better since it doesn't ignore the err because of nil keyFunc.

Luca answered 8/5 at 22:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.