Skip to content

[LdapRecord-Laravel] Authentication occurs twice using Laravel Fortify #219

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

Closed
fcno opened this issue Oct 19, 2020 · 13 comments
Closed

[LdapRecord-Laravel] Authentication occurs twice using Laravel Fortify #219

fcno opened this issue Oct 19, 2020 · 13 comments
Labels
question Further information is requested

Comments

@fcno
Copy link

fcno commented Oct 19, 2020

Using
LdapRecord - 1.7.3
Fortify with custom UI 1.6
Laravel 8

Hello.
I don't know if this would be the normal procedure, but it seems to me, from the log bellow, that the authentication routine is running twice. It would be normal or there is probably something wrong with my application, because if that's the case, I've tried to investigate here and I couldn't find where the problem could be. Any tips?


[2020-10-19 13:59:38] local.INFO: User [xxx] has been successfully located for authentication.  
[2020-10-19 13:59:38] local.INFO: Object with name [xxx] is being synchronized.  
[2020-10-19 13:59:38] local.INFO: Object with name [xxx] has been successfully synchronized.  
[2020-10-19 13:59:38] local.INFO: User [xxx] is authenticating.  
[2020-10-19 13:59:38] local.INFO: User [xxx] has successfully passed LDAP authentication.  
[2020-10-19 13:59:38] local.INFO: User [xxx] has been successfully located for authentication.  
[2020-10-19 13:59:38] local.INFO: Object with name [xxx] is being synchronized.  
[2020-10-19 13:59:38] local.INFO: Object with name [xxx] has been successfully synchronized.  
[2020-10-19 13:59:38] local.INFO: User [xxx] is authenticating.  
[2020-10-19 13:59:38] local.INFO: User [xxx] has successfully passed LDAP authentication.  
@stevebauman
Copy link
Member

Hi @Babiute,

How are you authenticating with Fortify? Can you post your usage? For example:

Fortify::authenticateUsing('...');

@stevebauman stevebauman added the question Further information is requested label Oct 19, 2020
@fcno
Copy link
Author

fcno commented Oct 19, 2020

Hi @stevebauman .

        // app/providers/AuthServiceProvider

        Fortify::authenticateUsing(function ($request) {
            // Determine the guard name by the submitted domain.
            $guard = $request->domain == 'alpha' ? 'alpha' : 'bravo';

            //Set default guard
            config(['fortify.guard' => $guard]);
            Auth::shouldUse($guard);

            $validated = Auth::validate([
                'samaccountname' => $request->input('username'),
                'password' => $request->input('password')
            ]);

            return $validate ? Auth::getLastAttempted() : null;
        });

@stevebauman
Copy link
Member

Ok, can you share your login view so I can understand how you're sending the post request?

@stevebauman
Copy link
Member

Also, do you have two-factor authentication enabled inside of your config/fortify.php file?

@fcno
Copy link
Author

fcno commented Oct 19, 2020

About the routes, I do not have my own login/logout routes. Im using the Login and Logout routes provided by Fortify default installation.

@include('layout.partial.top')

<nav class="navbar navbar-dark bg-secondary">
  <span class="navbar-brand">{{ config('app.name') }}</span>
</nav>

<section class="container">
  <div class="row justify-content-center">
    <div class="col-12 col-lg-6 my-3">

      <img class="mx-auto my-3 d-block" id="logo" src="{{ asset('imagem/logo.svg') }}" alt="logo">

      <form>
      @csrf

      {{--Input usuário--}}
        <input 
            class="form-control my-3 @error('username') is-invalid @enderror"
            type="text"
            name="username"
            id="username"
            placeholder="Usuário de rede"
            autocomplete="off"
            title="Informar o seu usuário de rede"
            required
            autofocus
            value="{{ old('username') }}">

        {{--Input senha--}}
        <input 
            class="form-control my-3 @error('password') is-invalid @enderror"
            type="password"
            name="password"
            id="password"
            placeholder="Senha de rede"
            autocomplete="off"
            title="Informar a sua senha de rede"
            required>

        {{--Select órgão--}}
        <select
            class="form-control my-3 @error('domain') is-invalid @enderror"
            name="domain"
            id="domain"
            required>
            <option value="">Escolha o órgão</option>
            @foreach (config('orgao.nome_completo') as $orgao => $nome_completo)
            <option {{ old('domain') == $orgao ? 'selected' : '' }} value="{{ $orgao }}" >{{ $nome_completo }}</option>
            @endforeach
        </select>


        {{--Botão para entrar na aplicação--}}
        <button
            class="btn btn-primary btn-block my-3"
            type="submit"
            formmethod="POST"
            formaction="{{ route('login') }}"
            name="button-autenticar"
            title="Acessar o sistema">
              Entrar
        </button>
      </form>

      @include('layout.partial.errors')

    </div>
  </div>
</section>

@include('layout.partial.footer')

@fcno
Copy link
Author

fcno commented Oct 19, 2020

Also, do you have two-factor authentication enabled inside of your config/fortify.php file?

No. Every default feature is disabled.

@stevebauman
Copy link
Member

I do not have my own login/logout routes. Im using the Login and Logout routes provided by Fortify default installation.

Yup absolutely -- I did not ask for the routes.

Can you post your App\Models\User.php?

@fcno
Copy link
Author

fcno commented Oct 19, 2020

About the routes, I was just trying to antecipate a possible question 👍

As I'm using multi-domain authentication, Im not using the default model. Im using the 'alpha' user.

<?php

namespace App\Ldap\Alpha;

use LdapRecord\Models\ActiveDirectory\User as LdapUser;

class User extends LdapUser
{
    protected $connection = 'alpha';
}

@stevebauman
Copy link
Member

stevebauman commented Oct 19, 2020

No worries!

Aren't you using database synchronization? That's what the logs indicate.

I'm looking for your applications Eloquent User model -- not the LDAP model. 👍

Your Eloquent user model would be configured inside of each guard inside of your config/auth.php file. For example:

// config/auth.php

'providers' => [
    // ...

    'ldap' => [
        'driver' => 'ldap',
        'model' => LdapRecord\Models\ActiveDirectory\User::class,
        'database' => [
            'model' => App\Models\User::class, // <-- This model here.
            '...',
        ],
    ],
],

@fcno
Copy link
Author

fcno commented Oct 19, 2020

Yes I got it.
It was an error to post the synchronization log. I enabled synchronization today (step by step), so I can learn how to properly use each feature in your library
But this double authentication has been going on since before.
The correct log would be this, from the branch without synchronization that I think is not what causes the problem.

[2020-10-19 15:22:03] local.INFO: User [xxx] has been successfully located for authentication.  
[2020-10-19 15:22:03] local.INFO: User [xxx] is authenticating.  
[2020-10-19 15:22:03] local.INFO: User [xxx] has successfully passed LDAP authentication.  
[2020-10-19 15:22:03] local.INFO: User [xxx] has been successfully located for authentication.  
[2020-10-19 15:22:03] local.INFO: User [xxx] is authenticating.  
[2020-10-19 15:22:03] local.INFO: User [xxx] has successfully passed LDAP authentication.  

Either way follows the model, but it doesn't even exist in the plain authentication branch where the double authentication is happening too.

namespace App\Models;

use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use LdapRecord\Laravel\Auth\LdapAuthenticatable;
use LdapRecord\Laravel\Auth\AuthenticatesWithLdap;

class User extends Authenticatable implements LdapAuthenticatable
{
    use HasFactory, Notifiable, AuthenticatesWithLdap;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name',
        'email',
        'password',
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password',
        'remember_token',
    ];

    /**
     * The attributes that should be cast to native types.
     *
     * @var array
     */
    protected $casts = [
        'email_verified_at' => 'datetime',
    ];
}

@stevebauman
Copy link
Member

I think I've discovered the issue. In Laravel Fortify, by default, it will always attempt the $authenticateUsingCallback twice if you do not override the $authenticateThroughCallback inside of the loginPipeline method shown here:

https://github.com/laravel/fortify/blob/c77b1832eed4223fa9e8fe1a5afc795c00c126a5/src/Http/Controllers/AuthenticatedSessionController.php#L69-L89

    protected function loginPipeline(LoginRequest $request)
    {
        if (Fortify::$authenticateThroughCallback) {
            return (new Pipeline(app()))->send($request)->through(array_filter(
                call_user_func(Fortify::$authenticateThroughCallback, $request)
            ));
        }

        if (is_array(config('fortify.pipelines.login'))) {
            return (new Pipeline(app()))->send($request)->through(array_filter(
                config('fortify.pipelines.login')
            ));
        }

        return (new Pipeline(app()))->send($request)->through(array_filter([
            config('fortify.limiters.login') ? null : EnsureLoginIsNotThrottled::class,
            RedirectIfTwoFactorAuthenticatable::class, // <-- `$authenticateUsingCallback` called here
            AttemptToAuthenticate::class, // <-- `$authenticateUsingCallback` called here
            PrepareAuthenticatedSession::class,
        ]));
    }

First, the RedirectIfTwoFactorAuthenticatable action is used, which calls the $authenticateUsingCallback (regardless if you have the feature disabled):

https://github.com/laravel/fortify/blob/c77b1832eed4223fa9e8fe1a5afc795c00c126a5/src/Http/Controllers/AuthenticatedSessionController.php#L85

https://github.com/laravel/fortify/blob/c77b1832eed4223fa9e8fe1a5afc795c00c126a5/src/Actions/RedirectIfTwoFactorAuthenticatable.php#L68-L74

The $authenticateUsingCallback is called a second time inside of the AttemptToAuthenticate action:

https://github.com/laravel/fortify/blob/c77b1832eed4223fa9e8fe1a5afc795c00c126a5/src/Actions/AttemptToAuthenticate.php#L48-L50

To work around this, you either have to define your own login pipeline inside of your config/fortify.php file, or define an $authenticateThroughCallback using Fortify::authenticateThrough().

@fcno
Copy link
Author

fcno commented Oct 20, 2020

Hey Steve.
Thx for the quick replay.
As I'm still in the learning curve of Laravel, I do not know how to properly do it.
But thx for you help and I hope it helps who face the same problem :)

@stevebauman
Copy link
Member

Hi @Babiute, last night I pushed a PR into Laravel Fortify that resolves this issue and it was merged this morning 🎉 :

laravel/fortify#127 (comment)

Once a new release of Laravel Fortify is created, it should contain this fix 👍

@stevebauman stevebauman changed the title Authentication Twince [LdapRecord-Laravel] Authentication occurs twice using Laravel Fortify Oct 20, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants