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.Parse
function 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
ParseWithClaims
function, which allows you to specify your own implementer of thejwt.Claims
interface 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.MapClaims
is a defined type with underlying typemap[string]interface{}
(code).jwt.StandardClaims
is a definedstruct
type (code):Both types implement the
jwt.Claims
interface type (definition), so is assignable to a variable of typejwt.Claims
:The
Token
struct has a field calledClaims
of typejwt.Claims
– any value which implements theClaims
interface can be assigned toClaims
.Type assertion definition
The language spec specifies for a type assertion expression of the form
x.(T)
to be valid whenT
is not an interface type, the dynamic type1 ofx
must 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.Parse
eventually callsjwt.ParseWithClaims
on the default parser, passing in an instance ofjwt.MapClaims
for the claims destination:so the dynamic type of the
Claims
field 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 MyInterface
has static type (defined at compile time)MyInterface
. However, at runtime, we can assign any value which implementsMyInterface
tox
. The underlying type of the value assigned tox
at any moment (the type which implements the interface) specifies the dynamic type of the variable.