Skip to content

Testing

The package ships test helpers in Bambamboole\LaravelOidc\Server\Testing. Add the trait to your suite:

// Pest (tests/Pest.php)
uses(Bambamboole\LaravelOidc\Server\Testing\InteractsWithOidc::class)->in('Feature');
// PHPUnit
abstract class TestCase extends BaseTestCase
{
use \Bambamboole\LaravelOidc\Server\Testing\InteractsWithOidc;
}

actingAsIdentity() logs the user in on the identity guard and seeds the session keys the authorization grant reads (oidc.auth_time, oidc.amr, oidc.id_token_claims, oidc.access_token_claims):

$this->actingAsIdentity($user, amr: ['pwd', 'otp'], authTime: time() - 60);

There is no acr parameter: the grant derives acr from amr through the bound AcrResolver (oidc.login.acr_single_factor / oidc.login.acr_multi_factor: 1 for a single method, 2 for multiple by default).

actingAsOidcUser() authenticates a user on the token guard with an unpersisted token that grants the listed scopes — for routes behind auth:oidc that only inspect scopes:

$this->actingAsOidcUser($user, ['openid', 'email']);

actingAsOidcClient() is the counterpart for a client_credentials caller: it authenticates a ClientPrincipal on the token guard with an unpersisted token granting the listed scopes.

$client = $this->createOidcMachineClient(); // a confidential client_credentials client
$this->actingAsOidcClient($client, ['orders.read']);

issueTokenFor() returns a signed at+jwt access token with a persisted persisted token row — ready for a Bearer header:

$jwt = $this->issueTokenFor($user, scopes: ['openid', 'email'], audience: ['https://api.orders.test']);
$this->withHeader('Authorization', 'Bearer '.$jwt)->get('/api/orders');

When no client is given, a default authorization-code client is created once per test and reused.

issueClientToken() mints the userless equivalent — the token client_credentials issues, whose principal is the client itself:

$jwt = $this->issueClientToken($this->createOidcMachineClient(), ['orders.read']);

A token minted with a custom audience: does not authenticate on plain auth:oidc routes unless that audience is the realm issuer or a resource registered under oidc.resources — the guard accepts a token only when its aud names one of them. Without audience: the token is addressed to the issuer and passes plain auth:oidc routes, but not a route behind CheckAudience for another resource. See Resource servers (CheckAudience).

$client = $this->createOidcClient(); // auth-code grant client
$client = $this->createOidcMachineClient(); // client_credentials grant client
$client = $this->withFirstPartyClient(); // + sets oidc.clients.first_party.* config

Config mutated in a test takes effect immediately — the package reads oidc.clients.first_party.* at call time, so no forgetInstance() ceremony is needed after config([...]) changes.

withFirstPartyClient() sets oidc.clients.first_party.trusted = true, so consent is skipped for that client; register a client via createOidcClient() instead when a test asserts consent behavior.

Sessions, session participants, consents, access and refresh tokens, authorization codes and authentication contexts have model factories, for a test that needs the row without the flow that writes it:

use Bambamboole\LaravelOidc\Server\Sessions\Models\OidcSession;
use Bambamboole\LaravelOidc\Server\Tokens\Models\AccessToken;
use Bambamboole\LaravelOidc\Server\Tokens\Models\RefreshToken;
$session = OidcSession::factory()->forUser($user)->create();
$token = AccessToken::factory()->forClient($client)->forUser($user)->create();
RefreshToken::factory()->forAccessToken($token)->create();

These rows name their user and client without a foreign key, so use forUser() and forClient() rather than for(). forClient() also puts the row in the client’s realm; otherwise a row lands in the current one.

authorizeAndApprove() drives authorize → approve → token with PKCE:

$result = $this->authorizeAndApprove($user, $client, scopes: 'openid email');
$result->accessToken;
$result->idToken;
$result->refreshToken;

The authorize and approve legs assert success; the token response is returned unasserted, so error paths stay testable:

$result = $this->authorizeAndApprove($user, $misconfiguredClient);
$result->response->assertStatus(401);
$result->response->json('error'); // invalid_client

params: overrides any authorize query parameter (state, nonce, max_age, redirect_uri, …), and pkce: accepts a fixed PkcePair when the test needs the verifier later. The helper binds a minimal JSON ConsentView unless the test (or FakesAuthViews, or the ui package) already bound one.

The CSRF exemption applied by authorizeAndApprove() persists for the remainder of the calling test method.