Skip to content

Authentication ​

Overview ​

Nexphant provides session-based authentication with:

  • Password hashing
  • Session management
  • Remember me functionality
  • Automatic session regeneration
  • Middleware protection

Setup ​

Enable Middleware ​

php
use Nexphant\App;
use Nexphant\Server\Middleware\{SessionMiddleware, AuthMiddleware};

$app = App::create();

$app->middleware(SessionMiddleware::class);

$app->middlewareAlias([
    'auth' => AuthMiddleware::class,
]);

User Model ​

php
use Nexphant\Database\Model;
use Nexphant\Database\Attributes\*;

#[Table('users')]
class User extends Model
{
    #[Id]
    public int $id;

    #[Email]
    #[Unique]
    public string $email;

    #[Length(min: 3, max: 100)]
    public string $name;

    #[Hidden]
    public string $password;

    #[Nullable]
    public ?string $rememberToken;

    #[CreatedAt]
    public DateTimeImmutable $createdAt;

    #[UpdatedAt]
    public DateTimeImmutable $updatedAt;
}

Registration ​

php
use App\Models\User;

$app->post('/register', function (Request $request) {
    $data = $request->json();
    
    // Validate
    if (User::query()->where('email', $data['email'])->exists()) {
        return ['error' => 'Email already exists'];
    }
    
    // Hash password
    $data['password'] = password_hash($data['password'], PASSWORD_DEFAULT);
    
    // Create user
    $userId = User::query()->insertGetId($data);
    
    // Log in
    session()->regenerate();
    session()->put('user_id', $userId);
    
    return ['success' => true];
});

Login ​

Basic Login ​

php
$app->post('/login', function (Request $request) {
    $credentials = $request->json();
    
    $user = User::query()
        ->where('email', $credentials['email'])
        ->first();
    
    if (!$user || !password_verify($credentials['password'], $user->password)) {
        return ['error' => 'Invalid credentials'];
    }
    
    // Regenerate session
    session()->regenerate();
    
    // Store user ID
    session()->put('user_id', $user->id);
    
    return ['success' => true];
});

With Remember Me ​

php
$app->post('/login', function (Request $request) {
    $credentials = $request->json();
    $remember = $credentials['remember'] ?? false;
    
    $user = User::query()
        ->where('email', $credentials['email'])
        ->first();
    
    if (!$user || !password_verify($credentials['password'], $user->password)) {
        return ['error' => 'Invalid credentials'];
    }
    
    session()->regenerate();
    session()->put('user_id', $user->id);
    
    if ($remember) {
        $token = bin2hex(random_bytes(32));
        
        User::query()
            ->where('id', $user->id)
            ->update(['remember_token' => hash('sha256', $token)]);
        
        cookie()
            ->make('remember_token', $token)
            ->maxAge(60 * 60 * 24 * 30) // 30 days
            ->httpOnly()
            ->secure();
    }
    
    return ['success' => true];
});

Logout ​

php
$app->post('/logout', function () {
    $userId = session()->get('user_id');
    
    if ($userId) {
        User::query()
            ->where('id', $userId)
            ->update(['remember_token' => null]);
    }
    
    session()->invalidate();
    
    cookie()->forget('remember_token');
    
    return ['success' => true];
});

Auth Helper ​

Check Authentication ​

php
if (auth()->check()) {
    // User is logged in
}

if (auth()->guest()) {
    // User is not logged in
}

Get Current User ​

php
$user = auth()->user();

if ($user) {
    echo $user->name;
}

User ID ​

php
$userId = auth()->id();

Protected Routes ​

Using Middleware ​

php
$app->get('/profile', function () {
    $user = auth()->user();
    
    return ['user' => $user];
})->middleware('auth');

Route Group ​

php
$app->group(['middleware' => ['auth']], function ($group) {
    $group->get('/profile', fn() => ['user' => auth()->user()]);
    $group->get('/settings', fn() => ['settings' => 'page']);
    $group->post('/update', fn(Request $req) => ['updated' => true]);
});

Manual Check ​

php
$app->get('/profile', function () {
    if (!auth()->check()) {
        return ['error' => 'Unauthorized'];
    }
    
    return ['user' => auth()->user()];
});

Configuration ​

config/auth.php:

php
return [
    'model' => \App\Models\User::class,
    
    'session_key' => 'user_id',
    
    'remember_key' => 'remember_token',
    
    'password_timeout' => 10800, // 3 hours
];

Password Reset ​

Request Reset ​

php
$app->post('/forgot-password', function (Request $request) {
    $email = $request->json('email');
    
    $user = User::query()->where('email', $email)->first();
    
    if (!$user) {
        return ['message' => 'If email exists, reset link sent'];
    }
    
    $token = bin2hex(random_bytes(32));
    
    // Store token (use separate password_resets table)
    DB::table('password_resets')->insert([
        'email' => $email,
        'token' => hash('sha256', $token),
        'created_at' => time(),
    ]);
    
    // Send email with $token...
    
    return ['message' => 'Reset link sent'];
});

Reset Password ​

php
$app->post('/reset-password', function (Request $request) {
    $data = $request->json();
    
    $reset = DB::table('password_resets')
        ->where('email', $data['email'])
        ->where('token', hash('sha256', $data['token']))
        ->first();
    
    if (!$reset || $reset['created_at'] < time() - 3600) {
        return ['error' => 'Invalid or expired token'];
    }
    
    $hashedPassword = password_hash($data['password'], PASSWORD_DEFAULT);
    
    User::query()
        ->where('email', $data['email'])
        ->update(['password' => $hashedPassword]);
    
    DB::table('password_resets')
        ->where('email', $data['email'])
        ->delete();
    
    return ['success' => true];
});

Two-Factor Authentication ​

Setup ​

php
use Nexphant\Auth\TwoFactorAuth;

$app->post('/2fa/enable', function () {
    $user = auth()->user();
    
    $secret = TwoFactorAuth::generateSecret();
    $qrCode = TwoFactorAuth::getQRCode($user->email, $secret);
    
    User::query()
        ->where('id', $user->id)
        ->update(['two_factor_secret' => $secret]);
    
    return [
        'secret' => $secret,
        'qr_code' => $qrCode,
    ];
})->middleware('auth');

Verify ​

php
$app->post('/2fa/verify', function (Request $request) {
    $user = auth()->user();
    $code = $request->json('code');
    
    if (TwoFactorAuth::verify($code, $user->two_factor_secret)) {
        session()->put('2fa_verified', true);
        return ['success' => true];
    }
    
    return ['error' => 'Invalid code'];
})->middleware('auth');

Complete Example ​

php
use Nexphant\App;
use Nexphant\Request;
use Nexphant\Server\Middleware\{SessionMiddleware, AuthMiddleware};
use App\Models\User;

$app = App::create();

// Middleware
$app->middleware(SessionMiddleware::class);
$app->middlewareAlias(['auth' => AuthMiddleware::class]);

// Public routes
$app->post('/register', function (Request $request) {
    $data = $request->json();
    $data['password'] = password_hash($data['password'], PASSWORD_DEFAULT);
    
    $userId = User::query()->insertGetId($data);
    
    session()->regenerate();
    session()->put('user_id', $userId);
    
    return ['success' => true];
});

$app->post('/login', function (Request $request) {
    $credentials = $request->json();
    
    $user = User::query()
        ->where('email', $credentials['email'])
        ->first();
    
    if (!$user || !password_verify($credentials['password'], $user->password)) {
        return ['error' => 'Invalid credentials'];
    }
    
    session()->regenerate();
    session()->put('user_id', $user->id);
    
    return ['success' => true, 'user' => $user];
});

// Protected routes
$app->group(['middleware' => ['auth']], function ($group) {
    $group->get('/profile', function () {
        return ['user' => auth()->user()];
    });
    
    $group->put('/profile', function (Request $request) {
        $data = $request->json();
        
        User::query()
            ->where('id', auth()->id())
            ->update($data);
        
        return ['success' => true];
    });
    
    $group->post('/logout', function () {
        session()->invalidate();
        return ['success' => true];
    });
});

$app->listen(port: 8080);

Security Best Practices ​

  1. Always hash passwords with password_hash()
  2. Regenerate session after login
  3. Use HTTPS in production
  4. Set secure cookies (httpOnly, secure, sameSite)
  5. Implement rate limiting on login
  6. Use CSRF protection
  7. Validate email before registration
  8. Implement password strength requirements
  9. Add account lockout after failed attempts
  10. Log authentication events

Next Steps ​

Released under the MIT License.