iain-logan / jwt

A Scala implementation of the JWT specification
MIT License
48 stars 16 forks source link

Optional private claims #2

Closed luksow closed 8 years ago

luksow commented 8 years ago

Hi,

I have problems implementing claims that are private and optional. What's preferred way of doing it?

If I put them on required list, validation fail if they're missing. If I put them on ignoredList, they don't appear in decoded token.

Thanks!

iain-logan commented 8 years ago

Hey, you raise on interesting point! I'm afraid however, the answer to your question may not be what you want to hear.

My implementation of the JWT spec does not really cater for the existence of optional claims, and this was a concious design choice. I feel that when validating a JWT, it is important to be explicit about all claims. We either ask that a claim exists explicitly, or that it may exist but we ignore it. This is important, as I don't want to allow potential clients of the JWT to be able to set arbitrary claims.

So, say we accept the above justification that when verifying a JWT all claims must be either required or ignored explicitly. Instead, consider the situation where we require your optional claim be set, but allow the value to be none or some value, in a similar fashion to Scala's Option type. Sadly JSON does not easily cater for this notion of optionality, so I don't believe this would be a desirable thing to attempt either.

I hope you can see why I've made these decisions. If you really do want to achieve optional claims, then there is a possible course of action, although I am unsure if it is a good idea.

In situations where you wish to cater for an optional claim when validating a JWT, you can first attempt to verify it with said optional claim. If the claim is found then you can continue with your program, but if the claim is not found then you can now attempt to verify the JWT without requiring the optional claim. This will allow you to emulate an optional claim, although I really can't recommend it. A better option may be to simply choose a default value for the claim, which indicates that the claim is not in use.

Does this answer satisfy you?

luksow commented 8 years ago

Hi, thanks for getting back to me!

While I respect your answer and decision, I agree only up to a point.

Many services that issue JWTs (ex. Google Identity Toolkit or Auth0) use optional claims. When you base your authentication on such services it's natural that you return user-encoded info that sometimes has full_name and sometimes don't (depending if it was already set or not).

I think it's very common to use JWTs as user-side, signed caches and that's when you especially need optional claims.

I feel that when validating a JWT, it is important to be explicit about all claims. We either ask that a claim exists explicitly, or that it may exist but we ignore it. This is important, as I don't want to allow potential clients of the JWT to be able to set arbitrary claims.

That's where I got lost. Usually it's server that sets (provides you with) claims - and clients has no way to put there anything as they're not able to sign modified JWT, right?

Sadly JSON does not easily cater for this notion of optionality, so I don't believe this would be a desirable thing to attempt either.

Hmm, if a field is not present in JSON (or value is null) we consider it optional, no? That's how it works in all major Scala JSON libraries.

Anyway, that's how I handled my optional claims:

  private case class FullName(value: Option[RFullName]) extends ClaimValue {
    override val field: ClaimField = FullName
    override val jsValue: JsValue = value.fold[JsValue](JsNull)(v => JsString(v.fullName.toString))
  }
  private object FullName extends ClaimField {
    override def attemptApply(value: JsValue): Option[ClaimValue] = value.asOpt[String].map(v => apply(Option(RFullName(v)))) orElse Option(apply(None))

    override val name: String = "full_name"
  }

It works as expected but requires to have full_name: null in some JWTs which is a waste of bandwidth.

iain-logan commented 8 years ago

Hmm, you are quite right.

Although this is not a good excuse, I've had my head stuck in my dissertation over the last few months, which has been in a rather different area. I should have more carefully considered my response to you.

I'm rather busy over the next month with final exams, but I shall go ahead and create an issue for this feature. When I have more time, l'll see what can be done to remedy the situation.

Thank you for your input!

luksow commented 8 years ago

Hey, good to hear I convinced you :smile: I'll dig into code and try to help you in some way with https://github.com/iain-logan/jwt/issues/4