iain-logan / jwt

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

Ignore Unknown Claim #11

Open bjet007 opened 8 years ago

bjet007 commented 8 years ago

I've encounter a issue today with the library and unknown claim that is a bit related to #2 and #4. The new Draft of JWT(https://tools.ietf.org/html/draft-ietf-oauth-amr-values-03) that came out last week add a new claim that have already been implemented by major vendor, namely auth0.

Suddenly all my api were rejecting a valid JWT token, which is not something that I would expect.

According to the specification unknown claim should be ignored (https://tools.ietf.org/html/rfc7519) :

The set of claims that a JWT must contain to be considered valid is context dependent and is outside the scope of this specification. Specific applications of JWTs will require implementations to understand and process some claims in particular ways. However, in the absence of such requirements, all claims that are not understood by implementations MUST be ignored.

Is It something that you were aware?

nrktkt commented 6 years ago

Seconding this. I think that a desirable behavior would be something like: Unregistered, and unrequired claims are ignored. Registered, unrequired, and unignored claims are checked if present. Required and ignored claims behave obviously. I'd even take it a step further to modify the validation API to look something like

validateEncodedJwt(
                          jwt: String,
                          key: String,
                          requiredAlg: Algorithm,
                          requiredHeaders: Set[HeaderField],
                          requiredClaims: Set[ClaimValidation],
                          ignoredHeaders: Set[String] = Set(),
                          ignoredClaims: Set[String] = Set(),
                          charset: String = "UTF-8"
)
case class Iss(value: String) extends ClaimValue with ClaimValidation {
  override val field: ClaimField = Iss
  override val jsValue: JsValue = JsString(value)
  override def validate(otherValue: Any) = value.equals(otherValue)
}
case object Exp extends ClaimField with ClaimValidation{
  override def attemptApply(value: JsValue): Option[ClaimValue] =
    value.asOpt[Long].map(apply)

  override val name = "exp"

  override def validate(value: Any) = value match {
    case time: Long if System.currentTimeMillis / 1000 < time => true
    case _ => false   
  }
}

which could be used like this

DecodedJwt.validateEncodedJwt(
  jwt,                  
  "secret",              
  Algorithm.HS256,     
  Set(Typ),              
  Set((Iss("readme"), Exp)
)

this also opens up more flexibility for custom validation like

class ExpValidatorWithClock(clock: Clock) extends ClaimValidation {
  val name = "exp"
  def validate(value: Any) = value match {
    case time: Long if Instant.now(clock).getEpochSecond < time => true
    case _ => false   
  }
}

I'd be open to making a PR for this change if you're interested.