Login with LinkedIn using JavaScript and PHP

Do you want to integrate login with LinkedIn using JavaScript in your application? When we use JavaScript for social login, your users won’t leave your website. Instead, it will open a popup where the user can login to their LinkedIn account. It will give a much better user experience on the website.

When I decided to write this article, I didn’t find any JavaScript SDK library on LinkedIn documentation. What I found from official documentation is a resource for Authorization code flow. LinkedIn follows OAuth 2.0 for Authorization. And to integrate Linkedin login, I had to fit this OAuth flow in the popup window.

Following this documentation, after spending a few hours I finally was able to build an OAuth flow with JavaScript and PHP. The server-side language is used to fetch the access token, get the user profile from LinkedIn, and insert them into the database.

That being said, let’s study integrating Login with LinkedIn with JavaScript and PHP.

Create a LinkedIn Application

To add the Sign in with a LinkedIn feature on the web application, you first need to register the application on your LinkedIn account. After creating the application, LinkedIn provides us with a client id and a client secret which are required to integrate the OAuth 2.0 flow.

  • Go to LinkedIn Developer Network.
  • Click on the ‘Create Application’ button.
  • Complete the basic information on the form.
  • Add YOUR_DOMAIN_URL/login.php in the redirect URLs field.
  • Copy the Client ID and Client Secret keys.

For the sake of the tutorial, I am using a local server and I set the redirect URL as http://localhost/linkedin/login.php. You can adjust this URL as per your requirement.

linkedin-app-js

Next, click on the ‘Products’ tab. Choose the ‘Sign In with LinkedIn’ from the list of available products. Upon selecting this product, it will go for the review and then be included as an added product. This may take some time for review. In my case, it took around 10 minutes.

add-linkedin-product

Database Configuration

We must store the user details in the database to complete the login process. Create the users table using the below SQL.

CREATE TABLE `users` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `in_uid` varchar(255) NOT NULL,
  `name` varchar(255) NOT NULL,
  `email` varchar(255) NOT NULL,
  `picture` text NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

Now, to perform a few operations like insert, update, and get user information against the database, I’ll create a class-db.php file and write a code below to it.

<?php
class DB {
    private $dbHost     = "DB_HOST";
    private $dbUsername = "DB_USERNAME";
    private $dbPassword = "DB_PASSWORD";
    private $dbName     = "DB_NAME";
 
    public function __construct(){
        if(!isset($this->db)){
            // Connect to the database
            $conn = new mysqli($this->dbHost, $this->dbUsername, $this->dbPassword, $this->dbName);
            if($conn->connect_error){
                die("Failed to connect with MySQL: " . $conn->connect_error);
            }else{
                $this->db = $conn;
            }
        }
    }
 
    public function get_user($id) {
        $sql = $this->db->query("SELECT * FROM users WHERE in_uid = '$id'");
        return $sql->fetch_assoc();
    }
 
    public function upsert_user($arr_data = array()) {
        $uid = $arr_data['id'];
        $name = $arr_data['name'];
        $email = $arr_data['email'];
        $picture = $arr_data['picture'];
 
        // check if user exists by fetching it's details
        $user = $this->get_user($uid);
 
        if(!$user) {
            // insert the user
            $this->db->query("INSERT INTO users(in_uid, name, email, picture) VALUES('$uid', '$name', '$email', '$picture')");
        } else {
            // update the user
            $this->db->query("UPDATE users SET name = '$name', email = '$email', picture = '$picture' WHERE in_uid = '$uid'");
        }
    }
}

Replace the placeholders with their actual values. We’ll use these methods in the later steps.

Login with LinkedIn using JavaScript

As I stated earlier, we have to build the OAuth flow in the popup window with JavaScript. To open the URL in the new browser window, a method called window.open is available. To this method, I’ll pass the Authorization URL along with the required parameters – client_id, scope, redirect_uri, and state.

The state parameter has a unique string value to avoid CSRF attacks. When a user completes the authorization, LinkedIn returns this state parameter as it is. You should then check this value with the previous one. Both values must be the same. I’ll manage the state value using the sessionStorage property.

Once a user is authorized, LinkedIn redirects the user to the login.php URL along with the Authorization Code. This code will be sent to the server-side PHP script via Ajax request. In this script, we will do the following stuff.

  • Exchange Authorization Code for an Access Token.
  • Call the LinkedIn API and get user details like name, email, profile picture, and LinkedIn profile ID.
  • Store user details into the database.
  • Set session of a user.

Create a login.php file and add the code below which integrate LinkedIn login with JavaScript.

<?php
session_start();
 
// logged in user shouldn't access this page
if(isset($_SESSION['uid'])) {
    header('Location: profile.php');
}
?>

<div id="loginContainer">
    <a href='#' onClick='login(event);'>Click here to login</a>
</div>

<script>
var client_id = "CLIENT_ID_HERE";
var redirect_uri = "http://localhost/linkedin/login.php"; // pass redirect_uri here
var scope = "r_liteprofile r_emailaddress"; // permissions required by end-user
var win;

function login(e) {
    e.preventDefault();

    if(!sessionStorage.inState) {
        sessionStorage.inState = inState = Math.floor(Math.random()*90000) + 10000;
    } else {
        inState = sessionStorage.inState;
    }
    var url = "https://www.linkedin.com/oauth/v2/authorization?response_type=code&client_id="+client_id+"&redirect_uri="+redirect_uri+"&scope="+scope+"&state="+inState;

    win = window.open(encodeURI(url), "LinkedIn Login", 'width=800, height=600, left=300, top=100');
    checkConnect = setInterval(function() {
        if (!win || !win.closed) return;
        clearInterval(checkConnect);
        // redirect to profile page
        location.href = 'http://localhost/linkedin/profile.php';
    }, 100);
}

var cur_url = new URL(window.location.href);
var urlParams = new URLSearchParams(cur_url.search);

if(urlParams.has('state') && (sessionStorage.inState == urlParams.get('state'))) {

    if(urlParams.has('code')) {
        document.getElementById("loginContainer").innerHTML = "Logging you in...";

        var code = urlParams.get('code');

        // send ajax request
        var xhttp = new XMLHttpRequest();
        xhttp.onreadystatechange = function() {
            if (this.readyState == 4 && this.status == 200) {
                if('success' == this.responseText) {
                    // close window
                    parent.close();
                }
            }
        };
        xhttp.open("POST", "save-user.php", true);
        xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
        xhttp.send("code=" + code);
    } else if(urlParams.has('error') && urlParams.has('error_description')) {
        // close window
        parent.close();
    }
}
</script>

When a user clicks the login, we initiate the OAuth process in the browser window. Upon successful authorization, we send the Authorization code received from LinkedIn to the save-user.php file. When we receive a ‘success’ response from the server, we close the window and redirect the user to the profile.php URL. The popup will be closed even if the user cancels the Authorization.

Let’s create these save-user.php and profile.php files and write the appropriate code to it.

Store User Details in the Database (save-user.php)

On the server side, we need to fetch an access token by exchanging the Authorization code. The access token is required to hit the LinkedIn API endpoints. We’ll call the API endpoints to get the user profile and email address. These details are then stored in the database.

The save-user.php file will handle all this stuff by sending the HTTP requests to the LinkedIn REST API.

To send HTTP requests, the Guzzle library can be used which makes it easy to send HTTP requests and handle the response. Install this library using the command:

composer require guzzlehttp/guzzle

save-user.php

<?php
session_start();
 
require_once 'vendor/autoload.php';
require_once 'class-db.php';

$client = new GuzzleHttp\Client([
    'base_uri' => 'https://www.linkedin.com',
]);

// get access token
$response = $client->request('POST', '/oauth/v2/accessToken', [
    'form_params' => [
        "grant_type" => "authorization_code",
        "code" => $_POST['code'],
        "client_id" => "CLIENT_ID_HERE",
        "client_secret" => "CLIENT_SECRET_HERE",
        "redirect_uri" => "http://localhost/linkedin/login.php",
    ],
]);

$res = json_decode($response->getBody());
$token = $res->access_token;

// get user details
$client2 = new GuzzleHttp\Client([
    'base_uri' => 'https://api.linkedin.com',
]);

$fields = [
    'id',
    'firstName',
    'lastName',
    'profilePicture(displayImage~:playableStreams)',
];

$response2 = $client2->request('GET', '/v2/me', [
    "headers" => [
        "Authorization" => "Bearer ". $token
    ],
    "query" => [
        'projection' => '(' . implode(',', $fields) . ')',
    ]
]);

$res2 = json_decode($response2->getBody());

$picture = '';
foreach ($res2->profilePicture as $key=>$value) {
    if ('displayImage~' == $key) {
        $element = end($value->elements);
        if (!empty($element->identifiers)) {
            $picture = reset($element->identifiers)->identifier;
        }
    }
}

// get email address
$email = '';
$response3 = $client2->request('GET', '/v2/emailAddress', [
    "headers" => [
        "Authorization" => "Bearer ". $token
    ],
    'query' => [
        'q' => 'members',
        'projection' => '(elements*(handle~))',
    ]
]);

$res3 = json_decode($response3->getBody());

foreach ($res3->elements as $element) {
    foreach ($element as $key=>$value) {
        if ('handle~' == $key) {
            $email = $value->emailAddress;
        }
    }
}

// send user data to the database
$locale = $res2->firstName->preferredLocale->language . '_' . $res2->firstName->preferredLocale->country;
$in_data['name'] = $res2->firstName->localized->$locale . ' ' . $res2->lastName->localized->$locale;
$in_data['id'] = $res2->id;
$in_data['email'] = $email;
$in_data['picture'] = $picture;

$db = new DB();
$db->upsert_user($in_data);

// set user id in session aka log in the user
if(!isset($_SESSION['uid'])) {
    $_SESSION['uid'] = $in_data['id'];
}

echo 'success';

User Account Page (profile.php)

After completing the OAuth 2.0 flow, I am redirecting users to the profile.php URL. This page can be different in your case. On this page, I get the user details from the database and display the name. I also give the option to logout to clear the login session of a user and redirect back to the login page.

<?php
session_start();

// non-logged in user shouldn't access this page
if(!isset($_SESSION['uid'])) {
    header('Location: login.php');
}

// log out the user and redirect to login page
if(isset($_GET['action']) && ('logout' == $_GET['action'])) {
    unset($_SESSION['uid']);
    header('Location: login.php');
}

require_once 'class-db.php';
$db = new DB();
$user = $db->get_user($_SESSION['uid']);

echo "Welcome ". $user['name'];
echo "<p><a href='profile.php?action=logout'>Log out</a></p>";

Conclusion

In this tutorial, we study how to integrate LinkedIn login with JavaScript and PHP on your website. We build the OAuth 2.0 flow in the window popup so our users won’t leave the website to complete the Authorization. This article should help you to add a sign-in with the LinkedIn option on your website.

Related Articles

If you liked this article, then please subscribe to our YouTube Channel for video tutorials.

Leave a Reply

Your email address will not be published.