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?
Why can I not cast my claims to
StandardClaims
and accessclaims.Subject
?
Conceptually, this is not possible because the jwt.Parse
function by default parses claims into an instance of jwt.MapClaims
. This is a fundamentally different data structure to jwt.StandardClaims
; there is no way the compiler can automatically convert between the two using a simple type conversion as they represent the data differently.
The library provides the ParseWithClaims
function, which allows you to specify your own implementer of the jwt.Claims
interface for claims to be decoded into. You can pass an instance of jwt.StandardClaims
. For example:
token, err := jwt.ParseWithClaims(
tokenString, &jwt.StandardClaims{},
func(token *jwt.Token) (interface{}, error) {
// ...
},
)
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:
claims, ok := token.Claims.(*jwt.StandardClaims)
if !ok {
// handle type assertion failure
}
// do something with "claims"
Let's dig into the language specification and library definition some more to provide a more rigorous assessment of this claim.
jwt.MapClaims
is a defined type with underlying type map[string]interface{}
(code).
jwt.StandardClaims
is a defined struct
type (code):
type StandardClaims struct {
// Field set elided for brevity, as it is unimportant to the
// answer.
}
Both types implement the jwt.Claims
interface type (definition), so is assignable to a variable of type jwt.Claims
:
type Claims interface {
Valid() bool
}
The Token
struct has a field called Claims
of type jwt.Claims
– any value which implements the Claims
interface can be assigned to Claims
.
The language spec specifies for a type assertion expression of the form x.(T)
to be valid when T
is not an interface type, the dynamic type1 of x
must be identical to the type T
. Here, you wish to evaluate the assertion x.(*jwt.StandardClaims)
; i.e. the asserted type is not an interface type.
The code for jwt.Parse
eventually calls jwt.ParseWithClaims
on the default parser, passing in an instance of jwt.MapClaims
for the claims destination:
func (p *Parser) Parse(tokenString string, keyFunc Keyfunc) (*Token, error) {
return p.ParseWithClaims(tokenString, MapClaims{}, keyFunc)
}
so the dynamic type of the Claims
field in your resulting token is of type jwt.MapClaims
. This type is different (i.e. not identical) to the type jwt.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 declaration var x MyInterface
has static type (defined at compile time) MyInterface
. However, at runtime, we can assign any value which implements MyInterface
to x
. The underlying type of the value assigned to x
at any moment (the type which implements the interface) specifies the dynamic type of the variable.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With