authn-sh / sdk-php
PHP backend SDK for authn.sh — BAPI client, JWT verification, and webhook signature verification.
Requires
- php: ^8.2
- ext-json: *
- php-http/discovery: ^1.19
- psr/http-client: ^1.0
- psr/http-factory: ^1.0
- psr/http-message: ^1.1 || ^2.0
- psr/log: ^3.0
- psr/simple-cache: ^3.0
- web-token/jwt-library: ^4.0
Requires (Dev)
- guzzlehttp/guzzle: ^7.8
- laravel/pint: ^1.10
- mockery/mockery: ^1.6
- nyholm/psr7: ^1.8
- pestphp/pest: ^3.0
- phpstan/phpstan: ^2.1
This package is auto-updated.
Last update: 2026-05-03 12:55:28 UTC
README
PHP backend SDK for authn.sh — call the Backend API (BAPI), verify session JWTs, and verify webhook signatures from server-side PHP. The Laravel integration lives in the separate authn-sh/sdk-php-laravel package.
Status: 0.1.x pre-release. APIs may change before
1.0.
Requirements
- PHP 8.2+
- A PSR-18 HTTP client (Guzzle, Symfony HttpClient, etc.) and PSR-17 factories. The SDK auto-discovers them via
php-http/discovery, so installingguzzlehttp/guzzle(or any PSR-18 client) is enough. - A PSR-16 cache (optional, for the JWT verifier). Without one the JWKS document is re-fetched on every verify.
Install
composer require authn-sh/sdk-php
If you don't already have a PSR-18 client in your project:
composer require guzzlehttp/guzzle
BAPI client
use Authn\Sdk\Client; $client = new Client(secretKey: $_ENV['AUTHN_SECRET_KEY']); $user = $client->users()->get('user_2x4yT…'); $session = $client->sessions()->revoke('sess_…'); $invite = $client->invitations()->create(['email_address' => 'a@b.com']);
Resource managers: users(), sessions(), invitations(), allowlistIdentifiers(), blocklistIdentifiers(), redirectUrls(), instance(), webhookEndpoints().
Custom HTTP client / logger
use Authn\Sdk\Client; use GuzzleHttp\Client as Guzzle; $client = new Client( secretKey: $_ENV['AUTHN_SECRET_KEY'], apiUrl: 'https://api.authn.sh', http: new Guzzle(['timeout' => 5]), logger: $psr3Logger, );
Errors
Authn\Sdk\Http\ApiException— non-2xx responses;getStatusCode(),getErrors(),getErrorCode(),getTraceId(),getRawBody().Authn\Sdk\Http\AuthenticationException(401),Authn\Sdk\Http\ResourceNotFoundException(404),Authn\Sdk\Http\RateLimitExceededException(429, withgetRetryAfter()).Authn\Sdk\Http\NetworkException— connection-level failures.
Verify a session JWT
use Authn\Sdk\Tokens\TokenVerifier; use Authn\Sdk\Tokens\TokenInvalidException; $verifier = new TokenVerifier( publishableKey: $_ENV['AUTHN_PUBLISHABLE_KEY'], // pk_test_… / pk_live_… cache: $psr16Cache, // optional PSR-16 cache for JWKS ); try { $claims = $verifier->verify($jwt); // throws TokenInvalidException // or: $claims = $verifier->tryVerify($jwt); // returns null on failure $userId = $claims->sub; // user_… $sessionId = $claims->sid; // sess_… } catch (TokenInvalidException $e) { // 401 the request } // Optional: enforce origin binding via the azp claim. $claims = $verifier->verify($jwt, expectedAzp: ['https://app.acme.com']);
The verifier fetches the FAPI JWKS once and caches it (PSR-16). On an unknown kid it refreshes the JWKS once before failing.
Verify a webhook
use Authn\Sdk\Webhooks\SignatureVerifier; use Authn\Sdk\Webhooks\SignatureInvalidException; // One signing secret, or many during a rotation overlap window. $verifier = new SignatureVerifier($_ENV['AUTHN_WEBHOOK_SECRET']); try { $event = $verifier->verify($rawBody, $request->getHeaders()); // throws SignatureInvalidException if ($event->type === 'user.created') { // $event->data carries the resource payload } } catch (SignatureInvalidException $e) { // 401 the request }
Replay protection is on by default with a 5-minute tolerance — pass toleranceSeconds: to widen or narrow it.
Development
composer install composer test # Pest composer phpstan # PHPStan @ level 10 composer pint # code style check composer pint:fix # apply fixes
CI runs the full suite on PHP 8.2, 8.3, 8.4, and 8.5.