Skip to content

Commit

Permalink
GITBOOK-23: change request with no subject merged in GitBook
Browse files Browse the repository at this point in the history
  • Loading branch information
joelbutcher authored and gitbook-bot committed Sep 20, 2024
1 parent 9503297 commit ce2acaa
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 0 deletions.
1 change: 1 addition & 0 deletions SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
* [Filament with Breeze](guides/filament-with-breeze.md)
* [Laravel Passport](guides/laravel-passport.md)
* [Socialite Providers](guides/socialite-providers.md)
* [Overriding Fortify's Authentication](guides/overriding-fortifys-authentication.md)

## 🔗 Links

Expand Down
102 changes: 102 additions & 0 deletions guides/overriding-fortifys-authentication.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# Overriding Fortify's Authentication

{% hint style="success" %}
This guide looks to provide assistance that specifically tackles scenarios where you may be overriding Fortify's authentication pipeline. via `Fortify::authenticateUsing()`.
{% endhint %}

## Background

If you are using the Socialstream + Jetstream stack, you may want to opt-in to Fortify's Two Factor Authentication mechanics. When logging in via Socialstream, we have had to override two fundamental authentication classes with our own. The first is to create our own `eloquent` user provider (found [here](https://github.com/joelbutcher/socialstream/blob/5309717b6d769ae9359844d518e835e1df894135/src/Auth/SocialstreamUserProvider.php) and registered [here](https://github.com/joelbutcher/socialstream/blob/d115bc82a4e15631faffcf512cbaf85e67ca8998/src/SocialstreamServiceProvider.php#L98-L101)). The second is to create our own version of Fortify's [login pipeline](https://github.com/laravel/fortify/blob/42695c45087e5abb3e173725b4f1ef4956a7b47d/src/Http/Controllers/AuthenticatedSessionController.php#L71) and use our own `RedirectIfTwoFactorAuthenticatable` class (which extends Fortify's one, but overrides the `validateCredentials` logic with our own).

If you're overriding the authentication pipeline to handle custom business logic (for example, [to check if a user has been blocked by an admin and redirect away if so](https://github.com/joelbutcher/socialstream/issues/370#issuecomment-2363262761)) you will need to make sure you include the same logic we use our `RedirectIfTwoFactorAuthenticatable` override, to make sure that login with socialstream providers still works.

```php
use App\Models\User;
use JoelButcher\Socialstream\Contracts\ResolvesSocialiteUsers;
use JoelButcher\Socialstream\Socialstream;
use Illuminate\Validation\ValidationException;

Fortify::authenticateUsing(function (Request $request) {
if ($provider = $request->route('provider')) {
$socialUser = app(ResolvesSocialiteUsers::class)
->resolve($provider);

$connectedAccount = Socialstream::$connectedAccountModel::where('email', $socialUser->getEmail())->first();

if (! $connectedAccount) {
ValidationException::withMessages([
Fortify::username() => [__('auth.failed')],
]);
}

return $connectedAccount->user;
}
// You're custom authentication logic here.
});
```

### Example

In this example, we're checking to see if a user has been blocked by and admin and returning a validation error if that is the case.

{% hint style="warning" %}
If you are following the example in Fortify's documentation and you are doing a hash check for the user entered password against the hash stored on the model, you will want to make sure you **don't** do this check for Socialstream routes.
{% endhint %}

First, check our route param to see if the user is coming from a Socialstream OAuth callback route:

```php
use App\Models\User;
use JoelButcher\Socialstream\Contracts\ResolvesSocialiteUsers;
use JoelButcher\Socialstream\Socialstream;
use Illuminate\Validation\ValidationException;

Fortify::authenticateUsing(function (Request $request) {
$user = null;
$provider = $request->route('provider');

// 1a. Attempt the resolve the user via socialstream
if ($provider) {
$socialUser = app(ResolvesSocialiteUsers::class)
->resolve($provider);

$connectedAccount = Socialstream::$connectedAccountModel::where('email', $socialUser->getEmail())->first();

if (! $connectedAccount) {
throw ValidationException::withMessages([
Fortify::username() => [__('auth.failed')],
]);
}

$user = $connectedAccount->user;
}

// 1b. Attempt to resolve the user if email present in request (i.e. from login form).
if (! $user && $request->has('email') {
$user = User::where('email', $request->email)->first();
}

// 2. Check if the resolved user is blocked and handle
if ($user->blockedByAdmin()) {
throw ValidationException::withMessages([
Fortify::username() => [__('auth.blocked')],
]);
}

// 3. User is not blocked, log in if from Socialstream route
if ($provider) {
return $user;
}

// 4. User hasn't set a password, so must login using an OAuth provider
if (is_null($user->password) {
throw ValidationException::withMessages([
Fortify::username() => [__('auth.failed')],
]);
}

// 5. Verify the password if the user has logged in via a form
return Hash::check($request->password, $user->password) ? $user : null;
});
```

0 comments on commit ce2acaa

Please sign in to comment.