Protected Routes
Protect your API endpoints with authentication and authorization. Learn how to use auth() method to verify JWT tokens and implement role-based access control.
What You'll Learn
Understanding auth()
What is the auth() method and how it works
Authentication
Protect routes with auth() - check for valid token
Authorization
Protect routes with auth([roles]) - check for specific roles
Video Coming Soon...
What is Protected Routes?
Protected routes are API endpoints that require authentication or authorization before allowing access. GEMVC provides a simple auth() method in the Request object to protect your endpoints.
- Authentication - Verifies that the user has a valid JWT token (
auth()) - Authorization - Verifies that the user has specific roles (
auth(['admin', 'salesManager'])) - Automatic Response - Returns 401 (Unauthorized) or 403 (Forbidden) automatically if check fails
- Multi-Role Support - Can check for multiple roles in a single call
Key Point: The auth() method is available in all API Service classes via $this->request->auth(). It automatically validates JWT tokens and checks roles.
Understanding auth() Method
The auth() Method
The auth() method is available in the Request object and can be called in two ways:
1. Authentication Only (No Arguments)
$this->request->auth() - Checks if the user has a valid JWT token.
- ✓ Verifies JWT token signature
- ✓ Checks token expiration
- ✓ Validates user ID
- ✓ Returns
trueif valid,falseotherwise - ✓ Returns 401 (Unauthorized) if invalid
2. Authorization with Roles (Array Argument)
$this->request->auth(['admin', 'salesManager']) - Checks if the user has a valid token AND has one of the specified roles.
- ✓ First performs authentication check (valid token)
- ✓ Then checks if user has any of the specified roles
- ✓ Supports multiple roles (OR logic - user needs at least one)
- ✓ Returns
trueif authenticated and authorized - ✓ Returns 401 (Unauthorized) if token invalid
- ✓ Returns 403 (Forbidden) if token valid but role missing
How Roles Work:
Roles are stored in the JWT token payload as a comma-separated string (e.g., "admin,user"). When you call auth(['admin', 'salesManager']), GEMVC checks if the user's token contains at least one of these roles.
Example: If token has role: "admin,user" and you call auth(['admin', 'salesManager']), it will return true because the user has the "admin" role.
Basic Authentication (auth() without arguments)
Protecting Routes with Authentication
Use auth() without arguments to require a valid JWT token. This is useful for endpoints that any authenticated user can access.
<?php
namespace App\Api;
use App\Controller\ProductController;
use Gemvc\Core\ApiService;
use Gemvc\Http\Request;
use Gemvc\Http\JsonResponse;
class Product extends ApiService
{
public function __construct(Request $request)
{
parent::__construct($request);
}
// Read - Requires authentication (any logged-in user)
public function read(): JsonResponse
{
// Check if user has valid JWT token
if (!$this->request->auth()) {
return $this->request->returnResponse(); // Returns 401 Unauthorized
}
// User is authenticated - proceed with request
return (new ProductController($this->request))->read();
}
// Update - Requires authentication (any logged-in user)
public function update(): JsonResponse
{
// Check authentication first
if (!$this->request->auth()) {
return $this->request->returnResponse(); // Returns 401 Unauthorized
}
// Validate schema
if(!$this->request->definePostSchema([
'id' => 'int',
'?name' => 'string',
'?price' => 'float'
])) {
return $this->request->returnResponse();
}
// User is authenticated - proceed
return (new ProductController($this->request))->update();
}
}
What Happens:
- ✓ If token is valid → Returns
true, request continues - ✓ If token is missing/invalid/expired → Returns
false - ✓
returnResponse()automatically returns 401 Unauthorized - ✓ No need to manually check response codes
Role-Based Authorization (auth() with roles)
Protecting Routes with Specific Roles
Use auth(['role1', 'role2']) with an array of roles to restrict access to users with specific roles. This is useful for admin-only endpoints or role-specific operations.
<?php
namespace App\Api;
use App\Controller\ProductController;
use Gemvc\Core\ApiService;
use Gemvc\Http\Request;
use Gemvc\Http\JsonResponse;
class Product extends ApiService
{
public function __construct(Request $request)
{
parent::__construct($request);
}
// Create - Only admin and salesManager can create products
public function create(): JsonResponse
{
// Check if user has valid token AND has 'admin' or 'salesManager' role
if (!$this->request->auth(['admin', 'salesManager'])) {
return $this->request->returnResponse();
// Returns 401 if no token, 403 if token valid but role missing
}
// Validate schema
if(!$this->request->definePostSchema([
'name' => 'string',
'price' => 'float',
'?description' => 'string'
])) {
return $this->request->returnResponse();
}
// User is authenticated and authorized - proceed
return (new ProductController($this->request))->create();
}
// Delete - Only admin can delete products
public function delete(): JsonResponse
{
// Check if user has 'admin' role
if (!$this->request->auth(['admin'])) {
return $this->request->returnResponse(); // Returns 401 or 403
}
// Validate schema
if(!$this->request->definePostSchema([
'id' => 'int'
])) {
return $this->request->returnResponse();
}
// User is authenticated and authorized - proceed
return (new ProductController($this->request))->delete();
}
}
How Role Checking Works:
- ✓
auth(['admin', 'salesManager'])checks if user has at least one of these roles - ✓ If user has 'admin' role → Access granted
- ✓ If user has 'salesManager' role → Access granted
- ✓ If user has neither role → Returns 403 Forbidden
- ✓ If token is invalid → Returns 401 Unauthorized
Response Codes:
- 401 Unauthorized - Token is missing, invalid, or expired
- 403 Forbidden - Token is valid but user doesn't have required role(s)
Complete Example: Product API
Full Product API with Different Access Levels
Here's a complete example showing different protection levels for different endpoints:
<?php
namespace App\Api;
use App\Controller\ProductController;
use Gemvc\Core\ApiService;
use Gemvc\Http\Request;
use Gemvc\Http\JsonResponse;
class Product extends ApiService
{
public function __construct(Request $request)
{
parent::__construct($request);
}
// Create - Only admin and salesManager
public function create(): JsonResponse
{
if (!$this->request->auth(['admin', 'salesManager'])) {
return $this->request->returnResponse();
}
if(!$this->request->definePostSchema([
'name' => 'string',
'price' => 'float',
'?description' => 'string'
])) {
return $this->request->returnResponse();
}
return (new ProductController($this->request))->create();
}
// Read - Any authenticated user
public function read(): JsonResponse
{
if (!$this->request->auth()) {
return $this->request->returnResponse();
}
if(!$this->request->defineGetSchema(['id' => 'int'])) {
return $this->request->returnResponse();
}
$id = $this->request->intValueGet('id');
if(!$id) {
return $this->request->returnResponse();
}
$this->request->post['id'] = $id;
return (new ProductController($this->request))->read();
}
// Update - Any authenticated user
public function update(): JsonResponse
{
if (!$this->request->auth()) {
return $this->request->returnResponse();
}
if(!$this->request->definePostSchema([
'id' => 'int',
'?name' => 'string',
'?price' => 'float'
])) {
return $this->request->returnResponse();
}
return (new ProductController($this->request))->update();
}
// Delete - Only admin
public function delete(): JsonResponse
{
if (!$this->request->auth(['admin'])) {
return $this->request->returnResponse();
}
if(!$this->request->definePostSchema(['id' => 'int'])) {
return $this->request->returnResponse();
}
return (new ProductController($this->request))->delete();
}
// List - Public (no authentication required)
public function list(): JsonResponse
{
$this->request->findable(['name' => 'string']);
$this->request->sortable(['id', 'name', 'price']);
return (new ProductController($this->request))->list();
}
}
Access Control Summary:
- ✓
create()- Requires 'admin' OR 'salesManager' role - ✓
read()- Requires any authenticated user - ✓
update()- Requires any authenticated user - ✓
delete()- Requires 'admin' role only - ✓
list()- Public (no authentication required)
Testing Protected Routes
How to Test Authentication and Authorization
When testing protected routes, you need to include the JWT token in the Authorization header:
Request with Token:
curl -X POST http://localhost:9501/api/Product/create \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_JWT_TOKEN_HERE" \
-d '{
"name": "New Product",
"price": 99.99,
"description": "Product description"
}'
Response (Success - 200):
{
"response_code": 201,
"message": "created",
"count": 1,
"service_message": "Product created successfully",
"data": { ... }
}
Response (No Token - 401):
{
"response_code": 401,
"message": "unauthorized",
"count": 0,
"service_message": "Invalid JWT token. Authentication failed",
"data": null
}
Response (Wrong Role - 403):
{
"response_code": 403,
"message": "forbidden",
"count": 0,
"service_message": "Role user not allowed to perform this action",
"data": null
}
Important Notes
- auth() without arguments: Only checks if the user has a valid JWT token. Any authenticated user can access the endpoint.
- auth(['role1', 'role2']): Checks for valid token AND verifies the user has at least one of the specified roles. Uses OR logic (user needs one role, not all).
- Response Codes: 401 = Invalid/missing token, 403 = Valid token but missing required role(s).
-
Token Format: Client must send token in
Authorization: Bearer TOKENheader format. -
Role Storage: Roles are stored in JWT token payload as comma-separated string (e.g.,
"admin,user").
🔐 Protected Routes Complete!
Excellent! You've learned how to protect your API endpoints with authentication and role-based authorization. Your GEMVC application is now secure!