Ldaprecord-laravel: Interception when User must change password at next logon

Created on 5 Mar 2020  路  9Comments  路  Source: DirectoryTree/LdapRecord-Laravel

I'm trying to figure out the most elegant way of intercepting when a user logs in, and seeing if their account is set to require a password change. I am using Active Directory and when a user attempts to login with this attribute set on their account, authentication fails completely until it is removed. What would the best way of doing this be if the response I always get back within this Laravel package for the user handler is just false? Should I check the user with AccountControl first to see if their password expired/forcing change, or is there a better way?

Thanks!

bug good first issue question

All 9 comments

Hi @ellisonpatterson,, great question!

What are you looking to achieve when this occurs? If you're wanting to return an error message to the login screen of you application, this is a feature built-in to LdapRecord-Laravel:

https://ldaprecord.com/docs/laravel/auth/usage/#displaying-ldap-error-messages

Here is the error map that is used when a user fails authentication:

https://github.com/DirectoryTree/LdapRecord-Laravel/blob/53275dd04650fe9ef89271e0ecaefd9d2dea55e1/src/Auth/ListensForLdapBindFailure.php#L85-L97

Let me know if that's what you're looking to achieve, or if you're looking to perform a different task upon a user failing authentication.

I want to know if they require a password change and redirect them to a form to change the password.
However upon looking further, the AD server is spitting this error back:
Invalid credentials as the $errorMessage.
"80090308: LdapErr: DSID-0C09041C, comment: AcceptSecurityContext error, data 773, v4563" as the $diagnosticMessage.
These are the parameter values for the method at https://github.com/DirectoryTree/LdapRecord-Laravel/blob/master/src/Auth/ListensForLdapBindFailure.php#L34

However, in that method, causedByInvalidCredentials() only checks to see if the $errorMessage equals Invalid credentials and even though the error response has an error code of 773, it will always send back the invalid credentials message. I think that's why after I implemented the trait to display validation errors I thought it was broken since the error message never changed from invalid credentials.

Ohh yes you're right, thanks for that! Let me fix that right now.

Let me also make this extensible so you can define your own method and return a response depending on each code.

Ohh yes you're right, thanks for that! Let me fix that right now.

Let me also make this extensible so you can define your own method and return a response depending on each code.

That would be awesome :D Thank you!

Ok done - can you try dev-master and let me know your thoughts? Here's how you will need to implement this on your login controller.

You will need to override the new method handleLdapBindError and then use the abort() helper and supply a redirect inside of it to the page you want to redirect users to:

// app/Http/Controllers/Auth/LoginController.php

protected function handleLdapBindError($message, $code = null)
{
    if ($code == '773') {
        abort(redirect('/password-reset'));
    }

    parent::handleLdapBindError($message, $code);
}

@stevebauman it's working great! My question now is that even though the authentication process fails with error code 773, if I should just log the user in like normal for that password reset or handle it another way.

Or I wonder if they could be set as the user instance but in a crippled state, or is that not a functionality of Laravel's user system?

Also how could I grab the half-made user model in the handleLdapBindError method to use it without having to parse the request() object?

Awesome! Well, you'll have to create your own "PasswordResetController" that allows the user to enter in a new password for their account.

The problem with this though, is that you won't be able to allow the user to "change" their password normally as you would using a domain joined computer. This is due to the fact that you need to bind as the user to be able to change their password, but binding will fail since they have their userAccountControl set to "Must change password on next login".

You would need to first bind as a domain administrator and then "reset" their password manually using that account. There isn't a way around this unfortunately without removing the UAC flag.

Or I wonder if they could be set as the user instance but in a crippled state, or is that not a functionality of Laravel's user system?

This isn't possible. The user is either a guest, or logged in. Any in-between would need to be built yourself in your application.

Also how could I grab the half-made user model in the handleLdapBindError method to use it without having to parse the request() object?

Half-made user model? You wouldn't do it there. You would have to listen to for the LdapRecord\Laravel\Events\Synchronizing event. The request object won't contain any user until a user is fully authenticated.

Hope this helps!

Oh one thing, you can't call the parent handleLdapBindError method since it doesn't work in traits, resulting in this error:

[2020-03-06 16:11:45] local.ERROR: Method App\Http\Controllers\LoginController::handleLdapBindError does not exist. {"exception":"[object] (BadMethodCallException(code: 0): Method App\\Http\\Controllers\\LoginController::handleLdapBindError does not exist. at /dev/proc/vendor/laravel/framework/src/Illuminate/Routing/Controller.php:68)
[stacktrace]

Ohh yes, my docs are incorrect, thanks! You'll need to override the trait method:

// app/Http/Controllers/Auth/LoginController.php

use ListensForLdapBindFailure {
    handleLdapBindError as baseHandleLdapBindError;
}

protected function handleLdapBindError($message, $code = null)
{
    if ($code == '773') {
        abort(redirect('/password-reset'));
    }

    $this->baseHandleLdapBindError($message, $code);
}
Was this page helpful?
0 / 5 - 0 ratings

Related issues

satyakresna picture satyakresna  路  7Comments

murrant picture murrant  路  5Comments

brinkonaut picture brinkonaut  路  6Comments

jagDanJu picture jagDanJu  路  8Comments

siegherr picture siegherr  路  6Comments