User Login
Create a user login endpoint that verifies credentials and generates JWT tokens. Learn how to validate passwords, create tokens, and return them to authenticated users.
What You'll Learn
Login Endpoint
Create login API endpoint with schema validation
Password Verification
Verify user credentials using CryptHelper
JWT Token Generation
Generate and return JWT tokens to authenticated users
Video Coming Soon...
What is User Login?
User login is the process of authenticating users by verifying their credentials (email and password) and returning JWT tokens for subsequent API requests. GEMVC provides a ready-to-use User service template after running gemvc:init.
- Ready-to-Use Template - After
gemvc:init, you get a complete User service with Controller, Model, and Table layers - Password Hashing - Uses
CryptHelper::hashPassword()for secure password storage - JWT Token Generation - Uses
JWTTokenclass to create Access, Refresh, and Login tokens - Database Setup - Just run
gemvc db:migrate UserTableto create the users table
Key Point: After gemvc:init, you have a complete User service structure. You only need to add the login(), auth(), and renew() methods to make it fully functional!
Understanding the Structure
After gemvc:init
When you run php vendor/bin/gemvc gemvc:init, GEMVC creates a complete User service structure:
Created Files:
- ✓
app/api/User.php- API Service layer - ✓
app/controller/UserController.php- Controller layer - ✓
app/model/UserModel.php- Model layer - ✓
app/table/UserTable.php- Table layer
The User service includes CRUD operations (create, read, update, delete, list) and password hashing. You just need to add login, authentication check, and token renewal methods.
⚠️ Before You Start:
- ✓ Make sure you've configured JWT in
.env(see JWT Setup) - ✓ Run
gemvc db:migrate UserTableto create the users table - ✓ Create at least one user using the
User::create()endpoint
Add Login Method to API Layer
Creating the Login Endpoint
Add a login() method to your app/api/User.php file. This method validates the email and password, then delegates to the Controller layer.
<?php
namespace App\Api;
use App\Controller\UserController;
use Gemvc\Core\ApiService;
use Gemvc\Http\Request;
use Gemvc\Http\JsonResponse;
class User extends ApiService
{
// ... existing methods (create, read, update, delete, list) ...
/**
* User Login
*
* @return JsonResponse
* @http POST
* @description Authenticate user and return JWT tokens
* @example /api/User/login
*/
public function login(): JsonResponse
{
// Validate POST schema (email and password required)
if(!$this->request->definePostSchema([
'email' => 'email',
'password' => 'string'
])) {
return $this->request->returnResponse();
}
// Delegate to Controller layer
return (new UserController($this->request))->login();
}
}
Key Points:
- ✓ Validates email format and password presence
- ✓ Delegates to
UserController::login() - ✓ URL:
POST /api/User/login - ✓ No authentication required (public endpoint)
Add Login Method to Controller Layer
Orchestrating the Login Flow
Add a login() method to your app/controller/UserController.php file. This method maps the request data to the Model and delegates to the Model layer.
<?php
namespace App\Controller;
use App\Model\UserModel;
use Gemvc\Core\Controller;
use Gemvc\Http\Request;
use Gemvc\Http\JsonResponse;
class UserController extends Controller
{
// ... existing methods (create, read, update, delete, list) ...
/**
* User Login
*
* @return JsonResponse
*/
public function login(): JsonResponse
{
// Map POST data to UserModel
$model = $this->request->mapPostToObject(
new UserModel(),
['email' => 'email', 'password' => 'password']
);
if(!$model instanceof UserModel) {
return $this->request->returnResponse();
}
// Delegate to Model layer for authentication and token generation
return $model->loginModel();
}
}
Note: The Controller maps email and password from the request to the UserModel. The Model layer will handle password verification and token generation.
Add Login Method to Model Layer
Password Verification and Token Generation
Add a loginModel() method to your app/model/UserModel.php file. This method verifies the password and generates JWT tokens.
<?php
namespace App\Model;
use App\Table\UserTable;
use Gemvc\Helper\CryptHelper;
use Gemvc\Http\JWTToken;
use Gemvc\Http\JsonResponse;
use Gemvc\Http\Response;
class UserModel extends UserTable
{
// ... existing methods (createModel, readModel, updateModel, deleteModel, setPassword) ...
/**
* User Login - Verify credentials and generate JWT tokens
*
* @return JsonResponse
*/
public function loginModel(): JsonResponse
{
// Normalize email to lowercase
$this->email = strtolower($this->email);
// Find user by email
$user = $this->selectByEmail($this->email);
if (!$user) {
return Response::unprocessableEntity('Invalid email or password');
}
// Verify password
if (!CryptHelper::verifyPassword($this->password, $user->getPassword())) {
return Response::unprocessableEntity('Invalid email or password');
}
// Generate JWT tokens
$jwtToken = new JWTToken();
$accessToken = $jwtToken->createAccessToken($user->id);
$refreshToken = $jwtToken->createRefreshToken($user->id);
$loginToken = $jwtToken->createLoginToken($user->id);
// Prepare response data
$userData = [
'id' => $user->id,
'name' => $user->name,
'email' => $user->email,
'description' => $user->description
];
$tokenData = [
'access_token' => $accessToken,
'refresh_token' => $refreshToken,
'login_token' => $loginToken,
'user' => $userData
];
return Response::success($tokenData, 1, 'Login successful');
}
}
What This Method Does:
- 1. Normalizes email to lowercase for consistent lookup
- 2. Finds user by email using
selectByEmail() - 3. Verifies password using
CryptHelper::verifyPassword() - 4. Generates three JWT tokens (Access, Refresh, Login)
- 5. Returns tokens and user data in response
Security Best Practices:
- ✓ Generic Error Message: "Invalid email or password" prevents user enumeration
- ✓ Password Verification: Uses
CryptHelper::verifyPassword()for secure comparison - ✓ Token Generation: Creates multiple token types for different use cases
Complete Login Flow
Request and Response Example
Here's how the login flow works from request to response:
Request:
POST /api/User/login
Content-Type: application/json
{
"email": "user@example.com",
"password": "userpassword123"
}
Response (Success):
{
"response_code": 200,
"message": "OK",
"count": 1,
"service_message": "Login successful",
"data": {
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"login_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": {
"id": 1,
"name": "John Doe",
"email": "user@example.com",
"description": "User description"
}
}
}
Response (Error):
{
"response_code": 422,
"message": "unprocessable_entity",
"count": 0,
"service_message": "Invalid email or password",
"data": null
}
Setup Database
Migrate UserTable
Before you can use the login endpoint, you need to create the users table in your database. GEMVC provides a simple command for this:
Migration Command:
gemvc db:migrate UserTable
This command creates the users table with all columns, indexes, and constraints defined in UserTable::defineSchema().
What Gets Created:
- ✓ Table:
users - ✓ Columns:
id,name,email,description,password - ✓ Indexes:
email(indexed and unique) - ✓ Primary key:
id(auto increment)
Test Your Login Endpoint
Testing the Login
After setting up the database and adding the login methods, you can test your login endpoint:
Step 1: Create a User
First, create a user using the User::create() endpoint:
curl -X POST http://localhost:9501/api/User/create \
-H "Content-Type: application/json" \
-d '{
"name": "John Doe",
"email": "john@example.com",
"password": "secret123",
"description": "Test user"
}'
Step 2: Test Login
Now test the login endpoint:
curl -X POST http://localhost:9501/api/User/login \
-H "Content-Type: application/json" \
-d '{
"email": "john@example.com",
"password": "secret123"
}'
Note: Replace localhost:9501 with your actual server URL. For Apache/Nginx, use your domain name.
Important Notes
-
JWT Configuration Required: Make sure you've configured
TOKEN_SECRETandTOKEN_ISSUERin your.envfile (see JWT Setup). -
Database Migration: Always run
gemvc db:migrate UserTableafter modifying the UserTable class structure. - Password Hashing: Passwords are automatically hashed using Argon2i when creating users. Never store plain text passwords!
- Token Storage: Store tokens securely on the client side (e.g., httpOnly cookies or secure storage). Never expose tokens in URLs or logs.
✅ Login Endpoint Complete!
Great job! You've created a login endpoint that generates JWT tokens. Now let's learn how to protect your API routes using these tokens.