The solution involves seven PHP files
- app/Http/Controllers/HomeController.php - homepage controller; the destination for an authenticated user
- app/Providers/ApiUserProvider.php - a custom provider to bootstrap and register the logged-in user, and implements the interface IlluminateContractsAuthUserProvider
- app/CoreExtensions/SessionGuardExtended.php - custom guard-controller to log-in the user and receives the authentication values and stores them in session array; extends class IlluminateAuthSessionGuard
- app/ApiUser - if you're using OAuth2 (Laravel's Passport); custom user class that exposes the OAuth access_token; extends IlluminateAuthGenericUser and implements the interface IlluminateContractsAuthAuthenticatable
- config/auth.php - the auth config which instructs the Auth() facade to return the custom session guard
- app/Providers/AuthServiceProvider.php - the auth bootstrap
- app/Providers/AppServiceProvider.php - the main application bootstrap
Source research/investigation material are cited for you to investigate for yourself and comprehend the background context to their existence. I make no claims to be a genius who created the solution from scratch through my own mojo, but rather that - like all innovators - I build on the efforts of others. The unique selling point of my article is that I provide a complete packaged solution, whereas the cited sources provide solutions to niche parts of the overall answer. Together, after much trial and error, they helped me to form a complete solution.
A really useful article to understands how config/auth.php affects execution in AuthManager.php is https://www.2hatslogic.com/blog/laravel-custom-authentication/
No code modifications are made to the following, but they're included to acknowledge the role they play and their importance in the process:
- vendor/laravel/framework/src/Illuminate/Auth/AuthManager.php - main authorization factory manager
- Auth() facade - returns the shrink-wrapped IlluminateAuthSessionGuard class instance by default, unless it's instructed to do otherwise through the config/auth.php file - Auth() is used ubiquitously throughout Laravel code to retrieve the session guard
The Code
app/Http/Controllers/HomeController.php
<?php
namespace AppHttpControllers;
use IlluminateHttpRequest;
/**
* Handles and manages the home-page
*
* @category controllers
*/
class HomeController extends Controller
{
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('auth');
}
public function index()
{
blah
}
... other methods ...
}
app/Providers/ApiUserProvider.php
Sources:
<?php
namespace AppProviders;
use IlluminateContractsAuthUserProvider;
use IlluminateContractsAuthAuthenticatable as UserContract;
use AppApiUser;
/**
* Delegates API user login and authentication
*
* @category providers
*/
class ApiUserProvider implements UserProvider
{
/**
* Custom API Handler
* Used to request API and capture responses
*
* @var PathToYourInternalApiHandler
*/
private $_oApi = null;
/**
* POST request to API
*
* @param string $p_url Endpoint URL
* @param array $p_arrParam Parameters
* @param boolean $p_isOAuth2 Is OAuth2 authenticated request? [Optional, Default=True]
*
* @return array
*/
private function _post(string $p_url, array $p_arrParam, bool $p_isOAuth2=true)
{
if (!$this->_oApi) {
$this->_oApi = new PathToYourInternalApiHandler();
}
$arrResponse = $this->_oApi->post($p_url, $p_arrParam, $p_isOAuth2);
return $arrResponse;
}
/**
* GET request to API
*
* @param string $p_url Endpoint URL
* @param array $p_arrParam Parameters [Optional, Default = array()]
*
* @return array
*/
private function _get(string $p_url, array $p_arrParam=[], bool $p_isOAuth2=true)
{
if (!$this->_oApi) {
$this->_oApi = new PathToYourInternalApiHandler();
}
$arrResponse = $this->_oApi->get($p_url, $p_arrParam);
return $arrResponse;
}
/**
* Retrieve a user by the given credentials.
*
* @param array $p_arrCredentials
*
* @return IlluminateContractsAuthAuthenticatable|null
*/
public function retrieveByCredentials(array $p_arrCredentials)
{
$arrResponse = $this->_post('/login', $p_arrCredentials, false);
if ( $arrResponse['result'] ) {
$arrPayload = array_merge(
$arrResponse['data'],
$p_arrCredentials
);
return $this->getApiUser($arrPayload);
}
}
/**
* Retrieve a user by their unique identifier.
*
* @param mixed $p_id
*
* @return IlluminateContractsAuthAuthenticatable|null
*/
public function retrieveById($p_id)
{
$arrResponse = $this->_get("user/id/{$p_id}");
if ( $arrResponse['result'] ) {
return $this->getApiUser($arrResponse['data']);
}
}
/**
* Validate a user against the given credentials.
*
* @param IlluminateContractsAuthAuthenticatable $p_oUser
* @param array $p_arrCredentials
*
* @return bool
*/
public function validateCredentials(UserContract $p_oUser, array $p_arrCredentials)
{
return $p_oUser->getAuthPassword() == $p_arrCredentials['password'];
}
/**
* Get the api user.
*
* @param mixed $p_user
*
* @return AppAuthApiUser|null
*/
protected function getApiUser($p_user)
{
if ($p_user !== null) {
return new ApiUser($p_user);
}
return null;
}
protected function getUserById($id)
{
$user = [];
foreach ($this->getUsers() as $item) {
if ($item['account_id'] == $id) {
$user = $item;
break;
}
}
return $user ?: null;
}
protected function getUserByUsername($username)
{
$user = [];
foreach ($this->getUsers() as $item) {
if ($item['email_address'] == $username) {
$user = $item;
break;
}
}
return $user ?: null;
}
/**
* The methods below need to be defined because of the Authenticatable contract
* but need no implementation for 'Auth::attempt' to work and can be implemented
* if you need their functionality
*/
public function retrieveByToken($identifier, $token) { }
public function updateRememberToken(UserContract $user, $token) { }
}
app/CoreExtensions/SessionGuardExtended.php
Sources:
<?php
namespace AppCoreExtensions;
use IlluminateAuthSessionGuard;
use IlluminateContractsAuthAuthenticatable;
/**
* Extended SessionGuard() functionality
* Provides added functionality to store the OAuth tokens in the session for later use
*
* @category guards
*
* @see https://stackoverflow.com/questions/36087061/extending-laravel-5-2-sessionguard
*/
class SessionGuardExtended extends SessionGuard
{
/**
* Log a user into the application.
*
* @param IlluminateContractsAuthAuthenticatable $p_oUser
* @param bool $p_remember
* @return void
*/
public function login(Authenticatable $p_oUser, $p_remember = false)
{
parent::login($p_oUser, $p_remember);
/**
* Writing the OAuth tokens to the session
*/
$key = 'authtokens';
$this->session->put(
$key,
[
'access_token' => $p_oUser->getAccessToken(),
'refresh_token' => $p_oUser->getRefreshToken(),
]
);
}
/**
* Log the user out of the application.
*
* @return void
*/
public function logout()
{
parent::logout();
/**
* Deleting the OAuth tokens from the session
*/
$this->session->forget('authtokens');
}
}
app/ApiUser
Sources:
<?php
namespace App;
use IlluminateAuthGenericUser;
use IlluminateContractsAuthAuthenticatable as UserContract;
class ApiUser extends GenericUser implements UserContract
{
/**
* Returns the OAuth access_token
*
* @return mixed
*/
public function getAccessToken()
{
return $this->attributes['access_token'];
}
public function getRefreshToken()
{
return $this->attributes['refresh_token'];
}
}
app/Providers/AuthServiceProvider.php
<?php
namespace AppProviders;
use IlluminateSupportFacadesAuth;
use IlluminateFoundationSupportProvidersAuthServiceProvider as ServiceProvider;
class AuthServiceProvider extends ServiceProvider
{
/**
* Register any authentication / authorization services.
*
* @return void
*/
public function boot()
{
$this->registerPolicies();
Auth::provider('frank_sinatra', function ($app, array $config) {
// Return an instance of IlluminateContractsAuthUserProvider...