Authentication
ApnaPHP provides a built-in authentication system that handles user login, registration, password management, and session handling out of the box.
1. Basic Authentication
Check if User is Logged In
<?php
if (auth()->check()) {
// User is logged in
$user = auth()->user();
echo "Welcome, " . $user['name'];
} else {
// User is not logged in
return redirect('/login');
}
Login User
<?php
// app/login/page.apna.php
use ApnaPHP\Routing\Request;
use ApnaPHP\Routing\Response;
function POST(Request $request): Response
{
$credentials = [
'email' => $request->input('email'),
'password' => $request->input('password')
];
$remember = $request->input('remember') === 'on';
if (auth()->attempt($credentials, $remember)) {
// Login successful
return redirect('/dashboard')->withSuccess('Welcome back!');
} else {
// Login failed
return redirect('/login')->withError('Invalid credentials');
}
}
?>
<!DOCTYPE html>
<html>
<head>
<title>Login</title>
</head>
<body>
<form method="POST">
<?= csrfField() ?>
<input type="email" name="email" value="<?= old('email') ?>" required>
<input type="password" name="password" required>
<label>
<input type="checkbox" name="remember">
Remember Me
</label>
<button type="submit">Login</button>
</form>
</body>
</html>
Register User
<?php
// app/register/page.apna.php
use ApnaPHP\Routing\Request;
use ApnaPHP\Routing\Response;
function POST(Request $request): Response
{
try {
$validated = validate($request->all(), [
'name' => 'required|min:3|max:255',
'email' => 'required|email',
'password' => 'required|min:8|confirmed'
]);
// Register user
$user = auth()->register($validated);
// Auto-login after registration
auth()->attempt([
'email' => $validated['email'],
'password' => $validated['password']
]);
return redirect('/dashboard')->withSuccess('Account created successfully!');
} catch (ValidationException $e) {
return redirect('/register')->withErrors($e->errors())->withInput();
}
}
?>
<!DOCTYPE html>
<html>
<head>
<title>Register</title>
</head>
<body>
<form method="POST">
<?= csrfField() ?>
<input type="text" name="name" value="<?= old('name') ?>" required>
<input type="email" name="email" value="<?= old('email') ?>" required>
<input type="password" name="password" required>
<input type="password" name="password_confirmation" required>
<button type="submit">Register</button>
</form>
</body>
</html>
Logout User
<?php
// app/logout/page.apna.php
auth()->logout();
return redirect('/')->withSuccess('Logged out successfully!');
2. Authentication Helpers
ApnaPHP provides many authentication helper functions:
auth()
Get authentication instance.
$authInstance = auth();
user()
Get current authenticated user.
$user = user(); // Returns user array or null
echo $user['name'];
echo $user['email'];
userId()
Get current user ID.
$id = userId(); // Returns int or null
userEmail()
Get current user email.
$email = userEmail(); // Returns string or null
userName()
Get current user name.
$name = userName(); // Returns string or null
userRole()
Get current user role.
$role = userRole(); // Returns string or null
isLoggedIn()
Check if user is logged in.
if (isLoggedIn()) {
// User is authenticated
}
isGuest()
Check if user is guest (not logged in).
if (isGuest()) {
// User is not authenticated
}
isAdmin()
Check if current user is admin.
if (isAdmin()) {
// User is admin
}
hasRole()
Check if current user has specific role.
if (hasRole('editor')) {
// User has editor role
}
3. Protecting Routes with Middleware
Require Authentication
<?php
// app/dashboard/middleware.apna.php
if (!auth()->check()) {
return redirect('/login')->withError('Please login to continue.');
}
Require Specific Role
<?php
// app/admin/middleware.apna.php
if (!auth()->check()) {
return redirect('/login')->withError('Please login to continue.');
}
if (!auth()->isAdmin()) {
return abort(403, 'Admin access required');
}
Using Helper Functions
<?php
// app/admin/middleware.apna.php
requireAuth(); // Redirects to /login if not authenticated
requireAdmin(); // Redirects to /unauthorized if not admin
requireRole('editor'); // Redirects to /unauthorized if not editor
4. Password Management
Hash Password
$hashedPassword = hashPassword('mypassword');
// Or
$hashedPassword = auth()->hashPassword('mypassword');
Verify Password
if (verifyPassword('mypassword', $hashedPassword)) {
// Password is correct
}
// Or
if (auth()->verifyPassword('mypassword', $hashedPassword)) {
// Password is correct
}
Change Password
<?php
// app/settings/password/page.apna.php
function POST(Request $request): Response
{
try {
$validated = validate($request->all(), [
'current_password' => 'required',
'new_password' => 'required|min:8|confirmed'
]);
if (changePassword($validated['current_password'], $validated['new_password'])) {
return redirect('/settings')->withSuccess('Password changed successfully!');
} else {
return redirect('/settings/password')->withError('Current password is incorrect');
}
} catch (ValidationException $e) {
return redirect('/settings/password')->withErrors($e->errors());
}
}
Password Reset
Generate Reset Token:
<?php
// app/forgot-password/page.apna.php
function POST(Request $request): Response
{
$email = $request->input('email');
if (!isValidEmail($email)) {
return redirect('/forgot-password')->withError('Invalid email address');
}
$token = generatePasswordResetToken($email);
if ($token) {
// Send email with reset link
$resetLink = env('APP_URL') . "/reset-password?token={$token}";
// mail($email, 'Password Reset', "Click here to reset: {$resetLink}");
return redirect('/forgot-password')->withSuccess('Password reset link sent to your email');
} else {
return redirect('/forgot-password')->withError('Email not found');
}
}
Reset Password with Token:
<?php
// app/reset-password/page.apna.php
function POST(Request $request): Response
{
try {
$validated = validate($request->all(), [
'token' => 'required',
'password' => 'required|min:8|confirmed'
]);
if (resetPassword($validated['token'], $validated['password'])) {
return redirect('/login')->withSuccess('Password reset successfully!');
} else {
return redirect('/reset-password')->withError('Invalid or expired token');
}
} catch (ValidationException $e) {
return redirect('/reset-password')->withErrors($e->errors());
}
}
5. Role-Based Access Control
Define Roles
In your User model:
<?php
namespace App\Models;
use ApnaPHP\Database\Model;
class User extends Model
{
protected array $schema = [
'name' => 'required|type:string|length:255',
'email' => 'required|unique|type:string|length:255',
'password' => 'required|type:string',
'role' => 'type:string|default:user|length:50', // admin, editor, user
'status' => 'type:string|default:active|length:50',
];
public function isAdmin(): bool
{
return $this->role === 'admin';
}
public function isEditor(): bool
{
return $this->role === 'editor';
}
public function can(string $permission): bool
{
$permissions = [
'admin' => ['create', 'read', 'update', 'delete', 'manage_users'],
'editor' => ['create', 'read', 'update'],
'user' => ['read']
];
return in_array($permission, $permissions[$this->role] ?? []);
}
}
Check Permissions
<?php
// app/posts/edit/[id]/page.apna.php
if (!auth()->check()) {
return redirect('/login');
}
$user = auth()->user();
// Check role
if ($user['role'] !== 'admin' && $user['role'] !== 'editor') {
return abort(403, 'You do not have permission to edit posts');
}
// Or use helper
if (!hasRole('admin') && !hasRole('editor')) {
return abort(403, 'Insufficient permissions');
}
6. API Authentication
Token-Based Authentication
<?php
// app/api/middleware.apna.php
use ApnaPHP\Routing\Request;
use ApnaPHP\Routing\Response;
$token = $request->header('Authorization');
if (!$token) {
return Response::json(['error' => 'Authorization header required'], 401);
}
// Remove 'Bearer ' prefix
$token = str_replace('Bearer ', '', $token);
// Validate token (example - in real app, check database)
$validToken = env('API_TOKEN', 'your-secret-token');
if ($token !== $validToken) {
return Response::json(['error' => 'Invalid token'], 401);
}
// Token is valid, continue
Session-Based API Auth
<?php
// app/api/middleware.apna.php
if (!auth()->check()) {
return Response::json(['error' => 'Unauthorized'], 401);
}
// Check role for API access
if (!auth()->isAdmin()) {
return Response::json(['error' => 'Admin access required'], 403);
}
7. Security Features
CSRF Protection
<?php
// app/page.apna.php
?>
<form method="POST" action="/submit">
<?= csrfField() ?> <!-- Generates hidden CSRF token field -->
<input type="text" name="data">
<button type="submit">Submit</button>
</form>
Verify CSRF Token:
<?php
// app/submit/page.apna.php
function POST(Request $request): Response
{
$token = $request->input('_token');
if (!verifyCsrfToken($token)) {
return abort(419, 'CSRF token mismatch');
}
// Process form
}
Rate Limiting
<?php
// app/api/middleware.apna.php
$ip = $request->ip();
$rateLimitKey = "rate_limit:api:{$ip}";
// Get current count
$currentRequests = cache()->get($rateLimitKey, 0);
if ($currentRequests >= 100) { // 100 requests per hour
return Response::json([
'error' => 'Too Many Requests',
'message' => 'Rate limit exceeded. Please try again later.'
], 429);
}
// Increment counter
cache()->put($rateLimitKey, $currentRequests + 1, 3600); // 1 hour TTL
Account Lockout
<?php
// app/login/page.apna.php
function POST(Request $request): Response
{
$email = $request->input('email');
// Check if account is locked
if (auth()->isAccountLocked($email)) {
return redirect('/login')->withError('Account locked due to too many failed attempts. Try again later.');
}
if (auth()->attempt($request->only(['email', 'password']))) {
// Clear login attempts on success
auth()->clearLoginAttempts($email);
return redirect('/dashboard');
} else {
// Increment failed attempts
auth()->incrementLoginAttempts($email);
$attempts = auth()->getLoginAttempts($email);
$remaining = 5 - $attempts;
return redirect('/login')->withError("Invalid credentials. {$remaining} attempts remaining.");
}
}
8. Session Management
Get Session Data
<?php
$sessionData = auth()->getSessionData();
// Returns:
// [
// 'logged_in' => true,
// 'user_id' => 1,
// 'user_email' => 'user@example.com',
// 'user_name' => 'John Doe',
// 'user_role' => 'admin',
// 'session_id' => 'abc123...'
// ]
Flash Messages
<?php
// Set flash message
flash('success', 'Operation completed successfully!');
flash('error', 'Something went wrong');
// Get flash message (and remove it)
$message = getFlash('success');
// Check if flash message exists
if (hasFlash('error')) {
$error = getFlash('error');
}
Old Input
<?php
// Redirect with old input for form repopulation
return redirect('/register')->withInput()->withError('Validation failed');
?>
<!-- In the form -->
<input type="text" name="name" value="<?= old('name') ?>">
<input type="email" name="email" value="<?= old('email') ?>">
9. Complete Authentication Example
Here's a complete authentication flow:
Registration Page
<?php
// app/register/page.apna.php
use ApnaPHP\Routing\Request;
use ApnaPHP\Routing\Response;
use App\Models\User;
function POST(Request $request): Response
{
try {
$validated = validate($request->all(), [
'name' => 'required|min:3|max:255',
'email' => 'required|email',
'password' => 'required|min:8|confirmed',
'terms' => 'required|in:yes,on,1,true'
], [
'terms.required' => 'You must accept the terms and conditions',
'password.confirmed' => 'Password confirmation does not match'
]);
// Create user
$user = User::create([
'name' => $validated['name'],
'email' => $validated['email'],
'password' => $validated['password'], // Will be hashed by model
'role' => 'user',
'status' => 'active'
]);
// Auto-login
auth()->attempt([
'email' => $validated['email'],
'password' => $validated['password']
]);
console_success('User registered', ['email' => $user->email]);
return redirect('/dashboard')->withSuccess('Welcome to ApnaPHP!');
} catch (ValidationException $e) {
return redirect('/register')->withErrors($e->errors())->withInput();
}
}
$errors = getFlash('errors', []);
?>
<!DOCTYPE html>
<html>
<head>
<title>Register - ApnaPHP</title>
</head>
<body>
<h1>Create Account</h1>
<?php if ($error = getFlash('error')): ?>
<div class="error"><?= $error ?></div>
<?php endif; ?>
<form method="POST">
<?= csrfField() ?>
<div>
<label>Name:</label>
<input type="text" name="name" value="<?= old('name') ?>" required>
<?php if (isset($errors['name'])): ?>
<span class="error"><?= $errors['name'][0] ?></span>
<?php endif; ?>
</div>
<div>
<label>Email:</label>
<input type="email" name="email" value="<?= old('email') ?>" required>
<?php if (isset($errors['email'])): ?>
<span class="error"><?= $errors['email'][0] ?></span>
<?php endif; ?>
</div>
<div>
<label>Password:</label>
<input type="password" name="password" required>
<?php if (isset($errors['password'])): ?>
<span class="error"><?= $errors['password'][0] ?></span>
<?php endif; ?>
</div>
<div>
<label>Confirm Password:</label>
<input type="password" name="password_confirmation" required>
</div>
<div>
<label>
<input type="checkbox" name="terms" value="yes">
I agree to the terms and conditions
</label>
<?php if (isset($errors['terms'])): ?>
<span class="error"><?= $errors['terms'][0] ?></span>
<?php endif; ?>
</div>
<button type="submit">Create Account</button>
<a href="/login">Already have an account?</a>
</form>
</body>
</html>
Login Page
<?php
// app/login/page.apna.php
use ApnaPHP\Routing\Request;
use ApnaPHP\Routing\Response;
function POST(Request $request): Response
{
$email = $request->input('email');
// Check if account is locked
if (auth()->isAccountLocked($email)) {
return redirect('/login')->withError('Account locked. Please try again later.');
}
try {
$validated = validate($request->all(), [
'email' => 'required|email',
'password' => 'required'
]);
$remember = $request->input('remember') === 'on';
if (auth()->attempt($validated, $remember)) {
auth()->clearLoginAttempts($email);
console_success('User logged in', ['email' => $email]);
return redirect('/dashboard')->withSuccess('Welcome back!');
} else {
auth()->incrementLoginAttempts($email);
$remaining = 5 - auth()->getLoginAttempts($email);
return redirect('/login')
->withError("Invalid credentials. {$remaining} attempts remaining.")
->withInput(['email' => $email]);
}
} catch (ValidationException $e) {
return redirect('/login')->withErrors($e->errors())->withInput();
}
}
?>
<!DOCTYPE html>
<html>
<head>
<title>Login - ApnaPHP</title>
</head>
<body>
<h1>Login</h1>
<?php if ($error = getFlash('error')): ?>
<div class="error"><?= $error ?></div>
<?php endif; ?>
<form method="POST">
<?= csrfField() ?>
<div>
<label>Email:</label>
<input type="email" name="email" value="<?= old('email') ?>" required>
</div>
<div>
<label>Password:</label>
<input type="password" name="password" required>
</div>
<div>
<label>
<input type="checkbox" name="remember">
Remember Me
</label>
</div>
<button type="submit">Login</button>
<a href="/register">Create an account</a>
<a href="/forgot-password">Forgot password?</a>
</form>
</body>
</html>
Protected Dashboard
<?php
// app/dashboard/middleware.apna.php
requireAuth(); // Automatically redirects if not logged in
?>
<?php
// app/dashboard/page.apna.php
metadata([
'title' => 'Dashboard - ' . userName(),
'robots' => 'noindex, nofollow' // Don't index private pages
]);
$user = user();
?>
<!DOCTYPE html>
<html>
<head>
<title>Dashboard</title>
</head>
<body>
<h1>Welcome, <?= e(userName()) ?>!</h1>
<div>
<p><strong>Email:</strong> <?= e(userEmail()) ?></p>
<p><strong>Role:</strong> <?= e(userRole()) ?></p>
<p><strong>User ID:</strong> <?= userId() ?></p>
</div>
<?php if (isAdmin()): ?>
<div class="admin-section">
<h2>Admin Panel</h2>
<a href="/admin/users">Manage Users</a>
</div>
<?php endif; ?>
<a href="/logout">Logout</a>
</body>
</html>
10. Best Practices
- Always Hash Passwords: Never store plain text passwords. Use
hashPassword(). - Use CSRF Protection: Always include CSRF tokens in forms.
- Implement Rate Limiting: Prevent brute force attacks with rate limiting.
- Account Lockout: Lock accounts after multiple failed login attempts.
- Remember Me Securely: Use secure cookies with proper expiry times.
- Validate Input: Always validate user input before processing.
- Log Authentication Events: Log logins, logouts, and failed attempts for security auditing.
- Use HTTPS: Always use HTTPS in production to protect credentials.
- Session Security: Use secure session settings and regenerate session IDs on login.
- Password Requirements: Enforce strong password requirements (min 8 chars, complexity).
ApnaPHP's authentication system provides a solid foundation for securing your application while remaining simple and flexible.
