lib-lti1p3-core icon indicating copy to clipboard operation
lib-lti1p3-core copied to clipboard

"error":"invalid_jwt_token","error_description":"iat missing"

Open alextoro85 opened this issue 2 years ago • 6 comments

After a succed login and launch validation, when I want to getContextMembershipForPayload (Lti1p3Nrps), it fails by responding "iat missing". I tried using OAT\Library\Lti1p3Core\Service\Client\LtiServiceClient directly. Same happens.

Whole error: Cannot get context membership for payload: Cannot get context membership for claim: Cannot get context membership: Cannot get access token: Client error: POST https://developer.anthology.com/api/v1/gateway/oauth2/jwttoken resulted in a 400 Bad Request response: {"error":"invalid_jwt_token","error_description":"iat missing"}

So it fails in getAccessToken from LtiServiceClient

The thing is that if I print the token claims... iat appears. So... very very strange.

Any ideas?

Thanks in advance

alextoro85 avatar Jan 04 '23 18:01 alextoro85

Same problem. I can see this is a Blackboard issue, isn't it? how did you solve it @alextoro85?

richard015ar avatar Jun 05 '24 13:06 richard015ar

Hi @richard015ar, If I'm not wrong... I think the issue here was Blackboard is not accepting microseconds. So, you can overwrite /src/Security/Jwt/Builder/Builder.php and just after $now = Carbon::now(); add: $now->setMicrosecond(0);

alextoro85 avatar Jun 05 '24 13:06 alextoro85

That did the trick, thank you @alextoro85! you saved me several hours debugging it. It's a little bit disappointing knowing that I'll have to fork or manually change it to make it work, only for Blackboard, tbh.

richard015ar avatar Jun 05 '24 14:06 richard015ar

/I know @richard015ar, very disappointing, as a tip, if you are using composer you can do something like I did:

"autoload": {
        "psr-4": {
            "App\\": "src/"
        },
        "exclude-from-classmap": [
            "vendor/oat-sa/lib-lti1p3-core/src/Security/Jwt/Builder/Builder.php"
        ],
        "files": [
            "app/override/lib-lti1p3-core/src/Security/Jwt/Builder/Builder.php"
        ]
}

alextoro85 avatar Jun 05 '24 14:06 alextoro85

Nice approach! I will definitely try it now!

richard015ar avatar Jun 05 '24 18:06 richard015ar

Checking the Specification for JWT https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.6.

[4.1.6](https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.6).  "iat" (Issued At) Claim

   The "iat" (issued at) claim identifies the time at which the JWT was
   issued.  This claim can be used to determine the age of the JWT.  Its
   value MUST be a number containing a NumericDate value.  Use of this
   claim is OPTIONAL.

According to JWT specification, IAT claim "MUST" be a NumericDate Value.

And NumericDate is defined in the document as follows,

NumericDate
      A JSON numeric value representing the number of seconds from
      1970-01-01T00:00:00Z UTC until the specified UTC date/time,
      ignoring leap seconds.  This is equivalent to the IEEE Std 1003.1,
      2013 Edition [[POSIX.1](https://datatracker.ietf.org/doc/html/rfc7519#ref-POSIX.1)] definition "Seconds Since the Epoch", in
      which each day is accounted for by exactly 86400 seconds, other
      than that non-integer values can be represented.  See [RFC 3339](https://datatracker.ietf.org/doc/html/rfc3339)
      [[RFC3339](https://datatracker.ietf.org/doc/html/rfc3339)] for details regarding date/times in general and UTC in
      particular.

Since time format for toDateTimeImmutable is hardcoded in Carbon to 'Y-m-d H:i:s.u', we get a non integer value for IAT.

    /**
     * Return native toDateTimeImmutable PHP object matching the current instance.
     *
     * @example
     * ```
     * var_dump(Carbon::now()->toDateTimeImmutable());
     * ```
     *
     * @return DateTimeImmutable
     */
    public function toDateTimeImmutable()
    {
        return new DateTimeImmutable($this->rawFormat('Y-m-d H:i:s.u'), $this->getTimezone());
    }

MR in #189 does resolve the problem due to a side effect that format format method seems to ignore the .u part when microseconds is set to 0.

d34dman avatar Sep 06 '24 09:09 d34dman