
LinkedIn OAuth Authentication in Laravel Without Socialite
In this guide, we’ll learn the process of integrating LinkedIn authentication into a Laravel 10 without using any third-party package like Laravel Socialite. We'll rely solely on LinkedIn API’s endpoints to create authentication from scratch. By the end of this tutorial, you’ll have a Laravel app where users can log in using their LinkedIn credentials. This setup is ideal for scenarios where you need more control over the authentication process or want to avoid extra dependencies.
Now goto your routes/web.php
Setting Up a Laravel Project
To get started, create a fresh Laravel project. Open your terminal and run the following command:composer create-project laravel/laravel linkedin-integration
After creating the project, we’ll need to install Laravel UI for authentication scaffolding. Laravel UI provides simple authentication scaffolding to get started quickly.
composer require laravel/ui
php artisan ui bootstrap –auth
npm install
Finally, don’t forget to configure your database connection in the .env file. For this tutorial, name your database social-integration.
DB_DATABASE=socialIntegration
Integrate LinkedIn Login
To integrate LinkedIn login, you'll need a LinkedIn Developer Account. Follow these steps:
- Visit LinkedIn Developer Portal.
- Click on "Create App".
- Fill out the required fields and set up your app.
- Once created, navigate to "Auth" settings, and you'll find your Client ID and Client Secret.
-
Set your redirect URI to match the one in your Laravel app, for example:
http://127.0.0.1:8000/auth/linkedin/callback
. -
Now, update your
.env
file with the LinkedIn API credentials:LINKEDIN_CLIENT_ID=your_client_id
LINKEDIN_CLIENT_SECRET=your_client_secret
LINKEDIN_REDIRECT_URI=http://127.0.0.1:8000/auth/linkedin/callback
DB_DATABASELINKEDIN_CLIENT_ID=77zxxxxxxxxxi7k
LINKEDIN_CLIENT_SECRET=NzqxxxxxxxxdQGJ
LINKEDIN_REDIRECT_URI="http://127.0.0.1:8000/auth/linkedin/callback"
Updating the Migration for LinkedIn Data
Next, we need to update the users table to store the linkedin_id and picture_url fields. This allows us to track which users logged in through LinkedIn and store their profile pictures Update your users migration like this:Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('linkedin_id')->nullable();
$table->string('picture_url')->nullable();
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->rememberToken();
$table->timestamps();
});
Updating the User Model
protected $fillable = [ 'name', 'email', 'linkedin_id', 'picture_url'];
After updating the migration, run the following command to apply the changes to your database:
php artisan migrate
Creating the LinkedIn Controller
We now need to create a controller to handle the LinkedIn authentication logic. Run this Artisan command to create a new controller:php artisan make:controller LinkedinController
namespace App\Http\Controllers;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Http;
class LinkedinController extends Controller
{
public function redirectToLinkedin()
{
$clientId = config('services.linkedin.client_id');
$redirectUri = config('services.linkedin.redirect_uri');
$scope = 'openid%20profile%20email';
$response_type = 'code';
// $state = bin2hex(random_bytes(16));
// session(['linkedin_state' => $state]); //optional
// if you are creating state then you also need to pass in url
return redirect("https://www.linkedin.com/oauth/v2/authorization?response_type=$response_type&client_id=$clientId&redirect_uri=$redirectUri&scope=$scope");
}
public function handlelinkedinCallback(Request $request)
{
// $state = $request->query('state');
// Verify the state parameter to prevent CSRF attacks
// if (!$state || $state !== $request->session()->pull('linkedin_state')) {
// return response()->json(['error' => 'Invalid state'], 400);
// }
$code = $request->query('code');
if (empty($code)) {
return response()->json(['error' => 'Authorization code not provided'], 400);
}
$clientId = config('services.linkedin.client_id');
$clientSecret = config('services.linkedin.client_secret');
$redirectUri = config('services.linkedin.redirect_uri');
$response = Http::asForm()->post('https://www.linkedin.com/oauth/v2/accessToken', [
'code' => $code,
'client_id' => $clientId,
'client_secret' => $clientSecret,
'redirect_uri' => $redirectUri,
'grant_type' => 'authorization_code',
]);
$accessToken = $response->json('access_token');
$userData = $this->getUserData($accessToken);
$user = $this->findOrCreateUser($userData);
$userRecord = User::where('email', $user['email'])->where('linkedin_id', $user['linkedin_id'])->first();
if ($userRecord) {
Auth::login($userRecord);
return redirect('/'); //Redirect the user where you want
} else {
return redirect()->route('login');
}
}
private function getUserData($accessToken)
{
$response = Http::withHeaders([
'Authorization' => 'Bearer ' . $accessToken,
])->get('https://api.linkedin.com/v2/userinfo');
return $response->json();
}
private function findOrCreateUser($userData)
{
$user = User::where('email', $userData['email'])->first();
if (!$user) {
// Create a new user if not exists in database
$user = User::create([
'name' => $userData['name'],
'email' => $userData['email'],
'linkedin_id' => $userData['sub'],
'picture_url' => $userData['picture'],
]);
}
return $user;
}
}
LinkedIn Login Functionality
Below are the key functionalities involved in LinkedIn Login integration:
- redirectToLinkedin: Redirects the user to LinkedIn's OAuth authorization page.
- handleLinkedinCallback: Handles the callback after LinkedIn’s authorization. It exchanges the authorization code for an access token and uses this token to fetch user data from LinkedIn.
- getUserData: Fetches user data from LinkedIn using the access token.
- findOrCreateUser: Finds an existing user or creates a new one based on the LinkedIn profile information.
Auth::routes();
Route::get('/home', [App\Http\Controllers\HomeController::class, 'index'])->name('home');
Route::get('auth/linkedin', [LinkedinController::class, 'redirectToLinkedin']);
Route::get('auth/linkedin/callback', [LinkedinController::class, 'handlelinkedinCallback']);
Lets step up our views.Now go to resources/views/auth/login.blade.php
<div class="row mb-0">
<div class="col-md-8 offset-md-4">
<a href="{{ url('auth/linkedin') }}" class="btn btn-primary">Login with Linkedin</a>
<button type="submit" class="btn btn-primary">
{{ __('Login') }}
</button>
@if (Route::has('password.request'))
<a class="btn btn-link" href="{{ route('password.request') }}">
{{ __('Forgot Your Password?') }}
</a>
@endif
</div>
</div>
Running the Application
You’re almost done! Open two terminals and run the following commands://In the first terminal, start the Laravel development server:
php artisan serve
//In the second terminal, compile your frontend assets:
npm run dev
Once the server is running, navigate to http://127.0.0.1:8000 and try logging in with LinkedIn.
![alt!]()
![alt!]()
![alt!]()