use ReturnEarly\CloudflareZeroTrust\Support\Access;
Access::user()?->email(); // "[email protected]" for SSO users
Access::service()?->commonName(); // "my-worker.access" for service tokens
use ReturnEarly\CloudflareZeroTrust\Enums\PrincipalKind;
'accounts' => [
'acme' => [
// Your team domain. Also accepted under the key 'issuer'.
'team_domain' => 'https://acme.cloudflareaccess.com',
// Optional. Defaults to {team_domain}/cdn-cgi/access/certs
// 'jwks_url' => 'https://acme.cloudflareaccess.com/cdn-cgi/access/certs',
'applications' => [
'internal-api' => [
// One or more AUD tags. Also accepted under the key 'audiences'.
'audience' => [env('CLOUDFLARE_INTERNAL_API_AUD')],
// Optional. Prefer a per-app env var so you can disable enforcement
// in local/staging without turning off every application.
// When false, middleware passes through and the guard returns no user.
'enabled' => (bool) env('CLOUDFLARE_INTERNAL_API_ENABLED', true),
// Which principal kinds may authenticate.
// Defaults to [PrincipalKind::User] — service tokens are opt-in.
'principals' => [PrincipalKind::User, PrincipalKind::Service],
// Optional. Set false to forbid identity enrichment for this app.
'identity_enabled' => true,
],
'horizon' => [
'audience' => [env('CLOUDFLARE_HORIZON_AUD')],
'enabled' => (bool) env('CLOUDFLARE_HORIZON_ENABLED', true),
'principals' => [PrincipalKind::User], // humans only
],
],
],
],
Route::middleware('cloudflare-access:internal-api')->group(function () {
// API endpoints reachable by SSO users and service tokens
});
Route::middleware('cloudflare-access:horizon')->group(function () {
// Human-only admin surface
});
use ReturnEarly\CloudflareZeroTrust\Support\Access;
Access::token(); // ?VerifiedAccessToken — raw JWT, header, claims, expiry
Access::principal(); // ?Principal — UserPrincipal or ServicePrincipal
Access::user(); // ?UserPrincipal — null if this is a service request
Access::service(); // ?ServicePrincipal — null if this is a user request
Access::isUser(); // bool
Access::isService(); // bool
Access::commonName(); // ?string — the service token's client ID
Access::applicationIs('horizon'); // bool — which named application authenticated this request
Access::identity(); // ?AccessIdentity — lazy enrichment; throws on failure
Access::tryIdentity(); // ?AccessIdentity — null instead of throwing
Access::hasGroup('Finance-Team'); // bool — never throws
$user = Access::user();
$user->subject(); // stable Cloudflare user ID (JWT "sub")
$user->email();
$user->country(); // ?string, e.g. "US"
$user->accountName(); // config account name, e.g. "acme"
$user->applicationName();
$user->claims(); // full validated claim set
$service = Access::service();
$service->commonName(); // the service token client ID, e.g. "my-worker.access"
$service->claims();
Route::middleware('auth:cloudflare')->get('/me', function () {
return auth('cloudflare')->user(); // UserPrincipal or ServicePrincipal
});
auth('cloudflare')->check();
auth('cloudflare')->id(); // user "sub" or service "common_name"
use Illuminate\Contracts\Auth\Authenticatable;
use ReturnEarly\CloudflareZeroTrust\Contracts\ApplicationUserResolver;
use ReturnEarly\CloudflareZeroTrust\Principals\UserPrincipal;
class CloudflareUserResolver implements ApplicationUserResolver
{
public function resolve(UserPrincipal $principal): ?Authenticatable
{
return User::firstWhere('email', $principal->email());
}
}