Skip to content

Commit

Permalink
Merge pull request #16 from moufmouf/autodiscovery
Browse files Browse the repository at this point in the history
Adding well-known configuration and JWKS routes in Laravel
  • Loading branch information
moufmouf authored Apr 25, 2024
2 parents 68e97f1 + 7ca1517 commit 9f56cce
Show file tree
Hide file tree
Showing 6 changed files with 139 additions and 7 deletions.
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,24 @@ In case you want to change the default scopes, add custom claim sets or change t
php artisan vendor:publish --tag=openid
```

### Discovery and JWKS

The Laravel Passport integration also provides:

- a discovery endpoint at `/.well-known/openid-configuration`.
- a JWKS endpoint at `/oauth/jwks`.

Those 2 endpoints are automatically added to the Laravel routes and can be disabled from the config (using
the `openid.routes.discovery` and `openid.routes.jwks` keys).

Laravel Passport does not provide a `userinfo` endpoint by default. If you provide one, you can add it to the
discovery document by naming the route `openid.userinfo`.

```php
Route::get('/oauth/userinfo', 'YourController@userinfo')->middleware('xxx')->name('openid.userinfo');
```


## Support
Found a bug? Got a feature request? [Create an issue](https://github.com/ronvanderheijden/openid-connect/issues).

Expand Down
46 changes: 46 additions & 0 deletions src/Laravel/DiscoveryController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php

namespace OpenIDConnect\Laravel;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;

class DiscoveryController
{
public function __invoke(Request $request)
{
$response = [
'issuer' => url('/'),
'authorization_endpoint' => route('passport.authorizations.authorize'),
'token_endpoint' => route('passport.token'),
'jwks_uri' => route('openid.jwks'),
'response_types_supported' => [
'code',
'token',
'id_token',
'code token',
'code id_token',
'token id_token',
'code token id_token',
'none',
],
'subject_types_supported' => [
'public',
],
'id_token_signing_alg_values_supported' => [
'RS256',
],
'scopes_supported' => config('openid.passport.tokens_can'),
'token_endpoint_auth_methods_supported' => [
'client_secret_basic',
'client_secret_post',
],
];

if (Route::has('openid.userinfo')) {
$response['userinfo_endpoint'] = route('openid.userinfo');
}

return response()->json($response, 200, [], JSON_PRETTY_PRINT);
}
}
38 changes: 38 additions & 0 deletions src/Laravel/JwksController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

namespace OpenIDConnect\Laravel;

use Illuminate\Config\Repository as Config;
use Laravel\Passport\Passport;

class JwksController
{
public function __invoke() {
$publicKey = $this->getPublicKey();

// Source: https://www.tuxed.net/fkooman/blog/json_web_key_set.html
$keyInfo = openssl_pkey_get_details(openssl_pkey_get_public($publicKey));

$jsonData = [
'keys' => [
[
'kty' => 'RSA',
'n' => rtrim(str_replace(['+', '/'], ['-', '_'], base64_encode($keyInfo['rsa']['n'])), '='),
'e' => rtrim(str_replace(['+', '/'], ['-', '_'], base64_encode($keyInfo['rsa']['e'])), '='),
],
],
];

return response()->json($jsonData, 200, [], JSON_PRETTY_PRINT);
}

private function getPublicKey(): string {
$publicKey = str_replace('\\n', "\n", config('passport.public_key', ''));

if (!$publicKey) {
$publicKey = 'file://'.Passport::keyPath('oauth-public.key');
}

return $publicKey;
}
}
24 changes: 17 additions & 7 deletions src/Laravel/PassportServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,25 +35,23 @@ public function boot()
__DIR__ . '/config/openid.php' => $this->app->configPath('openid.php'),
], ['openid', 'openid-config']);

$this->loadRoutesFrom(__DIR__.'/routes/web.php');

$tokens_can = config('openid.passport.tokens_can', null);
if ($tokens_can) {
Passport\Passport::tokensCan($tokens_can);
}

$this->registerClaimExtractor();
}

public function makeAuthorizationServer(): AuthorizationServer
{
$cryptKey = $this->makeCryptKey('private');

$customClaimSets = config('openid.custom_claim_sets');

$claimSets = array_map(function ($claimSet, $name) {
return new ClaimSet($name, $claimSet);
}, $customClaimSets, array_keys($customClaimSets));

$responseType = new IdTokenResponse(
app(config('openid.repositories.identity')),
new ClaimExtractor(...$claimSets),
app(ClaimExtractor::class),
Configuration::forSymmetricSigner(
app(config('openid.signer')),
InMemory::file($cryptKey->getKeyPath()),
Expand All @@ -70,4 +68,16 @@ public function makeAuthorizationServer(): AuthorizationServer
$responseType,
);
}

public function registerClaimExtractor() {
$this->app->singleton(ClaimExtractor::class, function () {
$customClaimSets = config('openid.custom_claim_sets');

$claimSets = array_map(function ($claimSet, $name) {
return new ClaimSet($name, $claimSet);
}, $customClaimSets, array_keys($customClaimSets));

return new ClaimExtractor(...$claimSets);
});
}
}
13 changes: 13 additions & 0 deletions src/Laravel/config/openid.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,17 @@
* Can be Ecdsa, Hmac or RSA
*/
'signer' => \Lcobucci\JWT\Signer\Hmac\Sha256::class,

'routes' => [
/**
* When set to true, this package will expose the OpenID Connect Discovery endpoint.
* - /.well-known/openid-configuration
*/
'discovery' => true,
/**
* When set to true, this package will expose the JSON Web Key Set endpoint.
* - /oauth/jwks
*/
'jwks' => true,
]
];
7 changes: 7 additions & 0 deletions src/Laravel/routes/web.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php
if (config('openid.routes.discovery', true)) {
Route::get('/oauth/jwks', \OpenIDConnect\Laravel\JwksController::class)->name('openid.jwks');
}
if (config('openid.routes.jwks', true)) {
Route::get('/.well-known/openid-configuration', \OpenIDConnect\Laravel\DiscoveryController::class)->name('openid.discovery');
}

0 comments on commit 9f56cce

Please sign in to comment.