kabiroman/octawire-auth-service-php-client-bundle
A Symfony bundle that integrates the OctaWire Auth Service PHP client, offering a convenient way to wire the client into Symfony applications through bundle configuration and service definitions. Best suited to projects already using the OctaWire authentication ecosystem.
This is a protocol client plus Symfony security glue for an Octowire Auth Service — not a Laravel-native auth driver. Day-to-day you'll use it as the token-validation layer behind a Laravel middleware or custom guard, so budget an hour to wire it, not a composer require and done.
composer require kabiroman/octawire-auth-service-php-client:^0.9.4
Requirements to confirm before you start: PHP 8.1+, Symfony 6.4+/7.0+ components (they'll come in as transitive deps — check they don't collide with an existing symfony/* pin in your composer.json).
First places to look, in this order:
README.md — the v0.9.4 migration guide is the fastest way to see the camelCase/snake_case field mapping in one table.INSTALLATION.md — the "v0.9.4 changes" section covers the config keys you must set.examples/integration-tests/ — new in this release and the most useful artifact. It shows a working end-to-end flow against a real service, including the TLS and non-TLS example configs — copy one of those configs verbatim before hand-tuning.Minimum config you need in config/services.php (or a dedicated config/octowire.php):
'octowire' => [
'base_url' => env('OCTOWIRE_AUTH_URL'),
'project_id' => env('OCTOWIRE_PROJECT_ID'), // required as of v0.9.4
'tls' => [
'cert' => env('OCTOWIRE_TLS_CERT'),
'key' => env('OCTOWIRE_TLS_KEY'),
'ca' => env('OCTOWIRE_TLS_CA'),
'verify' => env('OCTOWIRE_TLS_VERIFY', true),
],
],
project_id is no longer optional in practice — see Gotchas.
First use case: validating an inbound bearer token on an API route and resolving the caller's identity. Build the validator in a service provider, call it from middleware, stash the resulting OctowireUser on the request:
$user = $validator->validate($request->bearerToken());
$request->attributes->set('octowire_user', $user);
That's the 20-minute version. The rest of this assessment is what bites you after that works locally.
Token validation middleware. The canonical Laravel integration — the package's OctowireTokenAuthenticator is a Symfony Security authenticator and won't plug into Laravel's guard system directly. Wrap TokenValidator yourself:
final class ValidateOctowireToken
{
public function __construct(private TokenValidator $validator) {}
public function handle(Request $request, Closure $next)
{
try {
$user = $this->validator->validate($request->bearerToken());
} catch (InvalidTokenException $e) {
return response()->json(['error' => 'unauthorized'], 401);
}
$request->attributes->set('octowire_user', $user);
return $next($request);
}
}
Register it as a named middleware and apply to your API group. Keep the exception handling narrow — a network failure against the auth service is a 503, not a 401, and collapsing them will make incidents miserable.
Custom guard for Auth::user(). If you want the familiar Laravel ergonomics, add a guard in AppServiceProvider:
Auth::viaRequest('octowire', function (Request $request) {
return $request->attributes->get('octowire_user');
});
Order matters: the middleware must run before anything that touches Auth::user().
Consuming customClaims for authorization. v0.9.4 exposes customClaims on the authenticator path. In practice you read them off the OctowireUser and feed them into policies or gates:
Gate::define('view-invoices', fn (OctowireUser $u) =>
in_array('invoices:read', $u->getCustomClaims()['scopes'] ?? [], true)
);
Don't hardcode a claim shape — treat customClaims as an opaque map your auth service owns and validate the specific keys you need.
Reading identity fields defensively. OctowireUser::userId falls back to user_id, and TokenValidator does the same for projectId. That's deliberate dual-protocol support. Write your code against userId only; the fallback is the package's job, not yours. If you find yourself writing $user->userId ?? $user->user_id, you're fighting the release.
Issuing tokens for service-to-service calls. IssueTokenResponse now carries expiresIn. Use it to size your cache TTL rather than guessing:
$response = $issuer->issue(new IssueTokenRequest(...));
Cache::put("octowire.s2s.{$audience}", $response->getAccessToken(), $response->getExpiresIn() - 30);
The 30-second haircut avoids handing out a token that expires mid-flight.
Integration testing. The new examples/integration-tests/ gives you a template for testing against a real service. Two-tier strategy: unit tests with a faked HTTP client for your middleware and claim logic, and a separate, opt-in integration suite (tagged @group integration, skipped when OCTOWIRE_AUTH_URL is absent) for the actual protocol contract. The second tier is what catches a protocol drift like the one this release shipped.
Health checks. If you're exposing /health, HealthCheckResponse now returns a status string instead of a healthy boolean. Map it explicitly rather than casting to bool — you want to log the actual status value when it isn't the healthy one.
projectId is now mandatory and passed to ValidateTokenRequest. This release fixed Argument #2 ($projectId) not passed, which means prior versions were silently tolerant of a missing project ID. If your config doesn't set it, you'll now get a hard failure at validation time rather than a confusing 401. Check every environment — .env.example, staging, CI secrets — before deploying.
HealthCheckResponse::healthy → status is a breaking change. Any monitoring, uptime probe, or deploy gate reading a boolean will now see a string. In PHP, (bool) 'unhealthy' is true, so a naive cast flips your health check to permanently green. Grep your codebase and your infra configs for healthy before rolling out.
IssueTokenResponse constructor changed (expiresIn added to the signature). If you construct it in tests or fixtures rather than only consuming it, those break silently at runtime. Production code that only reads the getter is fine.
Symfony Security vs. Laravel guards. OctowireTokenAuthenticator implements Symfony's AuthenticatorInterface. Don't try to register it in a Laravel service provider and expect it to fire — it won't, and there's no error to tell you so. Either use it only in genuine Symfony contexts or ignore it entirely and drive TokenValidator yourself, as in the middleware example.
Dual camelCase/snake_case reads create a blind spot. The package tolerates both, which is great for rollout and terrible for debugging. When something looks wrong, log the raw decoded payload from the auth service, not just the mapped object — you need to know which casing actually arrived to tell a protocol problem from a mapping problem.
TLS config differs between environments. The example configs cover both TLS and non-TLS, and it's tempting to run non-TLS locally. Keep the config shape identical across environments and toggle via env var, so a cert path typo fails in dev rather than in prod. Set verify => true in staging at minimum — mTLS with verification off hides exactly the misconfiguration it's meant to catch.
Don't validate the same token twice per request. Validation is a network round-trip to the auth service. Resolution belongs in middleware, once, with the result on the request. If you find TokenValidator injected into half a dozen services, that's the smell.
Version-match the client. The notes pin kabiroman/octawire-auth-service-php-client: ^0.9.4, and the camelCase support lands on both sides of the wire. A ^0.9.3 install against a protocol-v1.0 auth service is a mixed-casing minefield. Lock the constraint explicitly.
Where to extend. TokenValidator and the user class are the sane seams — wrap rather than subclass, since the package is pre-1.0 and internal method signatures have already moved once. Any behavior you patch by extending will be your maintenance burden at 1.0.
Pre-1.0 expectations. At 0.9.x, a minor bump carrying a response-shape change and a constructor signature change is within normal bounds. Pin exact-ish constraints (~0.9.4), read the migration guide on every bump, and keep the integration suite running on a schedule so protocol drift surfaces before a customer does.
How can I help you explore Laravel packages today?