• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

daycry / auth / 22527357078

28 Feb 2026 07:22PM UTC coverage: 63.267% (+0.7%) from 62.568%
22527357078

push

github

daycry
Remove PHP 8.1 from PHPUnit CI matrix

Update .github/workflows/phpunit.yml to drop PHP 8.1 from the test matrix. CI will now run PHPUnit only on PHP 8.2 and 8.3, reducing the matrix to current supported versions.

3064 of 4843 relevant lines covered (63.27%)

41.52 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/Controllers/UserSecurityController.php
1
<?php
2

3
declare(strict_types=1);
4

5
/**
6
 * This file is part of Daycry Auth.
7
 *
8
 * (c) Daycry <daycry9@proton.me>
9
 *
10
 * For the full copyright and license information, please view
11
 * the LICENSE file that was distributed with this source code.
12
 */
13

14
namespace Daycry\Auth\Controllers;
15

16
use CodeIgniter\HTTP\RedirectResponse;
17
use Daycry\Auth\Libraries\TOTP;
18
use Daycry\Auth\Models\DeviceSessionModel;
19

20
/**
21
 * UserSecurityController
22
 *
23
 * Lets authenticated users manage their own security settings:
24
 *   - Active device sessions (list + revoke)
25
 *   - TOTP 2FA setup / disable
26
 *
27
 * Routes to add in your app (example):
28
 *   $routes->group('account/security', ['filter' => 'session'], static function ($routes): void {
29
 *       $routes->get('/',                  'Daycry\Auth\Controllers\UserSecurityController::index',             ['as' => 'security']);
30
 *       $routes->post('sessions/revoke',   'Daycry\Auth\Controllers\UserSecurityController::revokeSession',     ['as' => 'security-revoke-session']);
31
 *       $routes->post('sessions/revoke-all','Daycry\Auth\Controllers\UserSecurityController::revokeAllSessions',['as' => 'security-revoke-all']);
32
 *       $routes->get('totp/setup',         'Daycry\Auth\Controllers\UserSecurityController::totpSetup',        ['as' => 'totp-setup']);
33
 *       $routes->post('totp/setup/confirm','Daycry\Auth\Controllers\UserSecurityController::totpSetupConfirm', ['as' => 'totp-setup-confirm']);
34
 *       $routes->post('totp/disable',      'Daycry\Auth\Controllers\UserSecurityController::totpDisable',      ['as' => 'totp-disable']);
35
 *       $routes->post('sessions/password', 'Daycry\Auth\Controllers\UserSecurityController::changePassword',   ['as' => 'security-change-password']);
36
 *       $routes->get('email/change',       'Daycry\Auth\Controllers\UserSecurityController::changeEmailView',  ['as' => 'security-change-email']);
37
 *       $routes->post('email/change',      'Daycry\Auth\Controllers\UserSecurityController::changeEmail',      ['as' => 'security-change-email-action']);
38
 *       $routes->get('email/confirm',      'Daycry\Auth\Controllers\UserSecurityController::confirmEmailChange',['as' => 'security-confirm-email']);
39
 *       $routes->post('oauth/unlink',      'Daycry\Auth\Controllers\UserSecurityController::unlinkOauth',      ['as' => 'security-unlink-oauth']);
40
 *   });
41
 */
42
class UserSecurityController extends BaseAuthController
43
{
44
    protected function getValidationRules(): array
×
45
    {
46
        return [];
×
47
    }
48

49
    /**
50
     * Security overview: device sessions + TOTP status.
51
     */
52
    public function index(): string
×
53
    {
54
        $user = auth()->user();
×
55

56
        /** @var DeviceSessionModel $deviceModel */
57
        $deviceModel = model(DeviceSessionModel::class);
×
58

59
        $sessions   = $deviceModel->getActiveForUser($user);
×
60
        $currentSid = session_id();
×
61

62
        return $this->view('Daycry\Auth\Views\profile\security', [
×
63
            'sessions'    => $sessions,
×
64
            'currentSid'  => $currentSid,
×
65
            'totpEnabled' => $user->hasTotpEnabled(),
×
66
        ]);
×
67
    }
68

69
    /**
70
     * Revoke a single device session.
71
     */
72
    public function revokeSession(): RedirectResponse
×
73
    {
74
        $sessionId = (string) $this->request->getPost('session_id');
×
75

76
        if ($sessionId === '') {
×
77
            return redirect()->back()->with('error', 'Invalid session.');
×
78
        }
79

80
        $user = auth()->user();
×
81

82
        /** @var DeviceSessionModel $deviceModel */
83
        $deviceModel = model(DeviceSessionModel::class);
×
84

85
        // Safety check: ensure the session belongs to the current user
86
        $session = $deviceModel->findBySessionId($sessionId);
×
87

88
        if ($session === null || $session->user_id !== $user->id) {
×
89
            return redirect()->back()->with('error', 'Session not found.');
×
90
        }
91

92
        $deviceModel->terminateSession($sessionId);
×
93

94
        return redirect()->back()->with('message', 'Session revoked successfully.');
×
95
    }
96

97
    /**
98
     * Revoke all other active sessions (keep current one).
99
     */
100
    public function revokeAllSessions(): RedirectResponse
×
101
    {
102
        $user = auth()->user();
×
103

104
        /** @var DeviceSessionModel $deviceModel */
105
        $deviceModel = model(DeviceSessionModel::class);
×
106
        $deviceModel->terminateAllForUser($user, session_id());
×
107

108
        return redirect()->back()->with('message', 'All other sessions have been revoked.');
×
109
    }
110

111
    /**
112
     * Show the TOTP setup page with QR code.
113
     */
114
    public function totpSetup(): RedirectResponse|string
×
115
    {
116
        $user = auth()->user();
×
117

118
        if ($user->hasTotpEnabled()) {
×
119
            return redirect()->route('security')->with('error', 'TOTP is already enabled.');
×
120
        }
121

122
        $otpAuthUrl = $user->enableTotp();
×
123
        $secret     = $user->getTotpSecret();
×
124

125
        return $this->view('Daycry\Auth\Views\totp_setup_show', [
×
126
            'otpAuthUrl' => $otpAuthUrl,
×
127
            'secret'     => $secret ?? '',
×
128
            'qrCodeUrl'  => TOTP::getQRCodeUrl($otpAuthUrl),
×
129
            'confirmUrl' => url_to('totp-setup-confirm'),
×
130
        ]);
×
131
    }
132

133
    /**
134
     * Confirm TOTP setup by verifying the first code.
135
     */
136
    public function totpSetupConfirm(): RedirectResponse|string
×
137
    {
138
        $user = auth()->user();
×
139
        $code = (string) $this->request->getPost('token');
×
140

141
        if (! $user->verifyTotpCode($code)) {
×
142
            // Remove the just-generated secret so setup can restart cleanly
143
            $user->disableTotp();
×
144

145
            return redirect()->route('totp-setup')
×
146
                ->with('error', lang('Auth.totpSetupInvalidCode'));
×
147
        }
148

149
        return $this->view('Daycry\Auth\Views\totp_setup_success', [
×
150
            'redirectUrl' => url_to('security'),
×
151
        ]);
×
152
    }
153

154
    /**
155
     * Disable TOTP for the current user.
156
     */
157
    public function totpDisable(): RedirectResponse
×
158
    {
159
        $code = (string) $this->request->getPost('token');
×
160
        $user = auth()->user();
×
161

162
        if (! $user->verifyTotpCode($code)) {
×
163
            return redirect()->back()->with('error', lang('Auth.invalidTotpToken'));
×
164
        }
165

166
        $user->disableTotp();
×
167

168
        return redirect()->route('security')->with('message', 'Two-factor authentication has been disabled.');
×
169
    }
170
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc