laragear / twofactor Goto Github PK
View Code? Open in Web Editor NEWTwo-Factor Authentication for all your users out-of-the-box.
Home Page: https://github.com/sponsors/DarkGhostHunter
License: MIT License
Two-Factor Authentication for all your users out-of-the-box.
Home Page: https://github.com/sponsors/DarkGhostHunter
License: MIT License
8.2.3
MySQL 8
10.1.5
I treid to make it work on Laravel 10 with Breeze installed but it's not working. Is it Laravel 10 compatible?
The docs seem to be for older Laravel version.
For example I am getting this error when I try to add traits as in your docs: Undefined type 'Laragear\TwoFactor\Contracts\TwoFactorAuthenticatable'.intelephense(1009)
Also the implementation with routing and/or breeze etc. is missing in the docs as well.
By the way, is there any demo app or existing app where we can look how it should be implemented? I am especially interested in routes in the final app. But also final controllers.
No response
This would enable support for things like Inertiajs where views aren't really used a such but if we can use a route, it's more flexible. Would be super handy in my current project :)
instead of
$attempt = Auth2FA::attempt($request->only('email', 'password'), $request->filled('remember'))->view('custom2faview');
have
$attempt = Auth2FA::attempt($request->only('email', 'password'), $request->filled('remember'))->route('custom2faroute', [params]);
Since Laravel 10 was released.
N/A
Is it possible to use 6 inputs for entering the 2fa_code ? I tried adding
public function prepareForValidation()
{
$this->merge([
'2fa_code' => $this->code1 . $this->code2 . $this->code3 . $this->code4 . $this->code5 . $this->code6
]);
}
and I do receive the 2fa_code
// 123456
but it doesn't work or gets validated, I would like to know if its possible? Thank you
<!--begin::Section-->
<div class="mb-10">
<!--begin::Label-->
<div class="fw-bold text-start text-dark fs-6 mb-1 ms-1" data-kt-translate="two-step-label">Type your 6 digit security code</div>
<!--end::Label-->
<!--begin::Input group-->
<div class="d-flex flex-wrap flex-stack">
<input type="text" name="code_1" data-inputmask="'mask': '9', 'placeholder': ''" maxlength="1" class="form-control form-control-solid h-60px w-60px fs-2qx text-center border-primary border-hover mx-1 my-2" value="" />
<input type="text" name="code_2" data-inputmask="'mask': '9', 'placeholder': ''" maxlength="1" class="form-control form-control-solid h-60px w-60px fs-2qx text-center border-primary border-hover mx-1 my-2" value="" />
<input type="text" name="code_3" data-inputmask="'mask': '9', 'placeholder': ''" maxlength="1" class="form-control form-control-solid h-60px w-60px fs-2qx text-center border-primary border-hover mx-1 my-2" value="" />
<input type="text" name="code_4" data-inputmask="'mask': '9', 'placeholder': ''" maxlength="1" class="form-control form-control-solid h-60px w-60px fs-2qx text-center border-primary border-hover mx-1 my-2" value="" />
<input type="text" name="code_5" data-inputmask="'mask': '9', 'placeholder': ''" maxlength="1" class="form-control form-control-solid h-60px w-60px fs-2qx text-center border-primary border-hover mx-1 my-2" value="" />
<input type="text" name="code_6" data-inputmask="'mask': '9', 'placeholder': ''" maxlength="1" class="form-control form-control-solid h-60px w-60px fs-2qx text-center border-primary border-hover mx-1 my-2" value="" />
</div>
<!--begin::Input group-->
</div>
<!--end::Section-->
This is NOT a bug report at the moment.
I just upgraded the project I'm working on to L9. As such I had to move from darkghosthunter/laraguard
to laragear/two-factor
. This change required a lot of code change and now I'm not so sure if I took the best path.
My app has no frontend. It's just a backend where all the action happens. All users must be logged in, but not everyone may have 2FA enabled. The 2fa_code
is asked on a dedicated form just after the login one IF the user has 2FA enabled.
So I thought to do this in routes/web.php
:
Route::post('/', 'Auth\LoginController@login')
->name('login.submit')
->middleware('2fa.confirm');
But nope... it won't work that way. At least it didn't here. Users with 2FA enabled are just logged in as soon as they submit their login credentials. No 2FA form page presented as it used to be in darkghosthunter/laraguard
wonderful magic listeners.
After thoroughly reading this package README and many attempts later, the only way I found to accomplish the old behavior was to override L9's Auth\LoginController@login
. I'm not so confident if I got right and what kind of security holes I may have opened. Here's how it looks now:
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use Auth;
use DB;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Laragear\TwoFactor\TwoFactor;
use Symfony\Component\HttpFoundation\Response as SymfonyResponse;
class LoginController extends Controller
{
/**
* Where to redirect users after login.
*
* @var string
*/
protected $redirectTo = RouteServiceProvider::HOME;
/**
* Override AuthenticatesUsers::login() to implement 2FA.
*
* @see AuthenticatesUsers::login() To the original login implementation.
* @see https://github.com/Laragear/TwoFactor#logging-in To the Laragear\TwoFactor login implementation.
*
* @param Request $request
*
* @return JsonResponse|RedirectResponse|Response|SymfonyResponse
*/
public function login(Request $request) {
$credentials = $request->validate([
$this->username() => 'required|string',
'password' => 'required|string',
]);
if (method_exists($this, 'hasTooManyLoginAttempts') &&
$this->hasTooManyLoginAttempts($request)) {
$this->fireLockoutEvent($request);
return $this->sendLockoutResponse($request);
}
$twoFactor = DB::table('two_factor_authentications')->where('label', $credentials['email'])->count();
if ($twoFactor && ! $request->filled(config('app.2fa_field'))) {
return response()->view(
'two-factor::confirm',
[
'credentials' => $credentials,
'remember' => $request->filled('remember'),
]
);
} else {
if (Auth::attemptWhen($credentials, TwoFactor::hasCodeOrFails(), $request->filled('remember'))) {
if ($request->hasSession()) {
$request->session()->put('auth.password_confirmed_at', time());
}
return $this->sendLoginResponse($request);
}
}
$this->incrementLoginAttempts($request);
return $this->sendFailedLoginResponse($request);
}
}
It worked as expected, except that if users type a wrong 2FA code shw will be redirected back to login page (user/password). I can live with that, as it won't make explicit to an attacker if the wrong piece of data was the email, password or the TOTP code.
But at the end, is that right? Can it be improved somehow? Did I broke my auth toy?
PS: The documentation is far from clear when someone needs a custom login method like the case above (TOTP code in it's own form). But as I'm not sure if this is a bug with the middleware not attaching to POST login method, I will leave this as is for now.
Not available yet.
It would be really nice if Route::post()->middleware('2fa.confirm')
just worked as expected (i.e. force a user with 2FA enabled to enter TOTP code on it's own form page, leave users with 2FA disabled alone and just log them in). This would also leave such a critical method as Auth\LoginController@login
alone.
The issue generates no stack trace.
Hey,
This won't be an issue that's structured the way your forced issue templates are set. It's just a quick issue.
Documentation confused me for a good 5 minutes basically.
The repo is set as 2.x as the default branch. So I landed on that branch and this is what happened.
I installed the package as instructed via composer.
Ran the command two-factor:install.
No command in that namespace was found.
I checked the package files in the Vendor namespace, and there was no command so it wasn't an autoload issue.
I checked packagist and then saw that 2.x is not yet stable. (It also said release on the right of the repository I'm just blind).
Also, when I went to switch branches, you had master listed as a branch, so I clicked that. It's stale but, it could confuse others but I jumped to the 1.x branch.
Now, I seem to be implementing your package smoothly after reading the correct documentation 😄
I can submit a PR with a notice in the README where the installation instructions are informing the visitor to visit the other documentation as a fix? I think the correct solution is to use the 1.x branch as the default branch on this repository.
I assume you guys just aren't setting that as the default branch because some of Github's stuff and some tools always default to the default branch which makes things confusing if you guys are mainly spending time on version 2.x
try {
} catch ($exception e) {
throw new Exception();
}
can you support SMS.
no code
No, don't give priority to this
Hi.
Thanks!
Great package, it has everything that Fortify lacks and is much more customizable.
One small feature Im missing is telling if the user is logged in with a real 2-factor authentication challenge or bypassed the auth with a "safe device". It would be optimal if the validate() method could set some session variable when bypassing the two-factor challenge. Or vice versa, some session variable being set only when the user did a real 2-factor auth.
I will have a middleware forcing users to confirm important actions if:
Maybe this feature exists but I've missed it somewhere?
Thanks.
// If safe devices are enabled, and this is a safe device, bypass.
if ($this->isSafeDevicesEnabled() && $user->isSafeDevice($this->request)) {
**ADD**
$this->request->session()->put("bypassed_two_factor", $time);
return true;
}
8.1.6 & Windows (localhost)
mysqlnd 8.1.6
9.43.0
2FA code not requested on next login on same device when safe_devices enabled = true.
2FA code is requested on every login on the same device.
Column safe_devices stays on value null.
No additional browser cookie stored (2 cookies in use: session and csrf)
'safe_devices' => [
'enabled' => true,
'cookie' => '_2fa_remember',
'max_devices' => 3,
'expiration_days' => 14,
],
No response
8.1 - Ubuntu
9.3.4
I want to have all the files (views, configs) from the provider.
When I try to run the vendor publish command i am getting this info:
No publishable resources for tag [migrations].
And I even tried to add the provider to config/app.php
I normally installed the package like it says in the installation:
composer require laragear/two-factor
Then I am trying to publish the files with:
php artisan vendor:publish --provider="Laragear\TwoFactor\TwoFactorServiceProvider" --tag="migrations"
No response
No, don't give priority to this
Most probably I have done something wrong while setting everything up (this project is very early development still).
So for this project, all users are required to enable 2fa; so on my authenticated routes, I have added the 2fa.enabled
middleware. While I am using Laravel Breeze, for now I am mostly using your original views (though I created a 'confirm', 'prepare' and 'recovery' view to also use during the setup of 2fa for a user). I am also using the Facade to handle the login (as per the readme, set in the LoginRequest).
I further copied the "ConfirmtwoFactorCodeController" over to my other controllers, and modified it to also handle the setup of the 2fa for a user, using the following routes:
Route::get('2fa-notice', [\App\Http\Controllers\Frontend\ConfirmTwoFactorCodeController::class, 'notifyTwoFactor'])->name('2fa.notice');
Route::get('2fa-prepare', [\App\Http\Controllers\Frontend\ConfirmTwoFactorCodeController::class, 'prepareTwoFactor'])->name('2fa.prepare');
Route::post('2fa-prepare', [\App\Http\Controllers\Frontend\ConfirmTwoFactorCodeController::class, 'confirmTwoFactor'])->name('2fa.confirm');
Route::get('2fa-recovery', [\App\Http\Controllers\Frontend\ConfirmTwoFactorCodeController::class, 'showRecoveryCodes'])->name('2fa.recovery');
So the setup part is working, and the app code, verifies correctly.
The issue is happening when I attempt to log in. I provide the email and password, and then see the screen asking for the code (weird thing here is it immediately shows the field as having an error, saying the code is either wrong or expired - this is prior to me providing any code). Then when I provide the correct code, it tells me the login was unsuccessful (basically that both the email and password fields are required).
Any idea how I can solve this issue.
8.11 Mac M1
No response
10
$authFA = Auth2FA::attempt($request->only('email', 'password'), $request->filled('remember'));
The MAC is invalid.
8.2 Ubuntu-20.04
MySql 8
10
I try to modify App\Http\Requests\Auth\LoginRequest::authenticate() by replacing Auth with Auth2FA, but it does not work.
I cannot use Auth::user() or Auth2FA::user() to get login user session.
How should I do?
It prompts to ask 2FA form, but it does not work, no matter I enter correct 2fa code or not.
public function authenticate()
{
$this->ensureIsNotRateLimited();
if (!Auth2FA::attempt($this->only('email', 'password'), $this->boolean('remember'))) {
RateLimiter::hit($this->throttleKey());
throw ValidationException::withMessages([
'email' => trans('auth.failed'),
]);
}
RateLimiter::clear($this->throttleKey());
}
// what else I should do to make it work
No response
8.3.6 - Linux Mint 21.3 Cinnamon x64
MySQL 8.0.32
11.7.0
Your package has several events.
When I write an event Listener for those, I expect to be able to run the Listeners in a queue like this:
<?php
namespace App\Listeners;
use App\Notifications\TwoFactorRecoveryCodesGeneratedNotification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Laragear\TwoFactor\Events\TwoFactorRecoveryCodesGenerated;
class TwoFactorRecoveryCodesGeneratedListener implements ShouldQueue
{
use InteractsWithQueue;
public function handle(TwoFactorRecoveryCodesGenerated $event): void
{
// Some logic...
}
}
In the example code, the ShouldQueue
interface and the InteractsWithQueue
trait make the listener execute in a queue.
The problem is that the package events don't use the SerializesModels
trait which leads to the following problem:
Serialization of 'Closure' is not allowed
.
// For reproduction, just make an Event listener for any of the package events.
// The listener must implement `ShouldQueue` and use `InteractsWithQueue`.
// After that dispatch the event and it should result in an exception `Serialization of 'Closure' is not allowed`.
{
"message": "Serialization of 'Closure' is not allowed",
"exception": "Exception",
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Queue/Queue.php",
"line": 160,
"trace": [
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Queue/Queue.php",
"line": 160,
"function": "serialize"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Queue/Queue.php",
"line": 129,
"function": "createObjectPayload",
"class": "Illuminate\\Queue\\Queue",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Queue/RedisQueue.php",
"line": 212,
"function": "createPayloadArray",
"class": "Illuminate\\Queue\\Queue",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/horizon/src/RedisQueue.php",
"line": 86,
"function": "createPayloadArray",
"class": "Illuminate\\Queue\\RedisQueue",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Queue/Queue.php",
"line": 107,
"function": "createPayloadArray",
"class": "Laravel\\Horizon\\RedisQueue",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/horizon/src/RedisQueue.php",
"line": 46,
"function": "createPayload",
"class": "Illuminate\\Queue\\Queue",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Queue/Queue.php",
"line": 59,
"function": "push",
"class": "Laravel\\Horizon\\RedisQueue",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Events/Dispatcher.php",
"line": 633,
"function": "pushOn",
"class": "Illuminate\\Queue\\Queue",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Events/Dispatcher.php",
"line": 552,
"function": "queueHandler",
"class": "Illuminate\\Events\\Dispatcher",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Events/Dispatcher.php",
"line": 478,
"function": "Illuminate\\Events\\{closure}",
"class": "Illuminate\\Events\\Dispatcher",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Events/Dispatcher.php",
"line": 286,
"function": "Illuminate\\Events\\{closure}",
"class": "Illuminate\\Events\\Dispatcher",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Events/Dispatcher.php",
"line": 266,
"function": "invokeListeners",
"class": "Illuminate\\Events\\Dispatcher",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/helpers.php",
"line": 453,
"function": "dispatch",
"class": "Illuminate\\Events\\Dispatcher",
"type": "->"
},
{
"file": "/var/www/html/vendor/laragear/two-factor/src/TwoFactorAuthentication.php",
"line": 205,
"function": "event"
},
{
"file": "/var/www/html/app/Http/Controllers/API/Admin/ProfileController.php",
"line": 136,
"function": "generateRecoveryCodes",
"class": "App\\Models\\User",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php",
"line": 46,
"function": "generateRecoveryCodes",
"class": "App\\Http\\Controllers\\API\\Admin\\ProfileController",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Route.php",
"line": 260,
"function": "dispatch",
"class": "Illuminate\\Routing\\ControllerDispatcher",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Route.php",
"line": 206,
"function": "runController",
"class": "Illuminate\\Routing\\Route",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Router.php",
"line": 806,
"function": "run",
"class": "Illuminate\\Routing\\Route",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 144,
"function": "Illuminate\\Routing\\{closure}",
"class": "Illuminate\\Routing\\Router",
"type": "->"
},
{
"file": "/var/www/html/app/Http/Middleware/Confirm2FAMiddleware.php",
"line": 28,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 183,
"function": "handle",
"class": "App\\Http\\Middleware\\Confirm2FAMiddleware",
"type": "->"
},
{
"file": "/var/www/html/app/Http/Middleware/PermissionMiddleware.php",
"line": 48,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 183,
"function": "handle",
"class": "App\\Http\\Middleware\\PermissionMiddleware",
"type": "->"
},
{
"file": "/var/www/html/vendor/rakutentech/laravel-request-docs/src/LaravelRequestDocsMiddleware.php",
"line": 49,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 183,
"function": "handle",
"class": "Rakutentech\\LaravelRequestDocs\\LaravelRequestDocsMiddleware",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Middleware/SubstituteBindings.php",
"line": 50,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 183,
"function": "handle",
"class": "Illuminate\\Routing\\Middleware\\SubstituteBindings",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Middleware/ThrottleRequests.php",
"line": 161,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Middleware/ThrottleRequests.php",
"line": 127,
"function": "handleRequest",
"class": "Illuminate\\Routing\\Middleware\\ThrottleRequests",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Middleware/ThrottleRequests.php",
"line": 89,
"function": "handleRequestUsingNamedLimiter",
"class": "Illuminate\\Routing\\Middleware\\ThrottleRequests",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 183,
"function": "handle",
"class": "Illuminate\\Routing\\Middleware\\ThrottleRequests",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Auth/Middleware/Authenticate.php",
"line": 64,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 183,
"function": "handle",
"class": "Illuminate\\Auth\\Middleware\\Authenticate",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/sanctum/src/Http/Middleware/EnsureFrontendRequestsAreStateful.php",
"line": 25,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 144,
"function": "Laravel\\Sanctum\\Http\\Middleware\\{closure}",
"class": "Laravel\\Sanctum\\Http\\Middleware\\EnsureFrontendRequestsAreStateful",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 119,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/sanctum/src/Http/Middleware/EnsureFrontendRequestsAreStateful.php",
"line": 24,
"function": "then",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 183,
"function": "handle",
"class": "Laravel\\Sanctum\\Http\\Middleware\\EnsureFrontendRequestsAreStateful",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 119,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Router.php",
"line": 805,
"function": "then",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Router.php",
"line": 784,
"function": "runRouteWithinStack",
"class": "Illuminate\\Routing\\Router",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Router.php",
"line": 748,
"function": "runRoute",
"class": "Illuminate\\Routing\\Router",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Router.php",
"line": 737,
"function": "dispatchToRoute",
"class": "Illuminate\\Routing\\Router",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php",
"line": 200,
"function": "dispatch",
"class": "Illuminate\\Routing\\Router",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 144,
"function": "Illuminate\\Foundation\\Http\\{closure}",
"class": "Illuminate\\Foundation\\Http\\Kernel",
"type": "->"
},
{
"file": "/var/www/html/vendor/barryvdh/laravel-debugbar/src/Middleware/InjectDebugbar.php",
"line": 59,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 183,
"function": "handle",
"class": "Barryvdh\\Debugbar\\Middleware\\InjectDebugbar",
"type": "->"
},
{
"file": "/var/www/html/app/Http/Middleware/LocalizationMiddleware.php",
"line": 44,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 183,
"function": "handle",
"class": "App\\Http\\Middleware\\LocalizationMiddleware",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php",
"line": 121,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php",
"line": 64,
"function": "handleStatefulRequest",
"class": "Illuminate\\Session\\Middleware\\StartSession",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 183,
"function": "handle",
"class": "Illuminate\\Session\\Middleware\\StartSession",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php",
"line": 21,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ConvertEmptyStringsToNull.php",
"line": 31,
"function": "handle",
"class": "Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 183,
"function": "handle",
"class": "Illuminate\\Foundation\\Http\\Middleware\\ConvertEmptyStringsToNull",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php",
"line": 21,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TrimStrings.php",
"line": 51,
"function": "handle",
"class": "Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 183,
"function": "handle",
"class": "Illuminate\\Foundation\\Http\\Middleware\\TrimStrings",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Http/Middleware/ValidatePostSize.php",
"line": 27,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 183,
"function": "handle",
"class": "Illuminate\\Http\\Middleware\\ValidatePostSize",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/PreventRequestsDuringMaintenance.php",
"line": 110,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 183,
"function": "handle",
"class": "Illuminate\\Foundation\\Http\\Middleware\\PreventRequestsDuringMaintenance",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Http/Middleware/HandleCors.php",
"line": 62,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 183,
"function": "handle",
"class": "Illuminate\\Http\\Middleware\\HandleCors",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php",
"line": 119,
"function": "Illuminate\\Pipeline\\{closure}",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php",
"line": 175,
"function": "then",
"class": "Illuminate\\Pipeline\\Pipeline",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php",
"line": 144,
"function": "sendRequestThroughRouter",
"class": "Illuminate\\Foundation\\Http\\Kernel",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/Application.php",
"line": 1172,
"function": "handle",
"class": "Illuminate\\Foundation\\Http\\Kernel",
"type": "->"
},
{
"file": "/var/www/html/public/index.php",
"line": 19,
"function": "handleRequest",
"class": "Illuminate\\Foundation\\Application",
"type": "->"
},
{
"file": "/var/www/html/vendor/laravel/framework/src/Illuminate/Foundation/resources/server.php",
"line": 16,
"function": "require_once"
}
]
}
8.3 MacOS Sonoma 14.4.4
MySQL 8.0.33
10.43.0
When I run the migrations I expect it to add all the 2fa table without erroring
After running the migrations command I get this MySQL Error:
SQLSTATE[42000]: Syntax error or access violation: 1059 Identifier name 'two_factor_authentications_authenticatable_type_authenticatable_id_index' is too long (Connection: mysql, SQL: alter table two_factor_authentications
add index two_factor_authentications_authenticatable_type_authenticatable_id_index
(authenticatable_type
, authenticatable_id
))
php artisan two-factor:install
php artisan migrate
No response
8.1.3
9.5.1
The migration included in this package should be optional.
I'm migrating from the old Laraguard package. I already have a database table called two_factor_authentications
, from a migration that has a slightly different name than the migration included with this package.
This causes a Table 'two_factor_authentications' already exists
error when running php artisan migrate
, where the migration included in this package is called because it is not listed in my migrations table.
As far as I can see I cannot cancel this migration.
Schema::create('two_factor_authentications', function (Blueprint $table) {
$table->bigIncrements('id');
});
then run php artisan migrate
No response
No, don't give priority to this
How can I get the secret for use outside the Laravel application? I have a Node application the interfaces with this application. When passing in shared_secret to verify the 2fa code they gave it always returns false. So how can I derive the secret from the shared_secret?
For example notp requires the secret key for the user
Just wanted to clarify that currently there is an internal logic that will disallow same 2FA code from being used twice?
Is there a way to disable this or this is part of the requirement for TOTP?
My use case:
This post was meant as a clarification
I've installed the package following the readme.md. I still don't understand how to turn the login process into a two-step process for users who have enabled 2fa. As I've read in the instructions I should add a field to the login form for the 2fa_code, and this works. Users without 2fa enabled are logged in without entering anything, and users who do have one must enter a code.
However, I would prefer the user to first enter a valid username/password and then be asked for the 2fa_code. It would be great if the readme was a bit more explanatory on implementing the login routine. Alternatively, an example package that implements the functionality would be valuable.
Thanks for your great work!
- i have no idea on how to write that code.
No, don't give priority to this
Podrían considerar actualizar el paquete laragear/two-factor para ser compatible con la versión 3.0.0 de bacon/bacon-qr-code. Actualmente, laragear/two-factor requiere la versión 2.x de bacon/bacon-qr-code, lo cual genera incompatibilidades con otras dependencias en proyectos que ya utilizan la versión 3.0.0.
Esta actualización ayudaría a evitar conflictos y permitiría una mayor flexibilidad al integrarlo en proyectos existentes.
./composer.json has been updated
Running composer update laragear/two-factor
Loading composer repositories with package information
Updating dependencies
Your requirements could not be resolved to an installable set of packages.
Problem 1
- laragear/two-factor[v1.0.0, ..., v1.2.0] require bacon/bacon-qr-code ^2.0 -> found bacon/bacon-qr-code[2.0.0, ..., 2.0.8] but the package is fixed to v3.0.0 (lock file version) by a partial update and that version does not match. Make sure you list it as an argument for the update command.
- laragear/two-factor[v1.2.1, ..., v1.3.0, v2.0.0, ..., v2.0.1] require bacon/bacon-qr-code 2.* -> found bacon/bacon-qr-code[2.0.0, ..., 2.0.8] but the package is fixed to v3.0.0 (lock file version) by a partial update and that version does not match. Make sure you list it as an argument for the update command.
- Root composer.json requires laragear/two-factor * -> satisfiable by laragear/two-factor[v1.0.0, ..., v1.3.0, v2.0.0, v2.0.1].
Use the option --with-all-dependencies (-W) to allow upgrades, downgrades and removals for packages currently locked to specific versions.
You can also try re-running composer require with an explicit version constraint, e.g. "composer require laragear/two-factor:*" to figure out if any version is installable, or "composer require laragear/two-factor:^2.1" if you know which you need.
Installation failed, reverting ./composer.json and ./composer.lock to their original content.
PS /home/carlos-daniel/Herd/laravel/test-jetstream>
8.1.7 - Fedora 35 x64
9.17.0
When a user with 2FA enabled logs in and confirms their TOTP, they should be redirected to the application as authenticated.
The user is redirected back to the login page with an error stating that their credentials are invalid. This seems to be caused by the session flash not being set. Looking into TwoFactorLoginHelper.php
I identified that Illuminate\Contracts\Session\Session
does not have flash()
as a member function https://laravel.com/api/9.x/Illuminate/Contracts/Session/Session.html
protected function flashData(array $credentials, bool $remember): void
{
foreach ($credentials as $key => $value) {
$credentials[$key] = Crypt::encryptString($value);
}
$this->session->flash($this->sessionKey, ['credentials' => $credentials, 'remember' => $remember]);
}
As a result, when the TOTP form is submitted the credentials are blank. And since validation is overridden due to 2fa_code
being present, the app kicks back a credentials not found error.
You may want to use $this->session->put(...)
and $this->session->forget(...)
in combination with flashData()
and getFlashedData()
to approximate flash()
or instead use the $request->session()
which does have flash()
.
// https://github.com/Laragear/TwoFactor
No response
No, don't give priority to this
8.2.4
No response
10.13.5
Expected result
otpauth://totp/Laravel:[email protected]?secret=THISISMYSECRETPLEASEDONOTSHAREIT&issuer=Laravel&label=taylor%40laravel.com&algorithm=SHA1&digits=6&period=30
Result without setting the issuer config variable
otpauth://totp/%3A:[email protected]?label=%3Ataylor%40laravel.com&secret=THISISMYSECRETPLEASEDONOTSHAREIT&algorithm=SHA1&digits=6
Result with setting the issuer config variable
otpauth://totp/Name%3AName:[email protected]?issuer=Name&label=Name%3Ataylor%40laravel.com&secret=THISISMYSECRETPLEASEDONOTSHAREIT&algorithm=SHA1&digits=6
$secret = $user->createTwoFactorAuth();
No response
8.1.17
Mysql 8.0.32
10
It should be able to create createTwoFactorAuth
for logged in user.
I see that the secret key are stored differently in two version. Did I miss any documentation to migrate properly?
So I updated laravel from 8 to 10, and also have migrated from
darkghosthunter/laraguard => Laragear/TwoFactor.
So there is no any issue with doing new setup, but for the one which was setup from old version it gives payload issue. Please check the attachment.
$secret = $this->loggedInUser->createTwoFactorAuth();
No response
8.1.6 Windows
mysqlnd 8.1.6
9.43.0
No warnings should be issued
When using this line:
$attempt = Auth2FA::attempt($request->only('email', 'password'), $request->filled('remember'));
like in the documentation VSCode intelephense marks it as: "Non static method 'attempt' should not be called statically."
/**
* Handle an authentication attempt.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function authenticate(Request $request)
{
// If the user is trying for the first time, ensure both email and the password are
// required to log in. If it's not, then he would issue its 2FA code. This ensures
// the credentials are not required again when is just issuing his 2FA code alone.
if ($request->isNotFilled('2fa_code')) {
$request->validate([
'email' => 'required|email',
'password' => 'required|string'
]);
}
$attempt = Auth2FA::guard('admin')->attempt($request->only('email', 'password'), $request->filled('remember'));
if ($attempt) {
return redirect()->intended('/');
}
return back()->withErrors([
'email' => 'The provided credentials do not match our records.',
])->onlyInput('email');
}
No response
8.2.9 - MacOSX
PostgreSQL 15
10.21.0
I expect to be able to use uuids as the authenticable id
My users table uses uuids ``
I've updated the 2fa migration to use $table->uuidMorphs('authenticatable', '2fa_auth_type_auth_id_index');
$twoFactorModel->authenticable
throws an error because of the 'authenticatable_id' => 'int',
attempting to cast the string uuid to a int.
// user migration
$table->uuid('userId')->primary()->unique();
// two-factor migration
$table->uuidMorphs('authenticatable', '2fa_auth_type_auth_id_index');
// attempt to retrive the user from the 2fa model - results in an error because uuid is cast to int e.g.
// "97d08fcf-6639-4e66-a301-d325e7f7435b" -> 97
$twoFactorModel->authenticable
// Error Message
// PDOException: SQLSTATE[22P02]: Invalid text representation: 7 ERROR: invalid input syntax for type uuid: "97"
No response
8.1 - Ubuntu 22.04
No response
10.x
Hi,
I found 2 bugs during testing on an Apple device (IPhone 6S, Ios 15.7.1)
When using generating the QR code
\Laragear\TwoFactor\Models\Concerns\SerializesSharedSecret::toQr
It uses \Laragear\TwoFactor\Models\Concerns\SerializesSharedSecret::toUri
to build the otpauth uri.
It returns the url like this:
return 'otpauth://totp/'.rawurlencode($issuer).'%3A'.$this->attributes['label']."?$query";
The $this->attributes['label']
is not urlencoded like the issuer and this causes an issue when testing generating a QR code on an Apple device.
Instead of 'App name:[email protected]' the user was rendered as 'App%20name:[email protected]'.
There's a second issue to this, of which I think also is unwanted behavior, it by defaults creates a label based on the issuer + the e-mail of the user. https://github.com/Laragear/TwoFactor/blob/v1.2.2/src/TwoFactorAuthentication.php#L106
But the specs (think these are the official specs, could not find any other)
State:
otpauth://TYPE/LABEL?PARAMETERS
Instead of just passing on the label or using label + email, the module combines label + (label + email), which includes the label unnecessary twice. So I think the default label twoFactorLabel()
should not include the issuer by default and think it would be a good practice to always include it in the otpauth url.
Can't find label as an official parameter for otpauth://TYPE/LABEL?PARAMETERS either, although the module adds it to the query parameters, I don't think it's used.
rawurlencode($issuer . ':' . $this->attributes['label'])
.I've mitigated this issue by overriding the following method on the model
protected function twoFactorLabel(): string
* Set APP name with space like: 'The Application'
* Create TwoFactorAuthentication model
* Create QR code for the model
* Scan the code
* View result on a IOS device
No response
8.1 ubuntu 22
mysql 8
10.*
hasTwoFactorEnabled function not work because enabled_at still null
hasTwoFactorEnabled function not work because enabled_at still null
hasTwoFactorEnabled function not work because enabled_at still null
No response
8.0.12
9.17.0
When totp code is correct i expect the user to be logged in.
Users not using 2fa gets logged inn correctly. Users who has activated 2fa are getting the login page again with no error message.
//To activate sanctum for api calls (SPA authentication) i uncommented EnsureFrontendRequestsAreStateful in app/http/Kernel.php. That is when this "bug" sets in.
'api' => [
\Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
'throttle:api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
no stacktrace and nothing logged in laravel.log
No, don't give priority to this
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.