Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

'Expiration time' claim ('exp') must be a numeric value representing the future time at which the assertion expires #135

Open
matthewnessworthy opened this issue Oct 19, 2021 · 14 comments

Comments

@matthewnessworthy
Copy link

matthewnessworthy commented Oct 19, 2021

PHP version: 8.0.11

Description
I (occasionally) get the following error when attempting to authenticate as an app

'Expiration time' claim ('exp') must be a numeric value representing the future time at which the assertion expires

How to reproduce

$github = resolve(GitHubManager::class);

$installationId = $installationId ?: config('github.installations.' . $org);

$token = $github
    ->connection($org)
    ->apps()
    ->createInstallationToken($installationId);        

$githubConnection = $github
    ->getFactory()
    ->make(
        [
            'token' => $token['token'],
            'method' => 'token',
            'cache' => 'main',
            'backoff' => true,
        ]
    );

return $githubConnection;

Additional context
I am acting as the app installation, but perhaps I am going about it in the wrong way and there is a way to authenticate as an app installation already built in

@GrahamCampbell
Copy link
Owner

Are you using the latest version of this package. I'm sure I already fixed this in 10.0.2?

@GrahamCampbell
Copy link
Owner

The only other thing I can think of that would be causing this is not running artisan config:cache in production and loading environment variables using getenv and putenv (the laravel default). Those builtin PHP functions are not thread-safe and are not suitable for use in php-fpm (despite the fact that almost everyone does this becuse of the bad naming of the functions and superglobals. the $_SERVER superglobal is actually the place you should go to read and write env variables!).

@matthewnessworthy
Copy link
Author

@GrahamCampbell
I'm currently using a very recent version (10.3.0)
This error is occurring on a Vapor installation (also latest version) which has config caching built-in (as far as i recall)

@GrahamCampbell
Copy link
Owner

Is the exception coming from this line?

    ->createInstallationToken($installationId);        

@matthewnessworthy
Copy link
Author

Yes, it is
I found a stacktrace pointing tho that function call

#0 /var/task/vendor/php-http/httplug/src/Promise/HttpFulfilledPromise.php(31): Github\HttpClient\Plugin\GithubExceptionThrower->Github\HttpClient\Plugin\{closure}(Object(GuzzleHttp\Psr7\Response))
#1 /var/task/vendor/knplabs/github-api/lib/Github/HttpClient/Plugin/GithubExceptionThrower.php(124): Http\Client\Promise\HttpFulfilledPromise->then(Object(Closure))
#2 /var/task/vendor/php-http/client-common/src/PluginChain.php(48): Github\HttpClient\Plugin\GithubExceptionThrower->handleRequest(Object(Nyholm\Psr7\Request), Object(Closure), Object(Http\Client\Common\PluginChain))
#3 /var/task/vendor/php-http/client-common/src/Plugin/RetryPlugin.php(106): Http\Client\Common\PluginChain->Http\Client\Common\{closure}(Object(Nyholm\Psr7\Request))
#4 /var/task/vendor/php-http/client-common/src/Plugin/RetryPlugin.php(173): Http\Client\Common\Plugin\RetryPlugin->handleRequest(Object(Nyholm\Psr7\Request), Object(Closure), Object(Http\Client\Common\PluginChain))
#5 /var/task/vendor/php-http/client-common/src/Plugin/RetryPlugin.php(144): Http\Client\Common\Plugin\RetryPlugin->retry(Object(Nyholm\Psr7\Request), Object(Closure), Object(Http\Client\Common\PluginChain), '000000005952042...', 1000000)
#6 /var/task/vendor/php-http/httplug/src/Promise/HttpRejectedPromise.php(30): Http\Client\Common\Plugin\RetryPlugin->Http\Client\Common\Plugin\{closure}(Object(Github\Exception\RuntimeException))
#7 /var/task/vendor/php-http/client-common/src/Plugin/RetryPlugin.php(145): Http\Client\Promise\HttpRejectedPromise->then(Object(Closure), Object(Closure))
#8 /var/task/vendor/php-http/client-common/src/Plugin/RetryPlugin.php(173): Http\Client\Common\Plugin\RetryPlugin->handleRequest(Object(Nyholm\Psr7\Request), Object(Closure), Object(Http\Client\Common\PluginChain))
#9 /var/task/vendor/php-http/client-common/src/Plugin/RetryPlugin.php(144): Http\Client\Common\Plugin\RetryPlugin->retry(Object(Nyholm\Psr7\Request), Object(Closure), Object(Http\Client\Common\PluginChain), '000000005952042...', 500000)
#10 /var/task/vendor/php-http/httplug/src/Promise/HttpRejectedPromise.php(30): Http\Client\Common\Plugin\RetryPlugin->Http\Client\Common\Plugin\{closure}(Object(Github\Exception\RuntimeException))
#11 /var/task/vendor/php-http/client-common/src/Plugin/RetryPlugin.php(145): Http\Client\Promise\HttpRejectedPromise->then(Object(Closure), Object(Closure))
#12 /var/task/vendor/php-http/client-common/src/PluginChain.php(48): Http\Client\Common\Plugin\RetryPlugin->handleRequest(Object(Nyholm\Psr7\Request), Object(Closure), Object(Http\Client\Common\PluginChain))
#13 /var/task/vendor/php-http/client-common/src/PluginChain.php(63): Http\Client\Common\PluginChain->Http\Client\Common\{closure}(Object(Nyholm\Psr7\Request))
#14 /var/task/vendor/php-http/client-common/src/PluginClient.php(90): Http\Client\Common\PluginChain->__invoke(Object(Nyholm\Psr7\Request))
#15 /var/task/vendor/php-http/client-common/src/HttpMethodsClient.php(148): Http\Client\Common\PluginClient->sendRequest(Object(Nyholm\Psr7\Request))
#16 /var/task/vendor/php-http/client-common/src/HttpMethodsClient.php(108): Http\Client\Common\HttpMethodsClient->sendRequest(Object(Nyholm\Psr7\Request))
#17 /var/task/vendor/php-http/client-common/src/HttpMethodsClient.php(70): Http\Client\Common\HttpMethodsClient->send('POST', '/app/installati...', Array, NULL)
#18 /var/task/vendor/knplabs/github-api/lib/Github/Api/AbstractApi.php(145): Http\Client\Common\HttpMethodsClient->post('/app/installati...', Array, NULL)
#19 /var/task/vendor/knplabs/github-api/lib/Github/Api/AcceptHeaderTrait.php(34): Github\Api\AbstractApi->postRaw('/app/installati...', NULL, Array)
#20 /var/task/vendor/knplabs/github-api/lib/Github/Api/AbstractApi.php(127): Github\Api\Apps->postRaw('/app/installati...', NULL, Array)
#21 /var/task/vendor/knplabs/github-api/lib/Github/Api/AcceptHeaderTrait.php(29): Github\Api\AbstractApi->post('/app/installati...', Array, Array)
#22 /var/task/vendor/knplabs/github-api/lib/Github/Api/Apps.php(39): Github\Api\Apps->post('/app/installati...', Array)
#23 /var/task/app/Services/GithubService.php(29): Github\Api\Apps->createInstallationToken(******)
#24 /var/task/app/Jobs/ChannelReviewRequested.php(165): App\Services\GithubService->getConnection('******, ******)

@GrahamCampbell
Copy link
Owner

Right, so that would indicate that this is not a bug in this package, but instead, an issue in knplabs/github-api?

@matthewnessworthy
Copy link
Author

valid point, sorry about that, i'll follow up on knplabs/github-api

@matthewnessworthy
Copy link
Author

Hi @GrahamCampbell,
I'm still experiencing this issue and I've realised that knplabs/github-api is not the issue here.
The JWT token that is used for PK auth that is in turn used when calling createInstallationToken() is created via this package.
knplabs/github-api is reporting the error back from GitHub, but the source of the token is here.
I don't know if this is an issue that only/mostly occurs on Vapor (i.e., lambdas maybe being reused?), or if i am just lucky like this.
Any suggestions are welcome

@agamm
Copy link

agamm commented Feb 12, 2023

Hi @GrahamCampbell, I'm still experiencing this issue and I've realised that knplabs/github-api is not the issue here. The JWT token that is used for PK auth that is in turn used when calling createInstallationToken() is created via this package. knplabs/github-api is reporting the error back from GitHub, but the source of the token is here. I don't know if this is an issue that only/mostly occurs on Vapor (i.e., lambdas maybe being reused?), or if i am just lucky like this. Any suggestions are welcome

This also happens to me on nextjs api routes.

@GrahamCampbell
Copy link
Owner

Ah, interesting. You are both using Lambda, I think, based on the path in the stack. I think what is going wrong is that you are re-using the same connection after the JWT has expired. I wonder if we can re-work things so that the JWT is lazy (re-)generated, rather than being generated at connection instantiation, and then held for the lifetime of the Lambda. Probably that kind of refactor would need a new major release.

@GrahamCampbell
Copy link
Owner

How to reproduce

Are you actually making a new connection each time, or are you doing it once, and then re-using it?

@flavio-paqt
Copy link

I already fixed it, I swapped out the token:

$this->app->afterResolving(GitHubManager::class, function (GitHubManager $manager) {
            $manager->extend(strval(config('github.default')), function (array $config) {
               
               // Swap code

                return $connectionFactory->make($config);
            });
        });

@matthewnessworthy
Copy link
Author

@flavio-paqt i don't know how that helps or changes things
could you maybe show it using my original code example?

@ossycodes
Copy link

ossycodes commented Jan 9, 2024

@matthewnessworthy you can draw a bit of inspiration from here octokit/auth-app.js#146 (actual fix here octokit/auth-app.js#164), a quick dirty fix can be to try to catch the RuntimeException if it's related to an expired Expiration time' claim ('exp') then manually regenerate the token (see inside vendor/graham-campbell/github/src/Auth/Authenticator/PrivateKeyAuthenticator.php) and finally resend the request using this newly generated token

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants