Skip to content

Commit 578ffa1

Browse files
Merge pull request #15 from stackkit/feature/use-google-own-openid-verification
Use Google SDK open id verification library
2 parents 890ca99 + 72b2791 commit 578ffa1

12 files changed

+89
-452
lines changed

.github/workflows/run-tests.yml

Lines changed: 15 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -10,57 +10,22 @@ on:
1010

1111
jobs:
1212
php-tests:
13-
runs-on: ${{ matrix.os }}
13+
runs-on: ubuntu-latest
1414

1515
strategy:
1616
matrix:
17-
php: [8.1, 8.0, 7.4, 7.3]
18-
laravel: [9.*, 8.*, 7.*, 6.*, 5.8.*, 5.7.*, 5.6.*]
19-
os: [ubuntu-latest]
20-
include:
21-
- laravel: 9.*
22-
testbench: 7.*
23-
- laravel: 8.*
24-
testbench: 6.*
25-
- laravel: 7.*
26-
testbench: 5.*
27-
- laravel: 6.*
28-
testbench: 4.*
29-
- laravel: 5.8.*
30-
testbench: 3.8.*
31-
- laravel: 5.7.*
32-
testbench: 3.7.*
33-
- laravel: 5.6.*
34-
testbench: 3.6.*
35-
exclude:
36-
- laravel: 9.*
37-
php: 7.3
38-
- laravel: 9.*
39-
php: 7.4
40-
- laravel: 5.7.*
41-
php: 7.4
42-
- laravel: 5.6.*
43-
php: 7.4
44-
- laravel: 5.5.*
45-
php: 7.4
46-
- laravel: 5.8.*
47-
php: 8.0
48-
- laravel: 5.7.*
49-
php: 8.0
50-
- laravel: 5.6.*
51-
php: 8.0
52-
- laravel: 5.6.*
53-
php: 8.1
54-
- laravel: 5.7.*
55-
php: 8.1
56-
- laravel: 5.8.*
57-
php: 8.1
58-
- laravel: 6.*
59-
php: 8.1
60-
- laravel: 7.*
61-
php: 8.1
17+
payload:
18+
- { queue: 'github-actions-laravel9-php81', laravel: '9.*', php: '8.1', 'testbench': '7.*'}
19+
- { queue: 'github-actions-laravel9-php80', laravel: '9.*', php: '8.0', 'testbench': '7.*'}
20+
- { queue: 'github-actions-laravel8-php81', laravel: '8.*', php: '8.1', 'testbench': '6.*'}
21+
- { queue: 'github-actions-laravel8-php80', laravel: '8.*', php: '8.0', 'testbench': '6.*'}
22+
- { queue: 'github-actions-laravel8-php74', laravel: '8.*', php: '7.4', 'testbench': '6.*'}
23+
- { queue: 'github-actions-laravel7-php80', laravel: '7.*', php: '8.0', 'testbench': '5.*' }
24+
- { queue: 'github-actions-laravel7-php74', laravel: '7.*', php: '7.4', 'testbench': '5.*' }
25+
- { queue: 'github-actions-laravel6-php80', laravel: '6.*', php: '8.0', 'testbench': '4.*' }
26+
- { queue: 'github-actions-laravel6-php74', laravel: '6.*', php: '7.4', 'testbench': '4.*' }
6227

63-
name: PHP ${{ matrix.php }} - Laravel ${{ matrix.laravel }}
28+
name: PHP ${{ matrix.payload.php }} - Laravel ${{ matrix.payload.laravel }}
6429

6530
steps:
6631
- name: Checkout code
@@ -69,13 +34,13 @@ jobs:
6934
- name: Setup PHP
7035
uses: shivammathur/setup-php@v2
7136
with:
72-
php-version: ${{ matrix.php }}
37+
php-version: ${{ matrix.payload.php }}
7338
extensions: mbstring, dom, fileinfo
7439
coverage: none
7540

7641
- name: Install dependencies
7742
run: |
78-
composer require "laravel/framework:${{ matrix.laravel }}" "orchestra/testbench:${{ matrix.testbench }}" --no-interaction --no-update
79-
composer update --prefer-stable --prefer-dist --no-interaction --ignore-platform-reqs
43+
composer require "laravel/framework:${{ matrix.payload.laravel }}" "orchestra/testbench:${{ matrix.payload.testbench }}" --no-interaction --no-update
44+
composer update --prefer-stable --prefer-dist --no-interaction
8045
- name: Execute tests
8146
run: vendor/bin/phpunit

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,13 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
55
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
66

7+
# 2.0.0 - 2022-04-23
8+
9+
**Changed**
10+
11+
- Dropped older PHP and Laravel support
12+
- Bumped dependencies
13+
714
## 1.1.0 - 2022-02-09
815

916
**Changed**

README.md

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,15 @@ All these features are supported. This package scans your console kernel (`app/C
2323

2424
# Requirements
2525

26-
This package requires Laravel 5.6 or higher.
26+
This package requires Laravel 6 or higher.
2727

2828
Please check the table below for supported Laravel and PHP versions:
2929

3030
|Laravel Version| PHP Version |
3131
|---|---|
32-
| 5.6 | 7.3
33-
| 5.7 | 7.3
34-
| 5.8 | 7.3 or 7.4
35-
| 6.x | 7.3 or 7.4 or 8.0
36-
| 7.x | 7.3 or 7.4 or 8.0
37-
| 8.x | 7.3 or 7.4 or 8.0
32+
| 6.x | 7.4 or 8.0
33+
| 7.x | 7.4 or 8.0
34+
| 8.x | 7.4 or 8.0
3835
| 9.x | 8.0 or 8.1
3936

4037
# Installation

composer.json

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,12 @@
99
],
1010
"require": {
1111
"ext-json": "*",
12-
"google/cloud-scheduler": "^1.5",
13-
"firebase/php-jwt": "^5.5",
12+
"google/cloud-scheduler": "^1.6",
1413
"phpseclib/phpseclib": "~2.0"
1514
},
1615
"require-dev": {
17-
"mockery/mockery": "^1.3",
18-
"orchestra/testbench": "^3.5 || ^3.6 || ^3.7 || ^3.8 || ^4.0 || ^5.0",
19-
"psr/log": "^1.1",
20-
"spatie/macroable": "^1.0"
16+
"mockery/mockery": "^1.5",
17+
"orchestra/testbench": "^4.0 || ^5.0 || ^6.0 || ^7.0"
2118
},
2219
"autoload": {
2320
"psr-4": {

src/CloudSchedulerServiceProvider.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ class CloudSchedulerServiceProvider extends LaravelServiceProvider
1010
public function boot(Router $router)
1111
{
1212
$this->registerRoutes($router);
13+
$this->registerClient();
1314
}
1415

1516
public function register()
@@ -21,4 +22,9 @@ private function registerRoutes(Router $router)
2122
{
2223
$router->post('cloud-scheduler-job', [TaskHandler::class, 'handle']);
2324
}
25+
26+
private function registerClient()
27+
{
28+
$this->app->bind('open-id-verificator', OpenIdVerificatorConcrete::class);
29+
}
2430
}

src/OpenIdVerificator.php

Lines changed: 6 additions & 137 deletions
Original file line numberDiff line numberDiff line change
@@ -2,148 +2,17 @@
22

33
namespace Stackkit\LaravelGoogleCloudScheduler;
44

5-
use Carbon\Carbon;
6-
use Firebase\JWT\JWT;
7-
use Firebase\JWT\SignatureInvalidException;
8-
use GuzzleHttp\Client;
9-
use GuzzleHttp\Exception\ServerException;
10-
use Illuminate\Support\Arr;
11-
use Illuminate\Support\Facades\Cache;
12-
use phpseclib\Crypt\RSA;
13-
use phpseclib\Math\BigInteger;
14-
use Throwable;
5+
use Illuminate\Support\Facades\Facade;
156

16-
class OpenIdVerificator
7+
class OpenIdVerificator extends Facade
178
{
18-
private const V3_CERTS = 'GOOGLE_V3_CERTS';
19-
private const URL_OPENID_CONFIG = 'https://accounts.google.com/.well-known/openid-configuration';
20-
private const URL_TOKEN_INFO = 'https://www.googleapis.com/oauth2/v3/tokeninfo';
21-
22-
private $guzzle;
23-
private $rsa;
24-
private $jwt;
25-
private $maxAge = [];
26-
27-
public function __construct(Client $guzzle, RSA $rsa, JWT $jwt)
28-
{
29-
$this->guzzle = $guzzle;
30-
$this->rsa = $rsa;
31-
$this->jwt = $jwt;
32-
}
33-
34-
public function guardAgainstInvalidOpenIdToken($decodedToken)
35-
{
36-
/**
37-
* https://developers.google.com/identity/protocols/oauth2/openid-connect#validatinganidtoken
38-
*/
39-
if (!in_array($decodedToken->iss, ['https://accounts.google.com', 'accounts.google.com'])) {
40-
throw new CloudSchedulerException('The given OpenID token is not valid');
41-
}
42-
43-
if ($decodedToken->exp < time()) {
44-
throw new CloudSchedulerException('The given OpenID token has expired');
45-
}
46-
47-
if ($decodedToken->aud !== config('laravel-google-cloud-scheduler.app_url')) {
48-
throw new CloudSchedulerException('The given OpenID token is not valid');
49-
}
50-
}
51-
52-
public function decodeOpenIdToken($openIdToken, $kid, $cache = true)
53-
{
54-
if (!$cache) {
55-
$this->forgetFromCache();
56-
}
57-
58-
$publicKey = $this->getPublicKey($kid);
59-
60-
try {
61-
return $this->jwt->decode($openIdToken, $publicKey, ['RS256']);
62-
} catch (SignatureInvalidException $e) {
63-
if (!$cache) {
64-
throw $e;
65-
}
66-
67-
return $this->decodeOpenIdToken($openIdToken, $kid, false);
68-
}
69-
}
70-
71-
public function getPublicKey($kid = null)
72-
{
73-
if (Cache::has(self::V3_CERTS)) {
74-
$v3Certs = Cache::get(self::V3_CERTS);
75-
} else {
76-
$v3Certs = $this->getFreshCertificates();
77-
Cache::put(self::V3_CERTS, $v3Certs, Carbon::now()->addSeconds($this->maxAge[self::URL_OPENID_CONFIG]));
78-
}
79-
80-
$cert = $kid ? collect($v3Certs)->firstWhere('kid', '=', $kid) : $v3Certs[0];
81-
82-
return $this->extractPublicKeyFromCertificate($cert);
83-
}
84-
85-
private function getFreshCertificates()
86-
{
87-
$jwksUri = $this->callApiAndReturnValue(self::URL_OPENID_CONFIG, 'jwks_uri');
88-
89-
return $this->callApiAndReturnValue($jwksUri, 'keys');
90-
}
91-
92-
private function extractPublicKeyFromCertificate($certificate)
93-
{
94-
$modulus = new BigInteger(JWT::urlsafeB64Decode($certificate['n']), 256);
95-
$exponent = new BigInteger(JWT::urlsafeB64Decode($certificate['e']), 256);
96-
97-
$this->rsa->loadKey(compact('modulus', 'exponent'));
98-
99-
return $this->rsa->getPublicKey();
100-
}
101-
102-
public function getKidFromOpenIdToken($openIdToken)
103-
{
104-
return $this->callApiAndReturnValue(self::URL_TOKEN_INFO . '?id_token=' . $openIdToken, 'kid');
105-
}
106-
107-
private function callApiAndReturnValue($url, $value)
108-
{
109-
$attempts = 0;
110-
111-
while (true) {
112-
try {
113-
$response = $this->guzzle->get($url);
114-
115-
break;
116-
} catch (ServerException $e) {
117-
$attempts++;
118-
119-
if ($attempts >= 3) {
120-
throw $e;
121-
}
122-
123-
sleep(1);
124-
}
125-
}
126-
127-
$data = json_decode($response->getBody(), true);
128-
129-
$maxAge = 0;
130-
foreach ($response->getHeader('Cache-Control') as $line) {
131-
preg_match('/max-age=(\d+)/', $line, $matches);
132-
$maxAge = isset($matches[1]) ? (int) $matches[1] : 0;
133-
}
134-
135-
$this->maxAge[$url] = $maxAge;
136-
137-
return Arr::get($data, $value);
138-
}
139-
140-
public function isCached()
9+
protected static function getFacadeAccessor()
14110
{
142-
return Cache::has(self::V3_CERTS);
11+
return 'open-id-verificator';
14312
}
14413

145-
public function forgetFromCache()
14+
public static function fake(): void
14615
{
147-
Cache::forget(self::V3_CERTS);
16+
self::swap(new OpenIdVerificatorFake());
14817
}
14918
}

src/OpenIdVerificatorConcrete.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
namespace Stackkit\LaravelGoogleCloudScheduler;
4+
5+
use Google\Auth\AccessToken;
6+
use Illuminate\Support\Facades\Facade;
7+
8+
class OpenIdVerificatorConcrete extends Facade
9+
{
10+
public function verify(?string $token, array $config): void
11+
{
12+
if (!$token) {
13+
throw new CloudSchedulerException('Missing [Authorization] header');
14+
}
15+
16+
(new AccessToken())->verify(
17+
$token,
18+
[
19+
'audience' => config('laravel-google-cloud-scheduler.app_url'),
20+
'throwException' => true,
21+
]
22+
);
23+
}
24+
}

src/OpenIdVerificatorFake.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
namespace Stackkit\LaravelGoogleCloudScheduler;
4+
5+
class OpenIdVerificatorFake
6+
{
7+
public function verify(?string $token, array $config): void
8+
{
9+
//
10+
}
11+
}

0 commit comments

Comments
 (0)