Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

What is the purpose of Claims interface? #347

Answered by oxisto
koote asked this question in Q&A
Discussion options

Hi all, I am migrating my project from github.com/dgrijalva/jwt-go. I found that now the Claims interface includes a bunch of getters compares to the old version which only has Valid() for customer provided validation. I am confused as those getters seems trivial (maybe it is a stupid question) and my project uses custom claims and we don't even need those unnecessary claims like iss, sub, aud, etc,.

My project uses a custom struct for claims, like this:

type MyClaims struct {
 	OAuthID string `json:"oauthId,omitempty"`
	UserID string `json:"userId,omitempty"`
	UserEmail string `json:"userEmail,omitempty"`
	Roles []string `json:"roles,omitempty"` 
 ExpiresAt int64 `json:"exp,omitempty"`
}
func (mc MyClaims) Validate() error {
	now := time.Now().Unix()
	if now > pc.ExpiresAt {
		return errors.New("token expired")
	}
	if pc.UserID == "" || pc.UserEmail == "" || pc.OAuthID == "" {
		return errors.New("token doesn't have all the required claims set.")
	}
	return nil
}

When migrating to v5 jwt-go, compiler complains that the MyClaims doesn't implement Claims interface. So, to make it happy I added those functions:

type MyClaims struct {
 	jwt.RegisteredClaims
 	OAuthID string `json:"oauthId,omitempty"`
	UserID string `json:"userId,omitempty"`
	UserEmail string `json:"userEmail,omitempty"`
	Roles []string `json:"roles,omitempty"` 
 ExpiresAt int64 `json:"exp,omitempty"`
}
func (mc MyClaims) GetExpirationTime() (*jwtgo.NumericDate, error) {
	return pc.ExpiresAt, nil
}
func (mc MyClaims) GetIssuedAt() (*jwtgo.NumericDate, error) {
	return pc.IssuedAt, nil
}
func (mc MyClaims) GetNotBefore() (*jwtgo.NumericDate, error) {
	return pc.NotBefore, nil
}
func (mc MyClaims) GetIssuer() (string, error) {
	return pc.Issuer, nil
}
func (mc MyClaims) GetSubject() (string, error) {
	return pc.Subject, nil
}
func (mc MyClaims) GetAudience() (jwtgo.ClaimStrings, error) {
	return pc.Audience, nil
}

I am confused since those new added getters are just return corresponding fields introduced by RegisteredClaims, my custom token even doesn't want claims like iss, sub, aud, etc., Why do we need to be forced to implement those getters? Is it just to strictly follow the RFC standard?

Also, since those getters just return the corresponding fields, I cannot imagine when those getters could throw error, when could those getters return error?

Thanks.

You must be logged in to vote

Ok a couple of points

I am confused since those new added getters are just return corresponding fields introduced by RegisteredClaims, my custom token even doesn't want claims like iss, sub, aud, etc., Why do we need to be forced to implement those getters? Is it just to strictly follow the RFC standard?

Yes, it is primarily to follow the RFC standard. All fields which are optional in the RFC are optional though and can either be set to nil, e.g, for date-fields or an empty string or an empty array. Since you already embedding jwt.RegisteredClaims in your new example, you don't need to implement the interface. Go will check embedded structs, whether they implement the necessary function...

Replies: 2 comments

Comment options

Ok a couple of points

I am confused since those new added getters are just return corresponding fields introduced by RegisteredClaims, my custom token even doesn't want claims like iss, sub, aud, etc., Why do we need to be forced to implement those getters? Is it just to strictly follow the RFC standard?

Yes, it is primarily to follow the RFC standard. All fields which are optional in the RFC are optional though and can either be set to nil, e.g, for date-fields or an empty string or an empty array. Since you already embedding jwt.RegisteredClaims in your new example, you don't need to implement the interface. Go will check embedded structs, whether they implement the necessary functions for the interface.

The only function you might want to implement is Validate, if you want to check additional fields (such as your user ID, user email). See also this example here:

jwt/example_test.go

Lines 127 to 134 in 1691aa9

// Validate can be used to execute additional application-specific claims
// validation.
func (m MyCustomClaims) Validate() error {
if m.Foo != "bar" {
return errors.New("must be foobar")
}
return nil

Note: that this is performed in addition to regular validation, so you don't need to check the exp field on your own, it is already done by the validation helper.

Also, since those getters just return the corresponding fields, I cannot imagine when those getters could throw error, when could those getters return error?

There are specific corner-cases and those are the MapClaims, which are based fields in a map. Those could potentially be of the wrong type and we need to distinguish between a value that is empty (e.g., not present) and present but of wrong type.

Thanks.

You are welcome :)

PS: It might be worth to have another look in the JWT RFCs and possible the ouath ones, there are a couple of "standard" fields that you could re-use, for example sub (Subject) is the principal that the token was created for, usually either your userID or a username.

You must be logged in to vote
0 replies
Answer selected by koote
Comment options

Thanks @oxisto for your quick response!

You must be logged in to vote
0 replies
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Category
Q&A
Labels
None yet
2 participants

AltStyle によって変換されたページ (->オリジナル) /