Laravel-GitHub icon indicating copy to clipboard operation
Laravel-GitHub copied to clipboard

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

Open matthewnessworthy opened this issue 3 years ago • 14 comments

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

matthewnessworthy avatar Oct 19 '21 13:10 matthewnessworthy

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

GrahamCampbell avatar Nov 21 '21 22:11 GrahamCampbell

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!).

GrahamCampbell avatar Nov 21 '21 22:11 GrahamCampbell

@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)

matthewnessworthy avatar Nov 21 '21 22:11 matthewnessworthy

Is the exception coming from this line?

    ->createInstallationToken($installationId);        

GrahamCampbell avatar Nov 21 '21 23:11 GrahamCampbell

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('******, ******)

matthewnessworthy avatar Nov 22 '21 08:11 matthewnessworthy

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

GrahamCampbell avatar Nov 22 '21 11:11 GrahamCampbell

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

matthewnessworthy avatar Nov 22 '21 12:11 matthewnessworthy

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

matthewnessworthy avatar Apr 12 '22 07:04 matthewnessworthy

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.

agamm avatar Feb 12 '23 00:02 agamm

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 avatar Mar 16 '23 12:03 GrahamCampbell

How to reproduce

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

GrahamCampbell avatar Mar 16 '23 12:03 GrahamCampbell

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);
            });
        });

flavio-paqt avatar Mar 16 '23 14:03 flavio-paqt

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

matthewnessworthy avatar Apr 08 '23 12:04 matthewnessworthy

@matthewnessworthy you can draw a bit of inspiration from here https://github.com/octokit/auth-app.js/issues/146 (actual fix here https://github.com/octokit/auth-app.js/pull/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

ossycodes avatar Jan 09 '24 00:01 ossycodes