I am creating the token with the following code
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.StandardClaims{
Subject: string(user.Id),
})
tokenString, err := token.SignedString([]byte("secret"))
and trying to parse them with the following code
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, UnauthorizedError
}
return []byte("secret"), nil
})
if err != nil {
return -1, UnauthorizedError
}
if !token.Valid {
return -1, UnauthorizedError
}
claims, ok := token.Claims.(jwt.MapClaims)
if !ok {
return -1, UnauthorizedError
}
logrus.Info(claims)
Why can I not cast my claims to StandardClaims and access claims.Subject?
Conceptually, this is not possible because the
jwt.Parsefunction by default parses claims into an instance ofjwt.MapClaims. This is a fundamentally different data structure tojwt.StandardClaims; there is no way the compiler can automatically convert between the two using a simple type conversion as they represent the data differently.Resolution
The library provides the
ParseWithClaimsfunction, which allows you to specify your own implementer of thejwt.Claimsinterface for claims to be decoded into. You can pass an instance ofjwt.StandardClaims. For example:If possible, the claims will be parsed and decoded into the variable
token.Claims. The underlying (dynamic1) type of the value stored into this variable will be*jwt.StandardClaims. This can be used in a type assertion to recover the standard claims from the interface type:Let's dig into the language specification and library definition some more to provide a more rigorous assessment of this claim.
Background understanding of package types
jwt.MapClaimsis a defined type with underlying typemap[string]interface{}(code).jwt.StandardClaimsis a definedstructtype (code):Both types implement the
jwt.Claimsinterface type (definition), so is assignable to a variable of typejwt.Claims:The
Tokenstruct has a field calledClaimsof typejwt.Claims– any value which implements theClaimsinterface can be assigned toClaims.Type assertion definition
The language spec specifies for a type assertion expression of the form
x.(T)to be valid whenTis not an interface type, the dynamic type1 ofxmust be identical to the typeT. Here, you wish to evaluate the assertionx.(*jwt.StandardClaims); i.e. the asserted type is not an interface type.The code for
jwt.Parseeventually callsjwt.ParseWithClaimson the default parser, passing in an instance ofjwt.MapClaimsfor the claims destination:so the dynamic type of the
Claimsfield in your resulting token is of typejwt.MapClaims. This type is different (i.e. not identical) to the typejwt.StandardClaims, because user-defined types are always different from any other type except themselves. Hence, the type assertion fails.1Dynamic types (ref): recall in Go that interface types are fulfilled implicitly by any type which implements a super-set of the methods specified in an interface. If we define an interface of type
MyInterface, the variable declarationvar x MyInterfacehas static type (defined at compile time)MyInterface. However, at runtime, we can assign any value which implementsMyInterfacetox. The underlying type of the value assigned toxat any moment (the type which implements the interface) specifies the dynamic type of the variable.