jwt
jwt copied to clipboard
Add option to change time precision for creation/parsing tokens.
We can currently change the precision of timestamps getting serialized by modifying TimePrecision here.
However we are running into an issue where we have to serialize/validate different jwt tokens of various precisions (i.e. seconds, milliseconds).
As a work around, we are creating our own version of jwt.RegisteredClaims that serializes/deserializes times based on the precision that we want. However it would be nice if we can include a time precision option in jwt.New or jwt.ParseWithClaims
I had a similar feature in mind a while ago. This was previously not possible, but recently we changed the way the parser/validator works and both can now be supplied with options that are accessible in the respective (de)serialisation functions, so this might work.
You could probably take some hints from https://github.com/golang-jwt/jwt/pull/301. Specifically you need to apply the token options to the created token like this (which is not yet in main): https://github.com/golang-jwt/jwt/pull/301/files#diff-2feccbe0109db2f6b93af0c4afed26af571a61438266ebd8690bf669c75d2874R63-R66
Just to add, I am not quite sure why, but after writing these lines I think there was still some kind of issue with this we could not work around... But I cannot remember, so feel free to try it ;)
makes sense! Will take a crack and give and update!
Hmm I remember now why this probably won’t work. You will not have access to the options in the unmarshal func of NumericDate. I would be interested in how you solved this problem in your workaround.
You will not have access to the options in the unmarshal func of NumericDate.
Can Confirm, this is the issue i'm hitting against!
I am mainly working with the idea to add a precision field to NumericDate and remove any calls to truncate the time until it gets fetched, i.e.
type NumericDate struct {
t time.Time
precision time.Duration
}
func (d *NumericDate) Time() time.Time{
return d.t.Truncate(d.precision)
}
But like you said, parser won't be able to control the unmarshal issue. The one idea I had was to update the claims interface as:
type Claims interface {
GetExpirationTime() (*NumericDate, error)
GetIssuedAt() (*NumericDate, error)
GetNotBefore() (*NumericDate, error)
GetIssuer() (string, error)
GetSubject() (string, error)
GetAudience() (ClaimStrings, error)
SetPrecision(time.Duration)
}
And registered claims being something like:
func (c RegisteredClaims) SetPrecision(precision time.Duration) {
c.ExpiresAt.precision = precision
c.NotBefore.precision = precision
c.IssuedAt.precision = precision
}
And inside the parser, we call it before getting unmarshalled i.e.
token.Claims = claims
prec = // if options nil, use default time.Second
token.Claims.SetPrecision(prec)
...
err = json.Unmarshal(claimBytes, &claims)
That looks like it may work, but it feels a bit wonky. I think specifically main issues:
- This works very well for RegisteredClaims - not so much for MapClaims. There is maybe a hack where we store an additional precision field in the map, but I think that's dangerous to do.
- Adds additional complexity for users implementing their own claims
- Would require us to lose embedding of time object in NumericDate (although I can see a usecase where we set precision and truncate in place)
- This would only help with NumericDate that are part of Claims interface - if we define any other NumericDate in a custom claims, there isn't really a way for us to systematically update it.
I do think adding a precision field on the NumericDate object would be a step in the right direction, as that would give us more control, but not seeing a way for the parser to control that.
Curious on your thoughts!
we are probably stuck here until the v2 version of json arrives with options. See https://pkg.go.dev/github.com/go-json-experiment/json#Options