Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Installation question #8

Closed
kaizirlewagen opened this issue Apr 28, 2022 · 6 comments
Closed

Installation question #8

kaizirlewagen opened this issue Apr 28, 2022 · 6 comments

Comments

@kaizirlewagen
Copy link

Hi, i try to install and configure this module... but at the point To use on the screen page, use the trait Orchid\Fortify\TwoFactorScreenAuthenticatable: i don't know where i have to add this part.

If i add it to my user edit view, i got this error:

Method App\Orchid\Screens\User\UserEditScreen::twoFactorCommandBar does not exist.

I am using Laravel 9 and Orchid 12, can you give me some informations how i can add 2fa?

@tabuna
Copy link
Member

tabuna commented Apr 28, 2022

Hi @kaizirlewagen

It means that you need to add this trend and code to the UserProfileScreen class

@kaizirlewagen
Copy link
Author

I edit "UserProfileScreen.php" and i add use Orchid\Fortify\TwoFactorScreenAuthenticatable; and change the following functions:

     * Button commands.
     *
     * @return Action[]
     */
    public function commandBar(): iterable
    {
	    return [
	        $this->twoFactorCommandBar(),
	    ];
    }

    /**
     * @return \Orchid\Screen\Layout[]
     */
    public function layout(): iterable
    {
        return [
            $this->twoFactorLayout(),
            
            Layout::block(UserEditLayout::class)
                ->title(__('Profile Information'))
                ->description(__("Update your account's profile information and email address."))
                ->commands(
                    Button::make(__('Save'))
                        ->type(Color::DEFAULT())
                        ->icon('check')
                        ->method('save')
                ),

            Layout::block(ProfilePasswordLayout::class)
                ->title(__('Update Password'))
                ->description(__('Ensure your account is using a long, random password to stay secure.'))
                ->commands(
                    Button::make(__('Update password'))
                        ->type(Color::DEFAULT())
                        ->icon('check')
                        ->method('changePassword')
                ),
        ];
    }`

After that the following error is shown:

BadMethodCallException
Method App\Orchid\Screens\User\UserProfileScreen::twoFactorCommandBar does not exist.

Do you have an idea?

@czernika
Copy link
Contributor

czernika commented May 1, 2022

@kaizirlewagen What are you missing is you need to use TwoFactorScreenAuthenticatable as a trait within Screen

Basically the whole setup process is

  1. Install Laravel Fortify package
composer require laravel/fortify
  1. Publish its resources
php artisan vendor:publish --provider="Laravel\Fortify\FortifyServiceProvider"
  1. Run migrations to add two_fa columns
php artisan migrate
  1. Add FortifyServiceProvider into providers array within config/app.php (I lost 15 minutes of my life when forget to do this in a first time :)
  2. Add TwoFactorAuthenticatable trait to User model
use Laravel\Fortify\TwoFactorAuthenticatable;

class User extends Authenticatable
{
    use TwoFactorAuthenticatable;
}
  1. Install Orchid Fortify
composer require orchid/fortify
  1. Set to false auth key of config/platform.php

  2. Within required Screen (for example, UserProfileScreen add following lines

use Orchid\Screen\Screen;
use Orchid\Fortify\TwoFactorScreenAuthenticatable;

class UserProfileScreen extends Screen
{
	use TwoFactorScreenAuthenticatable; // I guess this is where you've been stacked

    public function commandBar(): iterable
    {
        return [
		$this->twoFactorCommandBar(), // dropdown button
	];
    }

    public function layout(): iterable
    {
        return [
		// other layouts
		
		$this->twoFactorLayout(), // modal and methods
        ];
    }
}
  1. Almost done, BUT it is still needs extra actions to be done to work

Fortify provides some options - one of them is to confirm Two FA or not. You may find this within config/fortify.php

'features' => [
    Features::twoFactorAuthentication([
        'confirm' => true, // confirm Two FA
        'confirmPassword' => true, // confirm password to enable Two FA
    ]),
],

confirm key requires to you to confirm TwoFA. If TwoFA keys was not confirmed you still be able to login without TwoFA.

Or you may just disable it ('confirm' => false) if you're ok with no confirmation - it is will work

You may achieve this as you wish, I will show "my way"

  1. Create Layout for confirmation code
use Orchid\Screen\Field;
use Orchid\Screen\Fields\Input;
use Orchid\Screen\Layouts\Rows;

class ProfileTwoFALayout extends Rows
{
    protected function fields(): iterable
    {
        return [
		Input::make('code'), // key should be named `code`. That's basically required minimum
	];
    }
}
  1. Add Layout to your Screen
class UserProfileScreen extends Screen
{
use TwoFactorScreenAuthenticatable; // I guess this is where you've been stacked

    public function commandBar(): iterable
    {
        return [
		$this->twoFactorCommandBar(), // dropdown button
	];
    }

    public function layout(): iterable
    {
        return [
            Layout::block(ProfileTwoFALayout::class)
                ->title(__('Confirm Two FA'))
		->canSee(
                    auth()->user()->two_factor_secret && // show if authorized user has enabled Two FA
                    !auth()->user()->two_factor_confirmed_at // but still doesn't confirmed it
                )
                ->commands(
                    Button::make(__('Confirm'))
                        ->type(Color::PRIMARY())
                        ->icon('check')
                        ->action(route('two-factor.confirm')) // this route provided by Fortify and should not be changed
                ),

		// other layouts

		$this->twoFactorLayout(), // modal and methods
        ];
    }
}

As you can see I implemented canSee() method to show this layout by specific condition. As I am using it only for user profile screen, I can use auth()->user() with no fear. You app may require other logic to handle

After hitting confirm you will be redirect back, and in your database you will see all three columns two_factor_secret, two_factor_recovery_code and two_factor_confirmed_at filled with information.

And that's it

@kaizirlewagen
Copy link
Author

@czernika Amazing... thank you very much... i will try it today. 👍

@kaizirlewagen
Copy link
Author

@czernika Works perfect. Thx for your help 👍

@darkol
Copy link

darkol commented Jan 31, 2024

I guess this should be added to README.md

I have followed the procedure above, filling some more information:

  1. Install Laravel Fortify package
composer require laravel/fortify
  1. Publish its resources
php artisan vendor:publish --provider="Laravel\Fortify\FortifyServiceProvider"
  1. Run migrations to add two_fa columns
php artisan migrate
  1. Add FortifyServiceProvider into providers array within config/app.php

Add App\Providers\FortifyServiceProvider::class to providers

    'providers' => ServiceProvider::defaultProviders()->merge([
        /*
         * Package Service Providers...
         */

        /*
         * Application Service Providers...
         */
        App\Providers\AppServiceProvider::class,
        App\Providers\AuthServiceProvider::class,
        // App\Providers\BroadcastServiceProvider::class,
        App\Providers\EventServiceProvider::class,
        App\Providers\RouteServiceProvider::class,
        App\Providers\FortifyServiceProvider::class,
    ])->toArray(),
  1. Add TwoFactorAuthenticatable trait to User model app\Models\User.php
use Laravel\Fortify\TwoFactorAuthenticatable;

class User extends Authenticatable
{
    use TwoFactorAuthenticatable;
}
  1. Install Orchid Fortify
composer require orchid/fortify
  1. Set to false the auth key of config/platform.php

  2. Within required Screen - for example UserProfileScreen (app\Orchid\Screens\User\UserProfileScreen.php) add following lines

use Orchid\Screen\Screen;
use Orchid\Fortify\TwoFactorScreenAuthenticatable;

class UserProfileScreen extends Screen
{
	use TwoFactorScreenAuthenticatable; // I guess this is where you've been stacked

    public function commandBar(): iterable
    {
        return [
		$this->twoFactorCommandBar(), // dropdown button
	];
    }

    public function layout(): iterable
    {
        return [
		// other layouts
		
		$this->twoFactorLayout(), // modal and methods
        ];
    }
}
  1. Almost done, BUT it is still needs extra actions to be done to work

Fortify provides some options - one of them is to confirm Two FA or not. You may find this within config/fortify.php

'features' => [
    Features::twoFactorAuthentication([
        'confirm' => true, // confirm Two FA
        'confirmPassword' => true, // confirm password to enable Two FA
    ]),
],

confirm key requires to you to confirm TwoFA. If TwoFA keys was not confirmed you still be able to login without TwoFA.

Or you may just disable it ('confirm' => false) if you're ok with no confirmation - it is will work

You may achieve this as you wish, I will show "my way"

Create Layout for confirmation code in new file app\Orchid\Screens\User\ProfileTwoFALayout.php

<?php

declare(strict_types=1);

namespace App\Orchid\Screens\User;

use Orchid\Screen\Field;
use Orchid\Screen\Fields\Input;
use Orchid\Screen\Layouts\Rows;

class ProfileTwoFALayout extends Rows
{
    protected function fields(): iterable
    {
        return [
			Input::make('code'), // key should be named `code`. That's basically required minimum
		];
    }
}

Add Layout to your Screen in app\Orchid\Screens\User\UserProfileScreen.php

class UserProfileScreen extends Screen
{
use TwoFactorScreenAuthenticatable; // I guess this is where you've been stacked

    public function commandBar(): iterable
    {
        return [
		$this->twoFactorCommandBar(), // dropdown button
	];
    }

    public function layout(): iterable
    {
        return [
            Layout::block(ProfileTwoFALayout::class)
                ->title(__('Confirm Two FA'))
		->canSee(
                    auth()->user()->two_factor_secret && // show if authorized user has enabled Two FA
                    !auth()->user()->two_factor_confirmed_at // but still doesn't confirmed it
                )
                ->commands(
                    Button::make(__('Confirm'))
                        ->type(Color::PRIMARY())
                        ->icon('check')
                        ->action(route('two-factor.confirm')) // this route provided by Fortify and should not be changed
                ),

		// other layouts

		$this->twoFactorLayout(), // modal and methods
        ];
    }
}

As you can see I implemented canSee() method to show this layout by specific condition.
As I am using it only for user profile screen, I can use auth()->user() with no fear. You app may require other logic to handle

After hitting confirm you will be redirect back, and in your database you will see all three columns two_factor_secret, two_factor_recovery_code and two_factor_confirmed_at filled with information.

PROBLEM: OK, I can add 2FA to the user that has at least main privilege to access admin panel to reach his User Profile Screen and I can see all three fields in the database populated, and confirm button is no longer visible in User Profile Screen, but I am not asked for 2FA on login?! What am I missing?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

4 participants