Loading...
Loading...
Use when protecting Laravel API endpoints with JWT Bearer token validation or scope checks. Integrates auth0/login with the AuthorizationGuard for stateless APIs receiving access tokens.
npx skill4agent add auth0/agent-skills auth0-laravel-apiauth0/loginAuthorizationGuardmbstringopenssljsonauth0-quickstart| Scenario | Use Instead |
|---|---|
| Laravel web app with login/logout UI | |
| Plain PHP API (no framework) | |
| Plain PHP web app | |
| Single Page Applications | |
| FastAPI / Python APIs | |
| Express / Node.js APIs | |
| Issuing tokens | This skill is for validating access tokens, not issuing them |
composer require auth0/loginauth0/loginauth0/auth0-phpcomposer require guzzlehttp/guzzle guzzlehttp/psr7php artisan vendor:publish --tag=auth0config/auth0.phpSTOP - ask the user before proceeding.Ask exactly this question and wait for their answer before doing anything else:"How would you like to create the Auth0 API resource?
- Automated - I'll run Auth0 CLI scripts that create the resource and write the exact values to your
automatically..env- Manual - You create the API yourself in the Auth0 Dashboard (or via
) and provide me the Domain and Audience.auth0 apis createWhich do you prefer? (1 = Automated / 2 = Manual)"Do NOT proceed to any setup steps until the user has answered. Do NOT default to manual.
.envauth0 apis create \
--name "My Laravel API" \
--identifier https://my-api.example.com \
--json.envAUTH0_DOMAIN=your-tenant.us.auth0.com
AUTH0_AUDIENCE=https://your-api.example.comAUTH0_DOMAINhttps://AUTH0_AUDIENCEconfig/auth.php'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'auth0-api' => [
'driver' => 'auth0.authorizer',
'provider' => 'auth0-provider',
'configuration' => 'api',
],
],
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\Models\User::class,
],
'auth0-provider' => [
'driver' => 'auth0.provider',
'repository' => 'auth0.repository',
],
],driverauth0.authorizerauth0.authenticatorconfiguration'api'apiconfig/auth0.phpauth0-apiconfig/auth0.phpguards.apistrategySdkConfiguration::STRATEGY_API'api'Configuration::CONFIG_STRATEGY'guards' => [
'api' => [
'strategy' => SdkConfiguration::STRATEGY_API, // value: 'api'
],
],defaultwebSTRATEGY_APIroutes/api.phpphp artisan install:apiroutes/api.phpbootstrap/app.php/apiroutes/api.phpuse Illuminate\Support\Facades\Route;
Route::get('/public', function () {
return response()->json(['message' => 'Public endpoint - no authentication required']);
});
Route::middleware('auth:auth0-api')->group(function () {
Route::get('/private', function () {
$user = auth('auth0-api')->user();
return response()->json([
'message' => 'Private endpoint',
'sub' => $user->getAuthIdentifier(),
]);
});
});auth:auth0-apihasScope()hasPermission()Route::middleware('auth:auth0-api')->group(function () {
Route::get('/messages', function () {
$guard = auth('auth0-api');
if (!$guard->hasScope('read:messages')) {
return response()->json(['error' => 'insufficient_scope'], 403);
}
return response()->json(['messages' => []]);
});
Route::delete('/users/{id}', function (string $id) {
$guard = auth('auth0-api');
if (!$guard->hasPermission('delete:users')) {
return response()->json(['error' => 'insufficient_permissions'], 403);
}
return response()->json(['deleted' => $id]);
});
});hasScope()scopehasPermission()permissionsStatelessUserRoute::middleware('auth:auth0-api')->get('/profile', function () {
$user = auth('auth0-api')->user();
return response()->json([
'sub' => $user->getAuthIdentifier(),
'email' => $user->email,
'permissions' => $user->permissions ?? [],
'all_claims' => $user->jsonSerialize(),
]);
});$user->getAuthIdentifier()sub$user->claim_name__get$user->getAttribute('claim_name')$user->jsonSerialize()Agent instruction: Start the Laravel dev server and verify the basic endpoints work without credentials:bashphp artisan serve & sleep 2 curl -s -H "Accept: application/json" http://localhost:8000/api/public curl -s -o /dev/null -w "%{http_code}" -H "Accept: application/json" http://localhost:8000/api/private
should return/api/public{"message":"Public endpoint - no authentication required"} should return HTTP 401/api/privateImportant: Theheader is required. Without it, Laravel's-H "Accept: application/json"middleware returns a 302 redirect instead of 401.AuthenticateIf both pass, the guard is working. Kill the background server when done:bashkill %1 2>/dev/nullFailcheck: If the server fails to start or responses are unexpected, check thatexists andconfig/auth0.phphas.envandAUTH0_DOMAINset. After 5-6 failed iterations, useAUTH0_AUDIENCE.AskUserQuestion
Agent instruction: STOP - ask after basic tests pass.Once public/401 tests pass, ask the user:"Your API is set up and the guard is working (public returns 200, private returns 401 without a token). Would you like me to help you set up testing with a real access token?
- Yes - I'll help you configure an M2M application to get test tokens.
- No - I'm done for now.
Which do you prefer?"Do NOT proceed with M2M/token setup unless the user says yes.
Agent instruction (M2M app setup - only if user wants testing):If the user chose to set up testing, ask:"To test your protected endpoints, you need a Machine-to-Machine (M2M) application authorized to request tokens for this API.
- Create new - I'll create a new M2M application and authorize it for this API.
- Use existing - You already have an M2M application. Provide the Client ID and I'll authorize it for this API.
Which do you prefer? (1 = Create new / 2 = Use existing)"Do NOT proceed until the user answers.If the user chose "Create new":bashauth0 apps create \ --name "${PWD##*/} (Test App)" \ --type m2m \ --no-input --jsonParse JSON withto extractjq. Do NOT useclient_id. Then create a client grant:--reveal-secretsbashauth0 api post "client-grants" --data '{ "client_id": "<CLIENT_ID>", "audience": "<API_IDENTIFIER>", "scope": ["<SCOPES>"] }'If the user chose "Use existing": Ask for Client ID. Create a client grant (409 conflict = already authorized, fine).
Agent instruction (TOKEN ISOLATION - CRITICAL):The agent MUST NEVER directly see or display access token values.
- Do NOT run
on its ownauth0 test token- Do NOT ask the user to paste their token
- Do NOT echo or store the token value
Secure testing (single-command chain):bashphp artisan serve & sleep 2 TEST_TOKEN=$(auth0 test token <M2M_CLIENT_ID> --audience <AUDIENCE> --scopes <SCOPE1,SCOPE2> 2>/dev/null | grep -o 'ey[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*') && \ [ -n "$TEST_TOKEN" ] && echo "Token acquired (${#TEST_TOKEN} chars)" && \ curl -s -H "Accept: application/json" -H "Authorization: Bearer $TEST_TOKEN" http://localhost:8000/api/private kill %1 2>/dev/nullIf the user does NOT ask to test, provide commands for them to run manually:bashauth0 test token <CLIENT_ID> --audience <AUDIENCE> --scopes <SCOPE1,SCOPE2> curl -H "Accept: application/json" -H "Authorization: Bearer <PASTE_TOKEN_HERE>" http://localhost:8000/api/private
php artisan servecurl -H "Accept: application/json" http://localhost:8000/api/publiccurl -H "Accept: application/json" http://localhost:8000/api/privateAccept: application/jsoncurl http://localhost:8000/api/private \
-H "Accept: application/json" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"| Mistake | Fix |
|---|---|
Using | API guard must use |
Using | Use |
| Created an Application instead of an API in Auth0 | Must create an API resource (Dashboard -> Applications -> APIs) |
Passing | Use bare domain: |
Using | Use |
| Not publishing config | Run |
Missing | Required for token validation - without it, tokens can't be verified against the correct audience |
Using | Defaults to the |
Checking | The |
Calling | Must enable "Add Permissions in the Access Token" in Auth0 Dashboard -> APIs -> Settings |
| Using ID tokens for API auth | Must use access tokens - ID tokens are for the client app |
Setting | Must be |
Testing with | Laravel returns 302 redirect instead of 401 - always send |
| Scopes must be defined on the API resource in Auth0 Dashboard - requesting a scope in the token request does not grant it unless defined |
| RBAC permissions are only embedded in tokens from user-based flows (Authorization Code), not client-credentials grants |
| Run |
| Method | Returns | Purpose |
|---|---|---|
| | Returns authenticated user or |
| | Whether request has a valid token |
| | Check if token has a specific scope |
| | Check if token has a specific RBAC permission |
| | Returns the |
| | Returns |
| | Returns any claim value |
| | Returns all claims as array |
| | Full credential with decoded token data |
auth0-laravelauth0-php-apiauth0-quickstartauth0-mfaauth0-cliconfig/auth.php'guards' => [
'auth0-api' => [
'driver' => 'auth0.authorizer',
'provider' => 'auth0-provider',
'configuration' => 'api',
],
],
'providers' => [
'auth0-provider' => [
'driver' => 'auth0.provider',
'repository' => 'auth0.repository',
],
],Route::middleware('auth:auth0-api')->group(function () {
Route::get('/resource', fn() => response()->json([...]));
});$guard = auth('auth0-api');
$guard->hasScope('read:messages'); // checks scope claim
$guard->hasPermission('delete:users'); // checks permissions claim (RBAC)$user = auth('auth0-api')->user();
$user->getAuthIdentifier(); // sub
$user->email; // any claim via __get
$user->getAttribute('iss'); // explicit claim accessAUTH0_DOMAINtenant.us.auth0.comAUTH0_AUDIENCEhttps://api.example.comauth:auth0-apihasScope()hasPermission()