Skip to content

Instantly share code, notes, and snippets.

@GuillermoFarias
Last active January 23, 2024 15:11
Show Gist options
  • Save GuillermoFarias/58b9d27aa06de76de974089708203192 to your computer and use it in GitHub Desktop.
Save GuillermoFarias/58b9d27aa06de76de974089708203192 to your computer and use it in GitHub Desktop.
Laravel 8 Custom Guard, login de usuario con API externa
<?php
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/
Route::middleware('auth:sso')->group(function () {
Route::get('/me', function (Request $request) {
return $request->user();
});
});
<?php
'guards' => [
'sso' => [
'driver' => 'sso'
]
],
<?php
namespace App\Providers;
use App\Providers\Sso\TokenGuard;
use App\Providers\Sso\TokenUserProvider;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Auth;
class AuthServiceProvider extends ServiceProvider
{
/**
* The policy mappings for the application.
*
* @var array
*/
protected $policies = [
// 'App\Models\Model' => 'App\Policies\ModelPolicy',
];
/**
* Register any authentication / authorization services.
*
* @return void
*/
public function boot()
{
$this->registerPolicies();
Auth::extend('sso', function ($app) {
$request = $app->make('request');
$userProvider = new TokenUserProvider($request->header('email'), $request->header('token'));
return new TokenGuard($userProvider, $request);
});
}
}
<?php
namespace App\Providers\Sso;
use Illuminate\Auth\GuardHelpers;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Contracts\Auth\Guard;
use Illuminate\Contracts\Auth\UserProvider;
use Illuminate\Http\Request;
class TokenGuard implements Guard
{
use GuardHelpers;
/**
* @var string
*/
private const EMAIL = 'email';
/**
* @var string
*/
private const TOKEN = 'token';
/**
* The request instance.
*
* @var \Illuminate\Http\Request
*/
protected $request;
/**
* Create a new authentication guard.
*
* @param \Illuminate\Contracts\Auth\UserProvider $provider
* @param \Illuminate\Http\Request $request
* @return void
*/
public function __construct(
UserProvider $provider,
Request $request
) {
$this->request = $request;
$this->provider = $provider;
}
/**
* Get the currently authenticated user.
*
* @return \Illuminate\Contracts\Auth\Authenticatable|null
*/
public function user(): ?Authenticatable
{
if (!is_null($this->user)) {
return $this->user;
}
$user = null;
if (!empty($this->request->header(self::TOKEN))) {
$credentials = [
self::EMAIL => $this->request->header(self::EMAIL),
self::TOKEN => $this->request->header(self::TOKEN),
];
$user = $this->provider->retrieveByCredentials($credentials);
}
return $this->user = $user;
}
/**
* Validate a user's credentials.
*
* @param array $credentials
* @return bool
*/
public function validate(array $credentials = []): bool
{
if (empty($credentials[self::TOKEN])) {
return false;
}
$credentials = [
self::EMAIL => $credentials[self::EMAIL],
self::TOKEN => $credentials[self::TOKEN],
];
if ($this->provider->retrieveByCredentials($credentials)) {
return true;
}
return false;
}
/**
* Set the current request instance.
*
* @param \Illuminate\Http\Request $request
* @return $this
*/
public function setRequest(Request $request)
{
$this->request = $request;
return $this;
}
}
<?php
namespace App\Providers\Sso;
use App\Models\User;
use Illuminate\Contracts\Auth\Authenticatable as UserContract;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Contracts\Auth\UserProvider;
use Illuminate\Support\Facades\Http;
class TokenUserProvider implements UserProvider
{
/**
* @var string
*/
private $host;
/**
* @var string
*/
private const EMAIL = 'email';
/**
* @var string
*/
private const TOKEN = 'token';
/**
* Create a new token sso user provider.
*
* @param string $email
* @param string $token
* @return void
*/
public function __construct()
{
$this->host = env('SSO_HOST');
}
/**
* Retrieve a user by their unique identifier.
*
* @param mixed $identifier
* @return \Illuminate\Contracts\Auth\Authenticatable|null
*/
public function retrieveById($identifier): ?Authenticatable
{
return null;
}
/**
* Retrieve a user by their unique identifier and "remember me" token.
*
* @param mixed $identifier
* @param string $token
* @return \Illuminate\Contracts\Auth\Authenticatable|null
*/
public function retrieveByToken($email, $token): ?Authenticatable
{
return $this->getUser($email, $token);
}
/**
* Update the "remember me" token for the given user in storage.
*
* @param \Illuminate\Contracts\Auth\Authenticatable $user
* @param string $token
* @return void
*/
public function updateRememberToken(UserContract $user, $token)
{
}
/**
* Retrieve a user by the given credentials.
*
* @param array $credentials
* @return \Illuminate\Contracts\Auth\Authenticatable|null
*/
public function retrieveByCredentials(array $credentials = []): ?Authenticatable
{
if (!array_key_exists(self::EMAIL, $credentials) && !array_key_exists(self::TOKEN, $credentials)) {
return null;
}
if (!$this->validate($credentials[self::EMAIL], $credentials[self::TOKEN])) {
return null;
}
return $this->getUser($credentials[self::EMAIL], $credentials[self::TOKEN]);
}
/**
* Validate a user against the given credentials.
*
* @param \Illuminate\Contracts\Auth\Authenticatable $user
* @param array $credentials
* @return bool
*/
public function validateCredentials(UserContract $user, array $credentials = []): bool
{
return $this->validate($credentials[self::EMAIL], $credentials[self::TOKEN]);
}
/**
* instance user from sso data user
*
* @param string $email
* @param string $token
* @return \Illuminate\Contracts\Auth\Authenticatable|null
*/
private function getUser(?string $email, ?string $token): ?Authenticatable
{
$response = Http::withHeaders([
self::EMAIL => $email,
self::TOKEN => $token,
])->get($this->host . '/api/whois');
if ($response->status() !== 200) {
return null;
}
$ssoUserInfo = json_decode($response->body());
if (!$ssoUserInfo) {
return null;
}
return new User((array) $ssoUserInfo);
}
/**
* validate if email and token are valid
*
* @param string $email
* @param string $token
* @return bool
*/
private function validate(?string $email, ?string $token): bool
{
$response = Http::withHeaders([
self::EMAIL => $email,
self::TOKEN => $token,
])->get($this->host . '/api/validate');
return $response->status() == 200;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment