Laravel Authentication
Laravel is a popular open-source PHP web application framework designed to simplify and accelerate web development. It provides a robust set of tools and features to help developers build web applications with clean, maintainable, and scalable code.
PropelAuth seamlessly integrates with Laravel via Socialite and PropelAuth's OAuth2 Support, offering developers an efficient way to implement secure and customizable authentication.
Here is more information on the PropelAuth Socialite Provider.
Setup in PropelAuth
We first need to create an OAuth2 provider within PropelAuth. To do so, navigate to the Frontend Integration page in your PropelAuth dashboard, click on Advanced Settings then Edit OAuth Config.
Create a New OAuth Client and copy down your Client ID and Client Secret. We'll be needing these for later.
While we're here, let's also add a Redirect URI. In this guide we'll be using http://localhost:8000/auth/callback
.
The last variable we need is our Auth URL. You can find this back in the Frontend Integration page.
Installation
Start by installing the PropelAuth Socialite provider in your Laravel project:
composer require socialiteproviders/propelauth
Let's now add some environment variables to your .env
. All four of these variables were retrieved in the previous step.
PROPELAUTH_CLIENT_ID=0166...
PROPELAUTH_CLIENT_SECRET=f92ac31...
PROPELAUTH_CALLBACK_URL=http://localhost:8000/auth/callback
PROPELAUTH_AUTH_URL=https://auth.example.com
Next, head over to config/services.php
and add the PropelAuth provider.
'propelauth' => [
'client_id' => env('PROPELAUTH_CLIENT_ID'),
'client_secret' => env('PROPELAUTH_CLIENT_SECRET'),
'redirect' => env('PROPELAUTH_CALLBACK_URL'),
'auth_url' => env('PROPELAUTH_AUTH_URL'),
],
We now need to register an event listener to extend Socialite with the PropelAuth provider. This will allow us to authenticate users via PropelAuth in the same ways you can with any other Socialite provider.
// /app/Providers/AppServiceProvider.php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Event;
class AppServiceProvider extends ServiceProvider
{
public function register(): void
{
}
public function boot(): void
{
Event::listen(function (\SocialiteProviders\Manager\SocialiteWasCalled $event) {
$event->extendSocialite('propelauth', \SocialiteProviders\PropelAuth\Provider::class);
});
}
}
Routing
To authenticate users using PropelAuth's OAuth2 provider we first need to create two routes. The first is for redirecting unauthenticated users to login and the other is for handling the OAuth callback after authentication.
The /auth/callback
endpoint is also responsible for saving the user to your database or saving the user's information in the session. This part is up to you, but in this example we'll be saving the user's data using Laravel's HTTP Session. For alternative methods see the Laravel docs here.
If you would like to customize how user information is mapped in the /auth/callback
route see the User Array for more information on what user information is available.
use Illuminate\Support\Facades\Route;
use Laravel\Socialite\Facades\Socialite;
Route::get('/auth/redirect', function () {
return Socialite::driver('propelauth')->redirect();
});
Route::get('/auth/callback', function () {
$user = Socialite::driver('propelauth')->user();
session(['refresh_token' => $user->refreshToken]);
session(['access_token' => $user->token]);
session(['user' => $user->getRaw()]);
return redirect('/');
});
In the example above we also store the user's refresh_token
and access_token
. We'll be needing these later to refresh the user's information and successfully log the user out of your app.
Protecting Routes and Displaying User information
Since we have stored the user's information in the session we can run a quick check if the user data is saved. If it is we can pass it along to the view. Otherwise, redirect the user to login via the /auth/redirect
route.
Route::get('/', function () {
$user = session('user');
if (!$user) {
return redirect('/auth/redirect');
}
return view('welcome', ['user' => $user]);
});
Refreshing User information
If you want the most up to date user information you can use Socialite's userFromToken method. This method automatically makes a request to the User Info Endpoint for you, providing you with a new and up to date User Array.
$accessToken = session('access_token');
$user = Socialite::driver('propelauth')->userFromToken($accessToken);
session(['user' => $user->getRaw()]);
PropelAuth's access_token
is valid for a total of 30 minutes, so the userFromToken
will eventually return a 401 error if it's not refreshed periodically. To do so, you can make a request to the Refresh Token Endpoint to retrieve a new access and refresh token.
use Illuminate\Support\Facades\Http;
use Laravel\Socialite\Facades\Socialite;
$accessToken = session('access_token');
try {
// Attempt to fetch the user info with the current access token
$user = Socialite::driver('propelauth')->userFromToken($accessToken);
session(['user' => $user->getRaw()]);
} catch (\Exception $e) {
if ($e->getCode() !== 401) {
return redirect('/auth/redirect');
}
// Handle token refresh
$refreshToken = session('refresh_token');
$response = Http::asForm()->post(env('PROPELAUTH_AUTH_URL') . '/propelauth/oauth/token', [
'grant_type' => 'refresh_token',
'refresh_token' => $refreshToken,
'client_id' => env('PROPELAUTH_CLIENT_ID'),
]);
if (!$response->ok()) {
return redirect('/auth/redirect');
}
$data = $response->json();
// Update session with new tokens
session(['refresh_token' => $data['refresh_token']]);
session(['access_token' => $data['access_token']]);
// Retry fetching the user with the new access token
try {
$user = Socialite::driver('propelauth')->userFromToken($data['access_token']);
session(['user' => $user->getRaw()]);
} catch (\Exception $retryException) {
return redirect('/auth/redirect');
}
}
Authorization / Organizations
You can also verify which organizations the user is in, and which roles and permissions they have in each organization.
Check Org Membership
Verify that the request was made by a valid user and that the user is a member of the specified organization.
Route::get('/org/{orgId}', function ($orgId) {
$user = session('user');
if (!$user) {
return redirect('/auth/redirect');
}
if (
isset($user['org_id_to_org_info'][$orgId])
) {
return view('welcome', ['user' => $user]);
} else {
return "You do not belong to this org";
}
});
Check Org Membership and Role
Similar to checking org membership, but will also verify that the user has a specific Role in the organization.
A user has a Role within an organization. By default, the available roles are Owner, Admin, or Member, but these can be configured. These roles are also hierarchical, so Owner > Admin > Member.
Route::get('/org/{orgId}', function ($orgId) {
$user = session('user');
if (!$user) {
return redirect('/auth/redirect');
}
$roleToCheck = "Admin";
if (
isset($user['org_id_to_org_info'][$orgId]) &&
$user['org_id_to_org_info'][$orgId]['user_role'] === $roleToCheck
) {
return view('welcome', ['user' => $user]);
} else {
return "You must be an Admin to access this page";
}
});
Check Org Membership and Permission
Similar to checking org membership, but will also verify that the user has the specified permission in the organization.
Permissions are arbitrary strings associated with a role. For example, can_view_billing
, ProductA::CanCreate
, and ReadOnly
are all valid permissions. You can create these permissions in the PropelAuth dashboard.
Route::get('/org/{orgId}', function ($orgId) {
$user = session('user');
if (!$user) {
return redirect('/auth/redirect');
}
$permissionToCheck = "can_view_billing";
if (
isset($user['org_id_to_org_info'][$orgId]) &&
in_array(
$permissionToCheck,
$user['org_id_to_org_info'][$orgId]['user_permissions'] ?? []
)
) {
return view('welcome', ['user' => $user]);
} else {
return "You do not have the correct permissions to view this page";
}
});
Logging Out
Logging a user out of your application requires two steps. The first is using the refresh_token
we saved earlier to make a request to PropelAuth. This will invalidate the refresh_token
as well as log the user out of any hosted pages.
The second step depends on how you logged the user into your app previously. Since we have been using session storage in this guide, we'll delete the user information and refresh token from the session storage after making the request to PropelAuth.
This can all easily be done by creating a new route in your Laravel application and redirecting your users to it.
Route::get('/auth/logout', function () {
// get refresh token
$refresh_token = session('refresh_token');
// delete user and refresh token from session
session()->forget(['refresh_token', 'user']);
if (!$refresh_token) {
return redirect('/auth/redirect');
}
$response = Http::withHeaders([
'Content-Type' => 'application/json',
])->post(env('PROPELAUTH_AUTH_URL') . '/api/backend/v1/logout', [
'refresh_token' => $refresh_token,
]);
if ($response->failed()) {
return response()->json(['error' => 'Failed to log out'], 400);
}
return redirect('/');
});
User Array
By default, only the user's email
and id
are returned after logging in. However, you can get all the user information by retrieving the raw
user array:
$user = Socialite::driver('propelauth')->user();
$rawUser = $user->getRaw();
This returns:
Array
(
[can_create_orgs] => null,
[created_at] => 1730301670,
[email] => 'test@example.com',
[email_confirmed] => 1,
[enabled] => 1,
[first_name] => 'Buddy',
[last_name] => 'Framm',
[has_password] => 1,
[last_active_at] => 1733774618,
[locked] => null,
[mfa_enabled] => null,
[sub] => '31c41c16-c281-44ae-9602-8a047e3bf33d',
[update_password_required] => null,
[user_id] => '31c41c16-c281-44ae-9602-8a047e3bf33d',
[username] => 'airbud3',
[picture_url] => 'https://example.com/picture.jpg',
[properties] => Array
(
[favoriteSport] => 'Basketball',
),
[org_id_to_org_info] => Array
(
[1189c444-8a2d-4c41-8b4b-ae43ce79a492] => Array
(
[additional_roles] => Array(),
[inherited_user_roles_plus_current_role] => Array
(
[0] => 'Admin',
[1] => 'Member',
),
[org_id] => '1189c444-8a2d-4c41-8b4b-ae43ce79a492',
[org_metadata] => Array
(
[customKey] => 'customValue',
),
[org_name] => 'Acme Inc',
[org_role_structure] => 'single_role_in_hierarchy',
[url_safe_org_name] => 'acme-inc',
[user_permissions] => Array
(
[0] => 'propelauth::can_manage_api_keys',
[1] => 'can_edit_billing',
),
[user_role] => 'Admin',
),
),
)