singleloginsystem/tests/Feature/OidcNonceTest.php
= 02455fc71c Feat: Implement OpenID Connect nonce handling
- Added `CustomAuthCodeRepository` to cache and store nonces during authorization.
- Introduced `CustomTokenResponseType` to resolve cached nonces during token exchange.
- Created `oidc-server.php` configuration for OpenID Connect support, including scopes, claims, and token lifetimes.
- Registered custom implementations in `AppServiceProvider` with bindings for `AuthCodeRepository` and `TokenResponseType`.
- Added feature tests to validate nonce caching and resolution logic.
2026-06-17 07:22:26 +00:00

66 lines
2.7 KiB
PHP

<?php
declare(strict_types=1);
use App\OAuth\{CustomAuthCodeRepository, CustomTokenResponseType};
use Defuse\Crypto\Crypto;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Laravel\Passport\Passport;
use League\OAuth2\Server\Entities\{AuthCodeEntityInterface, ClientEntityInterface};
uses(RefreshDatabase::class);
it('caches the nonce from the request during authorization and resolves it during token request', function (): void {
// --- Phase 1: Authorization ---
// 1. Create a request to authorize endpoint with a nonce
$authorizeRequest = Request::create('/oauth/authorize', 'GET', ['nonce' => 'secure-nonce-value-xyz']);
app()->instance('request', $authorizeRequest);
// 2. Create the mock AuthCodeEntity
$client = Mockery::mock(ClientEntityInterface::class);
$client->shouldReceive('getIdentifier')->andReturn('client-123');
$authCodeEntity = Mockery::mock(AuthCodeEntityInterface::class);
$authCodeEntity->shouldReceive('getIdentifier')->andReturn('code-id-abc');
$authCodeEntity->shouldReceive('getUserIdentifier')->andReturn('user-1');
$authCodeEntity->shouldReceive('getClient')->andReturn($client);
$authCodeEntity->shouldReceive('getScopes')->andReturn([]);
$authCodeEntity->shouldReceive('getExpiryDateTime')->andReturn(new DateTimeImmutable('+10 minutes'));
// 4. Run the repository persist method
$repository = new CustomAuthCodeRepository();
$repository->persistNewAuthCode($authCodeEntity);
// 5. Assert the nonce is cached
expect(Cache::get('oidc_nonce_code-id-abc'))->toBe('secure-nonce-value-xyz');
// --- Phase 2: Token Exchange ---
// 1. Prepare mock encrypted auth code payload
$key = Passport::tokenEncryptionKey(app('encrypter'));
$payload = json_encode([
'client_id' => 'client-123',
'auth_code_id' => 'code-id-abc',
'user_id' => 'user-1',
'scopes' => [],
'expire_time' => time() + 600,
]);
$encryptedAuthCode = Crypto::encryptWithPassword($payload, $key);
// 2. Set up the request to token endpoint containing the encrypted code
$tokenRequest = Request::create('/oauth/token', 'POST', ['code' => $encryptedAuthCode]);
app()->instance('request', $tokenRequest);
// 3. Resolve CustomTokenResponseType and call the protected resolveNonce method using reflection
$responseType = app(CustomTokenResponseType::class);
$reflection = new ReflectionClass(CustomTokenResponseType::class);
$method = $reflection->getMethod('resolveNonce');
$method->setAccessible(true);
$resolvedNonce = $method->invoke($responseType);
// 4. Assert the resolved nonce is correct
expect($resolvedNonce)->toBe('secure-nonce-value-xyz');
});