{"id":13405052,"url":"https://github.com/Laragear/TwoFactor","last_synced_at":"2025-03-14T09:32:08.551Z","repository":{"id":37622562,"uuid":"458662412","full_name":"Laragear/TwoFactor","owner":"Laragear","description":"Two-Factor Authentication for all your users out-of-the-box.","archived":false,"fork":false,"pushed_at":"2024-07-23T00:54:59.000Z","size":342,"stargazers_count":264,"open_issues_count":0,"forks_count":20,"subscribers_count":2,"default_branch":"2.x","last_synced_at":"2024-09-30T21:06:06.796Z","etag":null,"topics":["2fa","2factor","authentication","laravel","php","two-factor","two-factor-authentication"],"latest_commit_sha":null,"homepage":"https://github.com/sponsors/DarkGhostHunter","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Laragear.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"DarkGhostHunter","custom":"https://paypal.me/darkghosthunter"}},"created_at":"2022-02-12T23:19:37.000Z","updated_at":"2024-09-22T08:55:43.000Z","dependencies_parsed_at":"2024-05-22T04:32:09.116Z","dependency_job_id":"8fd9deca-279e-49cd-bb60-8099620266da","html_url":"https://github.com/Laragear/TwoFactor","commit_stats":null,"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Laragear%2FTwoFactor","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Laragear%2FTwoFactor/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Laragear%2FTwoFactor/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Laragear%2FTwoFactor/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Laragear","download_url":"https://codeload.github.com/Laragear/TwoFactor/tar.gz/refs/heads/2.x","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243554146,"owners_count":20309871,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["2fa","2factor","authentication","laravel","php","two-factor","two-factor-authentication"],"created_at":"2024-07-30T19:01:55.072Z","updated_at":"2025-03-14T09:32:08.151Z","avatar_url":"https://github.com/Laragear.png","language":"PHP","readme":"# Two Factor\n[![Latest Version on Packagist](https://img.shields.io/packagist/v/laragear/two-factor.svg)](https://packagist.org/packages/laragear/two-factor)\n[![Latest stable test run](https://github.com/Laragear/TwoFactor/workflows/Tests/badge.svg)](https://github.com/Laragear/TwoFactor/actions)\n[![Codecov coverage](https://codecov.io/gh/Laragear/TwoFactor/branch/1.x/graph/badge.svg?token=BJMBVZNPM8)](https://codecov.io/gh/Laragear/TwoFactor)\n[![Maintainability](https://api.codeclimate.com/v1/badges/64241e25adb0f55d7ba1/maintainability)](https://codeclimate.com/github/Laragear/TwoFactor/maintainability)\n[![Sonarcloud Status](https://sonarcloud.io/api/project_badges/measure?project=Laragear_TwoFactor\u0026metric=alert_status)](https://sonarcloud.io/dashboard?id=Laragear_TwoFactor)\n[![Laravel Octane Compatibility](https://img.shields.io/badge/Laravel%20Octane-Compatible-success?style=flat\u0026logo=laravel)](https://laravel.com/docs/11.x/octane#introduction)\n\nOn-premises Two-Factor Authentication for all your users out of the box.\n\n```php\nuse Illuminate\\Http\\Request;\nuse Laragear\\TwoFactor\\Facades\\Auth2FA;\n\npublic function login(Request $request)\n{\n    $attempt = Auth2FA::attempt($request-\u003eonly('email', 'password'));\n    \n    if ($attempt) {\n        return 'You are logged in!';\n    }\n    \n    return 'Hey, you should make an account!';\n}\n```\n\nThis package enables TOTP authentication using 6 digits codes. No need for external APIs.\n\n\u003e [!TIP]\n\u003e\n\u003e Want to authenticate users with Passkeys? Check out [Laragear WebAuthn](https://github.com/Laragear/WebAuthn).\n\n## Become a sponsor\n\n[![](.github/assets/support.png)](https://github.com/sponsors/DarkGhostHunter)\n\nYour support allows me to keep this package free, up-to-date and maintainable. Alternatively, you can **[spread the word!](http://twitter.com/share?text=I%20am%20using%20this%20cool%20PHP%20package\u0026url=https://github.com%2FLaragear%2FTwoFactor\u0026hashtags=PHP,Laravel)**\n\n## Requirements\n\n* Laravel 10 or later\n\n## Installation\n\nFire up Composer and require this package in your project.\n\n    composer require laragear/two-factor\n\nThat's it.\n\n### How this works\n\nThis package adds a **Contract** to detect if a user, after the credentials are deemed valid, should use Two-Factor Authentication as a second layer of authentication.\n\nIt includes a custom **view** and a **helper** to handle the Two-Factor authentication itself during login attempts.\n\nWorks without middleware or new guards, but you can go full manual if you want.\n\n## Set up\n\n1. First, install the migration, translations, views and config into your application, with the `two-factor:install` Artisan command.\n\n```shell\nphp artisan two-factor:install\n```\n\n\u003e [!TIP]\n\u003e\n\u003e You can [edit the migration](MIGRATIONS.md) by adding new columns before migrating, and also change the [table name](MIGRATIONS.md#custom-table-name).\n\nAfter that, you may migrate your table like always through the Artisan command.\n\n```shell\nphp artisan migrate\n```\n\n2. Add the `TwoFactorAuthenticatable` _contract_ and the `TwoFactorAuthentication` trait to the User model, or any other model you want to make Two-Factor Authentication available. \n\n```php\n\u003c?php\n\nnamespace App;\n\nuse Illuminate\\Foundation\\Auth\\User as Authenticatable;\nuse Laragear\\TwoFactor\\TwoFactorAuthentication;\nuse Laragear\\TwoFactor\\Contracts\\TwoFactorAuthenticatable;\n\nclass User extends Authenticatable implements TwoFactorAuthenticatable\n{\n    use TwoFactorAuthentication;\n    \n    // ...\n}\n```\n\n\u003e [!TIP]\n\u003e\n\u003e The contract is used to identify the model using Two-Factor Authentication, while the trait conveniently implements the methods required to handle it.\n\nThat's it. You're now ready to use 2FA in your application.\n\n### Enabling Two-Factor Authentication\n\nTo enable Two-Factor Authentication for the User, he must sync the Shared Secret between its Authenticator app and the application. \n\n\u003e [!TIP]\n\u003e\n\u003e Free Authenticator Apps, in no particular order, are [iOS Authenticator](https://www.apple.com/ios/), [FreeOTP](https://freeotp.github.io/), [Authy](https://authy.com/), [2FAS](https://2fas.com/), [2Stable Authenticator](https://authenticator.2stable.com/), [Step-two](https://steptwo.app/), [BinaryRoot Authenticator](https://www.binaryboot.com/totp-authenticator), [Google](https://apps.apple.com/app/google-authenticator/id388497605) [Authenticator](https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2\u0026hl=en), and [Microsoft Authenticator](https://www.microsoft.com/en-us/account/authenticator), to name a few.\n\nTo start, generate the needed data using the `createTwoFactorAuth()` method. This returns a serializable _Shared Secret_ that you can show to the User as a string or QR Code (encoded as SVG) in your view.\n\n```php\nuse Illuminate\\Http\\Request;\n\npublic function prepareTwoFactor(Request $request)\n{\n    $secret = $request-\u003euser()-\u003ecreateTwoFactorAuth();\n    \n    return view('user.2fa', [\n        'qr_code' =\u003e $secret-\u003etoQr(),     // As QR Code\n        'uri'     =\u003e $secret-\u003etoUri(),    // As \"otpauth://\" URI.\n        'string'  =\u003e $secret-\u003etoString(), // As a string\n    ]);\n}\n```\n\n\u003e [!TIP]\n\u003e \n\u003e When you use `createTwoFactorAuth()` on someone with Two-Factor Authentication already enabled, the previous data becomes permanently invalid. This ensures a User **never** has two Shared Secrets enabled at any given time.\n\nThen, the User must confirm the Shared Secret with a Code generated by their Authenticator app. The `confirmTwoFactorAuth()` method will automatically enable it if the code is valid.\n\n```php\nuse Illuminate\\Http\\Request;\n\npublic function confirmTwoFactor(Request $request)\n{\n    $request-\u003evalidate([\n        'code' =\u003e 'required|numeric'\n    ]);\n    \n    $activated = $request-\u003euser()-\u003econfirmTwoFactorAuth($request-\u003ecode);\n    \n    // ...\n}\n```\n\nIf the User doesn't issue the correct Code, the method will return `false`. You can tell the User to double-check its device's timezone, or create another Shared Secret with `createTwoFactorAuth()`.\n\n### Recovery Codes\n\nRecovery Codes are automatically generated each time the Two-Factor Authentication is enabled. By default, a Collection of ten one-use 8-characters codes are created.\n\nYou can show them using `getRecoveryCodes()`.\n\n```php\nuse Illuminate\\Http\\Request;\n\npublic function confirmTwoFactor(Request $request)\n{\n    if ($request-\u003euser()-\u003econfirmTwoFactorAuth($request-\u003ecode)) {\n        return $request-\u003euser()-\u003egetRecoveryCodes();\n    }\n    \n    return 'Try again!';\n}\n```\n\nYou're free on how to show these codes to the User, but **ensure** you show them at least one time after a successfully enabling Two-Factor Authentication, and ask him to print them somewhere.\n\n\u003e [!TIP]\n\u003e\n\u003e These Recovery Codes are handled automatically when the User sends it instead of a TOTP code. If it's a recovery code, the package will use and mark it as invalid, so it can't be used again.\n\nThe User can generate a fresh batch of codes using `generateRecoveryCodes()`, which replaces the previous batch.\n\n```php\nuse Illuminate\\Http\\Request;\n\npublic function showRecoveryCodes(Request $request)\n{\n    return $request-\u003euser()-\u003egenerateRecoveryCodes();\n}\n```\n\n\u003e [!IMPORTANT]\n\u003e\n\u003e If the User depletes his recovery codes without disabling Two-Factor Authentication, or Recovery Codes are deactivated, **he may be locked out forever without his Authenticator app**. Ensure you have countermeasures in these cases, like recovery emails.\n\n#### Custom Recovery Codes\n\nWhile it's not recommended, as the included logic will suffice for the vast majority of situations, you can create your own generator for recovery codes. Just add a callback using the  `generateRecoveryCodesUsing()` of the `TwoFactorAuthentication` model.\n\nThis method receives a callback that should return a random alphanumeric code, and will be invoked on each code to generate.\n\n```php\nuse Laragear\\TwoFactor\\Models\\TwoFactorAuthentication;\nuse MyRandomGenerator;\n\n$generator = function ($length, $iteration, $amount) {\n    return MyRandomGenerator::random($length)-\u003emake();\n}\n\nTwoFactorAuthentication::generateRecoveryCodesUsing($generator);\n```\n\n### Logging in\n\nThe easiest way to login users in your application is to use the `Auth2FA` facade. It comes with everything you would need to handle a user that requires a 2FA Code:\n\n- Only works if the user has 2FA enabled.\n- Shows a custom form if the 2FA code is required.\n- Credentials are encrypted and flashed in the session to re-use them. \n\nIn your Login Controller, use the `Auth2FA::attempt()` method with the credentials. If the user requires a 2FA Code, it will automatically stop the authentication and show a form to use it.\n\nYou can **blatantly copy-and-paste this code** in your log in controller:\n\n```php\nuse Laragear\\TwoFactor\\Facades\\Auth2FA;\nuse Illuminate\\Http\\Request;\n\npublic function login(Request $request)\n{\n    // If the user is trying for the first time, ensure both email and the password are\n    // required to log in. If it's not, then he would issue its 2FA code. This ensures\n    // the credentials are not required again when is just issuing his 2FA code alone.\n    if ($request-\u003eisNotFilled('2fa_code')) {\n        $request-\u003evalidate([\n            'email' =\u003e 'required|email',\n            'password' =\u003e 'required|string'\n        ]);\n    }\n    \n    $attempt = Auth2FA::attempt($request-\u003eonly('email', 'password'), $request-\u003efilled('remember'));\n    \n    if ($attempt) {\n        return redirect()-\u003ehome();\n    }\n    \n    return back()-\u003ewithErrors(['email' =\u003e 'There is no existing user for these credentials']);\n}\n```\n\nYou can further customize how to handle the 2FA code authentication procedure with the following fluent methods:\n\n| Method            | Description                                                                       |\n|-------------------|-----------------------------------------------------------------------------------|\n| guard($guard)     | The guard to use for authentication. Defaults to the application default (`web`). |\n| view($view)       | Return a custom view to handle the 2FA Code retry.                                |\n| redirect($route)  | Redirect to a location to handle the 2FA Code retry.                              |\n| message($message) | Return a custom message when the 2FA code fails or is not present.                |\n| input($input)     | Sets the input where the TOTP code is in the request. Defaults to `2fa_code`.     |\n| sessionKey($key)  | The key used to flash the encrypted credentials. Defaults to `_2fa_login`.        |\n\nFor example, we can change the message to show and the input to use from the login form.\n\n```php\nuse Laragear\\TwoFactor\\Facades\\Auth2FA;\n\nAuth2FA::message('You need 2FA set up to access this area')\n    -\u003eredirect('/auth/2fa-required')\n    -\u003einput('2fa-code')\n    -\u003eattempt($request-\u003eonly('email', 'password'), $request-\u003efilled('remember'));\n```\n\n\n\u003e [!TIP]\n\u003e \n\u003e * For [Laravel UI](https://github.com/laravel/ui), override the `attemptLogin()` method to replace the default guard attempt with `Auth2FA::attempt()` and `validateLogin` method to wrap in the `if ($request-\u003eisNotFilled('2fa_code'))` statement in your Login controller.\n\u003e * For [Laravel Breeze](https://laravel.com/docs/starter-kits#laravel-breeze), you may need to extend the `LoginRequest::authenticate()` call.\n\u003e * For [Laravel Fortify](https://laravel.com/docs/fortify) and [Jetstream](https://jetstream.laravel.com/), you may need to set a custom callback with the [`Fortify::authenticateUsing()`](https://laravel.com/docs/11.x/fortify#customizing-user-authentication) method.\n\nAlternatively, you may use `Auth::attemptWhen()` with TwoFactor helper methods, which returns a callback to check if the user needs a 2FA Code before proceeding using `TwoFactor::hasCode()`.\n\n```php\nuse Illuminate\\Support\\Facades\\Auth;\nuse Laragear\\TwoFactor\\TwoFactor;\n\n$attempt = Auth::attemptWhen(\n    [/* Credentials... */], TwoFactor::hasCode(), $request-\u003efilled('remember')\n);\n```\n\nYou can use the `hasCodeOrFails()` method that does the same, but throws a validation exception, which is handled gracefully by the framework. It even accepts a custom message in case of failure, otherwise a default [translation](#translations) line will be used.\n\n#### Determining Safe Device bypass\n\nWhen the user is under a [safe device](#safe-devices), which is determined by cookie, no 2FA code will be required to log in. To check if this was the case on the current request, use the `wasTwoFactorBypassedBySafeDevice()` method on the user.\n\n```php\nuse Illuminate\\Http\\Request;\n\npublic function changeServerSetting(Request $request)\n{\n    if ($request-\u003euser()-\u003ewasTwoFactorBypassedBySafeDevice()) {\n        // Do something ...\n    }\n    \n    // ...\n}\n```\n\n### Deactivation\n\nYou can deactivate Two-Factor Authentication for a given User using the `disableTwoFactorAuth()` method. This will automatically invalidate the authentication data, allowing the User to log in with just his credentials.\n\n```php\npublic function disableTwoFactorAuth(Request $request)\n{\n    $request-\u003euser()-\u003edisableTwoFactorAuth();\n    \n    return 'Two-Factor Authentication has been disabled!';\n}\n```\n\n## Events\n\nThe following events are fired in addition to the default Authentication events.\n\n* `TwoFactorEnabled`: An User has enabled Two-Factor Authentication.\n* `TwoFactorRecoveryCodesDepleted`: An User has used his last Recovery Code.\n* `TwoFactorRecoveryCodesGenerated`: An User has generated a new set of Recovery Codes.\n* `TwoFactorDisabled`: An User has disabled Two-Factor Authentication.\n\n\u003e [!TIP]\n\u003e\n\u003e You can use `TwoFactorRecoveryCodesDepleted` to tell the User to create more Recovery Codes or mail them some more.\n\n## Middleware\n\nTwoFactor comes with two middleware for your routes: `2fa.enabled` and `2fa.confirm`.\n\n\u003e [!IMPORTANT]\n\u003e\n\u003e To avoid unexpected results, middleware only act on your users models implementing the `TwoFactorAuthenticatable` contract. If a user model doesn't implement it, the middleware will bypass any 2FA logic.\n\n### Require 2FA\n\nIf you need to ensure the User has Two-Factor Authentication enabled before entering a given route, you can use the `2fa.enabled` middleware. Users who implement the `TwoFactorAuthenticatable` contract and have 2FA disabled will be redirected to a route name containing the warning, which is `2fa.notice` by default.\n\n```php\nRoute::get('system/settings', function () {\n    // ...\n})-\u003emiddleware('2fa.enabled');\n```\n\nYou can implement the view easily with the one included in this package, optionally with a URL to point the user to enable 2FA:\n\n```php\nuse Illuminate\\Support\\Facades\\Route;\n\nRoute::view('2fa-required', 'two-factor::notice', [\n    'url' =\u003e url('settings/2fa')\n])-\u003ename('2fa.notice');\n```\n\n### Confirm 2FA\n\nMuch like the [`password.confirm` middleware](https://laravel.com/docs/authentication#password-confirmation), you can also ask the user to confirm entering a route by issuing a 2FA Code with the `2fa.confirm` middleware.\n\n```php\nRoute::get('api/token', function () {\n    // ...\n})-\u003emiddleware('2fa.confirm');\n\nRoute::post('api/token/delete', function () {\n    // ...\n})-\u003emiddleware('2fa.confirm');\n```\n\nThe middleware will redirect the user to the named route `2fa.confirm` by default, but you can change it in the first parameter. To implement the receiving routes, TwoFactor comes with the `Confirm2FACodeController` and a view you can use for a quick start.\n\n```php\nuse Illuminate\\Support\\Facades\\Route;\nuse Laragear\\TwoFactor\\Http\\Controllers\\ConfirmTwoFactorCodeController;\n\nRoute::get('2fa-confirm', [ConfirmTwoFactorCodeController::class, 'form'])\n    -\u003ename('2fa.confirm');\n\nRoute::post('2fa-confirm', [ConfirmTwoFactorCodeController::class, 'confirm']);\n```\n\nSince a user without 2FA enabled won't be asked for a code, you can combine the middleware with `2fa.require` to ensure confirming is mandatory for users without 2FA enabled.\n\n```php\nuse Illuminate\\Support\\Facades\\Route;\n\nRoute::get('api/token', function () {\n    // ...\n})-\u003emiddleware('2fa.require', '2fa.confirm');\n```\n\n#### Force confirmation\n\nWhen user confirm with their TOTP code, the middleware will [remember the confirmation](#confirmation-middleware) for a set amount of time.\n\nYou may _always_ force a confirmation, even if the user already confirmed, setting the first or second parameter as \"force\" or \"true\".\n\n```php\nuse Illuminate\\Support\\Facades\\Route;\n\nRoute::get('api/token', function () {\n    // ...\n})-\u003emiddleware('2fa.require', '2fa.confirm:force');\n\nRoute::get('api/important-token', function () {\n    // ...\n})-\u003emiddleware('2fa.require', '2fa.confirm:my-redirect-route-name,true');\n```\n\n## Validation\n\nSometimes you may want to manually trigger a TOTP validation in any part of your application for the authenticated user. You can validate a TOTP code for the authenticated user using the `topt` rule.\n\n```php\npublic function checkTotp(Request $request)\n{\n    $request-\u003evalidate([\n        'code' =\u003e 'totp'\n    ]);\n\n    // ...\n}\n```\n\nThis rule will succeed only if  the user is authenticated, it has Two-Factor Authentication enabled, and the code is correct or is a recovery code.\n\n\u003e [!TIP]\n\u003e\n\u003e You can enforce the rule to NOT use recovery codes using `totp:code`.\n\n## Translations\n\nTwoFactor comes with translation files that you can use immediately in your application. These are also used for the [validation rule](#validation).\n\n```php\npublic function disableTwoFactorAuth()\n{\n    // ...\n\n    session()-\u003eflash('message', trans('two-factor::messages.success'));\n\n    return back();\n}\n```\n\nTo add your own language, publish the translation files. These will be located in `lang/vendor/two-factor`:\n\n```shell\nphp artisan vendor:publish --provider=\"Laragear\\TwoFactor\\TwoFactorServiceProvider\" --tag=\"translations\"\n```\n\n## Configuration\n\nTo further configure the package, publish the configuration file:\n\n```shell\nphp artisan vendor:publish --provider=\"Laragear\\TwoFactor\\TwoFactorServiceProvider\" --tag=\"config\"\n```\n\nYou will receive the `config/two-factor.php` config file with the following contents:\n\n```php\nreturn [\n    'cache' =\u003e [\n        'store' =\u003e null,\n        'prefix' =\u003e '2fa.code'\n    ],\n    'recovery' =\u003e [\n        'enabled' =\u003e true,\n        'codes' =\u003e 10,\n        'length' =\u003e 8,\n\t],\n    'safe_devices' =\u003e [\n        'enabled' =\u003e false,\n        'max_devices' =\u003e 3,\n        'expiration_days' =\u003e 14,\n\t],\n    'confirm' =\u003e [\n        'key' =\u003e '_2fa',\n        'time' =\u003e 60 * 3,\n    ],\n    'login' =\u003e [\n        'view' =\u003e 'two-factor::login',\n        'key' =\u003e '_2fa_login',\n        'flash' =\u003e true,\n    ],\n    'secret_length' =\u003e 20,\n    'issuer' =\u003e env('OTP_TOTP_ISSUER'),\n    'totp' =\u003e [\n        'digits' =\u003e 6,\n        'seconds' =\u003e 30,\n        'window' =\u003e 1,\n        'algorithm' =\u003e 'sha1',\n    ],\n    'qr_code' =\u003e [\n        'size' =\u003e 400,\n        'margin' =\u003e 4\n    ],\n];\n```\n\n### Cache Store\n\n```php\nreturn  [\n    'cache' =\u003e [\n        'store' =\u003e null,\n        'prefix' =\u003e '2fa.code'\n    ],\n];\n```\n\n[RFC 6238](https://tools.ietf.org/html/rfc6238#section-5) states that one-time passwords shouldn't be able to be usable more than once, even if is still inside the time window. For this, we need to use the Cache to ensure the same code cannot be used again.\n\nYou can change the store to use, which it's the default used by your application, and the prefix to use as cache keys, in case of collisions.\n\n### Recovery\n\n```php\nreturn [\n    'recovery' =\u003e [\n        'enabled' =\u003e true,\n        'codes' =\u003e 10,\n        'length' =\u003e 8,\n    ],\n];\n```\n\nRecovery codes handling are enabled by default, but you can disable it. If you do, ensure Users can authenticate by other means, like sending an email with a link to a signed URL that logs him in and disables Two-Factor Authentication, or SMS.\n\nThe number and length of codes generated is configurable. 10 Codes of 8 random characters are enough for most authentication scenarios.\n\n### Safe devices\n\n```php\nreturn [\n    'safe_devices' =\u003e [\n        'enabled' =\u003e false,\n        'max_devices' =\u003e 3,\n        'expiration_days' =\u003e 14,\n    ],\n];\n```\n\nEnabling this option will allow the application to \"remember\" a device using a cookie, allowing it to bypass Two-Factor Authentication once a code is verified in that device. When the User logs in again in that device, it won't be prompted for a 2FA Code again.\n\nThe cookie contains a random value which is checked against a list of safe devices saved for the authenticating user. It's considered a safe device if the value matches and has not expired.\n\nThere is a limit of devices that can be saved, but usually three is enough (phone, tablet and PC). New devices will displace the oldest devices registered. Devices are considered no longer \"safe\" until a set amount of days.\n\nYou can change the maximum number of devices saved and the amount of days of validity once they're registered. More devices and more expiration days will make the Two-Factor Authentication less secure.\n\n\u003e [!TIP]\n\u003e\n\u003e When disabling Two-Factor Authentication, the list of safe devices is always flushed.\n\n### Confirmation Middleware\n\n```php\nreturn [\n    'confirm' =\u003e [\n        'key' =\u003e '_2fa',\n        'time' =\u003e 60 * 3,\n    ],\n];\n```\n\nThese control which key to use in the session for handling [`2fa.confirm` middleware](#confirm-2fa), and the expiration time in minutes.\n\n### Login Helper\n\n```php\nreturn [\n    'login' =\u003e [\n        'view' =\u003e 'two-factor::login',\n        'key' =\u003e '_2fa_login',\n        'flash' =\u003e true,\n    ],\n];\n```\n\nThis controls the login helper configuration, like the Blade view to render, the session key to hold the login input (like email and password), and if it should store these credentials using `flash` or just `put`.\n\nAbout the use of `flash`, you may disable it if you expect other requests during login, like it may happen with Inertia.js or Livewire, but this may keep the login input forever in the session, which in some cases it may be undesirable.\n\n### Secret length\n\n```php\nreturn [\n    'secret_length' =\u003e 20,\n];\n```\n\nThis controls the length (in bytes) used to create the Shared Secret. While a 160-bit shared secret is enough, you can tighten or loosen the secret length to your liking.\n\nIt's recommended to use 128-bit or 160-bit because some Authenticator apps may have problems with non-RFC-recommended lengths.\n\n### TOTP Configuration \n\n```php\nreturn [\n    'issuer' =\u003e env('OTP_TOTP_ISSUER'),\n    'totp' =\u003e [\n        'digits' =\u003e 6,\n        'seconds' =\u003e 30,\n        'window' =\u003e 1,\n        'algorithm' =\u003e 'sha1',\n    ],\n];\n```\n\nThis controls TOTP code generation and verification mechanisms:\n\n* Issuer: The name of the issuer of the TOTP. Default is the application name. \n* TOTP Digits: The amount of digits to ask for TOTP code. \n* TOTP Seconds: The number of seconds a code is considered valid.\n* TOTP Window: Additional steps of seconds to keep a code as valid.\n* TOTP Algorithm: The system-supported algorithm to handle code generation.\n\nThis configuration values are always URL-encoded and passed down to the authentication app as URI parameters:\n\n    otpauth://totp/Laravel%30taylor%40laravel.com?secret=THISISMYSECRETPLEASEDONOTSHAREIT\u0026issuer=Laravel\u0026label=Laravel%30taylor%40laravel.com\u0026algorithm=SHA1\u0026digits=6\u0026period=30\n\nThese values are printed to each 2FA data record inside the application. Changes will only take effect for new activations.\n\n\u003e [!WARNING]\n\u003e\n\u003e Do not edit these parameters if you plan to use publicly available Authenticator apps, since some of them **may not support non-standard configuration**, like more digits, different period of seconds or other algorithms.\n\n### QR Code Configuration \n\n```php\nreturn [\n    'qr_code' =\u003e [\n        'size' =\u003e 400,\n        'margin' =\u003e 4\n    ],\n];\n```\n\nThis controls the size and margin used to create the QR Code, which are created as SVG.\n\n## Custom TOTP Label\n\nYou may change how your model creates a TOTP Label, which is shown to the user on its authenticator, using the `getTwoFactorIssuer()` and `getTwoFactorUserIdentifier()` methods of your user.\n\nFor example, we can change the issuer and identifier depending on which domain the user is visiting.\n\n```php\npublic function getTwoFactorIssuer(): string\n{\n    return request()-\u003egetHost();\n}\n\npublic function getTwoFactorUserIdentifier(): string\n{\n    return request()-\u003egetHost() === 'admin.myapp.com'\n        ? $this-\u003egetAttribute('name')\n        : $this-\u003egetAttribute('email');\n}\n```\n\nThe above will render `users.myapp.com:john@gmail.com` or `admin.myapp.com:John Doe`.\n\n## Migration\n\nThis packages comes with a very hands-off approach for migrations. If you check the migration `...create_two_factor_authentications_table.php`, you will see something like this:\n\n```php\nuse Illuminate\\Database\\Schema\\Blueprint;\nuse Laragear\\TwoFactor\\Migrations\\TwoFactorAuthenticationMigration;\n\nreturn new class extends TwoFactorAuthenticationMigration\n{\n    /**\n     * Add additional columns to the table\n     */\n    public function addCustomColumns(Blueprint $table): void\n    {\n        // Here you can add custom columns to the Two Factor table.\n        //\n        // $table-\u003estring('alias')-\u003enullable();\n    }\n};\n```\n\nThe schema of the table is handled internally. The `addCustomColumns()` method gives you the opportunity to add more columns to the table.\n\nIf you need to execute logic after creating the table, or before dropping it, use the `afterUp()` and `beforeDown()` methods, respectively.\n\n```php\nuse Illuminate\\Database\\Schema\\Blueprint;\n\npublic function afterUp(Blueprint $table)\n{\n    $table-\u003eforeignId('authenticatable_id')-\u003ereferences('id')-\u003eon('users');\n}\n\npublic function beforeDown(Blueprint $table)\n{\n    $table-\u003edropForeign('authenticatable_id');\n}\n```\n\n### Morphs\n\nBy default, the table uses the default id of the Query Builder, `Builder::$defaultMorphKeyType`. If you want to change the morph key type for only this table, you may set the `$morphsType` property of the migration to `uuid` or `ulid`.\n\n```php\nuse Illuminate\\Database\\Schema\\Blueprint;\nuse Laragear\\TwoFactor\\Migrations\\TwoFactorAuthenticationMigration;\n\nreturn new class extends TwoFactorAuthenticationMigration\n{\n    protected string $morphsType = 'ulid'\n};\n```\n\n## Custom table name\n\nBy default, the `TwoFactorAuthentication` model will use the `two_factor_authentications` name for the table. If you want to change the name for whatever reason, set the table using the `$useTable` static property of the `TwoFactorAuthentication` model. You should do this on the `register()` method of your `AppServiceProvider`.\n\n```php\nuse Laragear\\TwoFactor\\Models\\TwoFactorAuthentication;\n\npublic function register(): void\n{\n    TwoFactorAuthentication::$useTable = 'my_custom_table';\n}\n```\n\n## Laravel Octane Compatibility\n\n- There are no singletons using a stale application instance. \n- There are no singletons using a stale config instance. \n- There are no singletons using a stale request instance. \n- There are no static properties written during a request.\n\nThere should be no problems using this package with Laravel Octane.\n\n## Security\n\nWhen using the [Login Helper](#logging-in), credentials are saved encrypted into the session. This can be undesirable for some applications. While this mechanism exists for convenience, you are welcome to create your own 2FA authentication flow with this package to avoid _flashing_ the credentials.\n\nOne alternative is to use the `2fa.confirm` site-wide, and set the config key `two-factor.confirm.time` to `INF`.\n\nIf you discover any security related issues, please email darkghosthunter@gmail.com instead of using the issue tracker.\n\n# License\n\nThis specific package version is licensed under the terms of the [MIT License](LICENSE.md), at time of publishing.\n\n[Laravel](https://laravel.com) is a Trademark of [Taylor Otwell](https://github.com/TaylorOtwell/). Copyright © 2011-2024 Laravel LLC.\n","funding_links":["https://github.com/sponsors/DarkGhostHunter","https://paypal.me/darkghosthunter"],"categories":["PHP"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FLaragear%2FTwoFactor","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FLaragear%2FTwoFactor","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FLaragear%2FTwoFactor/lists"}