golang-jwt / jwt

Go implementation of JSON Web Tokens (JWT).

Home Page:https://golang-jwt.github.io/jwt/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

ParseWithClaims fails from json.NewDecoder

Aner-Git opened this issue · comments

commented

An error occurs for this simple code:

import (
	"testing"

	"github.com/golang-jwt/jwt/v5"
	"github.com/stretchr/testify/assert"
)

type cc struct {
	Foo string `json:"foo"`
	jwt.RegisteredClaims
}

func TestParseWClaims(t *testing.T) {

	token := jwt.NewWithClaims(jwt.SigningMethodHS256, cc{Foo: "value"})

	// Sign and get the complete encoded token as a string using the secret
	tokenString, err := token.SignedString([]byte("xxxx"))
	assert.Nil(t, err, "Sign failed")

	parser := &jwt.Parser{}
	var c cc
	ttoken, err := parser.ParseWithClaims(tokenString, c, func(t *jwt.Token) (any, error) { return []byte("xxxx"), nil })
	t.Logf("%+v", ttoken.Claims)
	assert.Nil(t, err, "Parse claims")

	assert.Equal(t, ttoken.Claims.(cc).Foo, "value")

}

Produces:
&fmt.wrapErrors{msg:"token is malformed: could not JSON decode claim: json: cannot unmarshal object into Go value of type jwt.Claims", errs:[]error{(*errors.errorString)(0xc0000588d0), (*json.UnmarshalTypeError)(0xc0000743c0)}}

go version
go version go1.20.4 linux/amd64

The error occurs calling Decode of a json decoder. Here is a simplified example of the call stack in the code.
Note the func call which 'simulates' the call to ParseWithClaims.

type cc struct {
	Foo string `json:"foo"`
	jwt.RegisteredClaims
}

func TestJsonDec(t *testing.T) {

	bs := []byte(`{"Foo":"value"}`)

	func(claim jwt.Claims) {
		c := claim
		dec := json.NewDecoder(bytes.NewBuffer(bs))
		e := dec.Decode(&c)
		assert.Nil(t, e)
	}(c)
}

Apart from using the map not clear how to solve this.

Pass the memory address of c, e.g.,

ttoken, err := parser.ParseWithClaims(tokenString, &c <--- here

and then assert Claims to your type (*cc) in this case, e.g.,

v, ok := ttoken.Claims.(*cc)
if ok {
  fmt.Println(v.Foo)
  // value
}

There is an example of custom claims here if it helps:

jwt/http_example_test.go

Lines 101 to 109 in 8aa5d6c

token, err := jwt.ParseWithClaims(tokenString, &CustomClaimsExample{}, func(token *jwt.Token) (interface{}, error) {
// since we only use the one private key to sign the tokens,
// we also only use its public counter part to verify
return verifyKey, nil
})
fatal(err)
claims := token.Claims.(*CustomClaimsExample)
fmt.Println(claims.CustomerInfo.Name)

commented
ttoken, err := jwt.ParseWithClaims(tokenString, &c, func(t *jwt.Token) (any, error) { return []byte("xxxx"), nil })

I get

jwt_test.go:63: &{Foo:value RegisteredClaims:{Issuer: Subject: Audience:[] ExpiresAt:<nil> NotBefore:<nil> IssuedAt:<nil> ID:}}
panic: interface conversion: jwt.Claims is *auth.cc, not auth.cc [recovered]
	panic: interface conversion: jwt.Claims is *auth.cc, not auth.cc

A few suggestions,

  1. assert for the type so you don't get panic (comma okay):
- assert.Equal(t, ttoken.Claims.(cc).Foo, "value")
+  customClaims, ok := ttoken.Claims.(cc)
  1. the type you're asserting for should be *cc and not cc
 ttoken.Claims.(*cc)

Moving this to the Q&A discussion.