Issue with LinkedIn openID SSO

27 views Asked by At

I am trying to implement the Sign In with LinkedIn using OpenID API on my website (built under Wordpress). So I am trying to get member profile informations for scope: openid, email, profile (this is the scope I have access to in my Linkedin app).

So far, I managed to get an access token and the necessary scope. This is the information I exchange with LinkedIn, after sending the request:

array(5) {
["access_token"]=>
string(350) "[...]"
["expires_in"]=>
int(5183999)
["scope"]=>
string(20) "email,openid,profile"
["token_type"]=>
string(6) "Bearer"
["id_token"]=>
string(1391) "[...]"

However, at the end, I get the following answer:

array(3) {
["serviceErrorCode"]=>
int(100)
["message"]=>
string(41) "Not enough permissions to access: GET /me"
["status"]=>
int(403)
}

Any idea on how to handle that?

Thanks for your attention, Marc

What I tried is the following (sorry it is the full code, a bit long):

function linkedin_login_button_shortcode() {
    // LinkedIn App credentials
    $client_id = '[...]';
    $client_secret = '[...]';
    $redirect_uri = '[...]';

    // Generate and store a unique state parameter
    $state = bin2hex(random_bytes(16));
    $_SESSION['linkedin_state'] = $state; // Store it in the session

    // LinkedIn scopes
    $scopes = 'openid email profile';

    // Check if the authorization code is present in the URL
    if (isset($_GET['code'])) {
        // Authorization code is present, proceed with token exchange
        $authorization_code = sanitize_text_field($_GET['code']);

        // Verify the state parameter to prevent CSRF attacks
        $state = sanitize_text_field($_GET['state']);
        // Verify $state against the stored state value from the initial request
        if ($state !== $_SESSION['linkedin_state']) {
            // Invalid state, log the error to the browser console
            echo '<script>console.error("Invalid state parameter: ' . $state . '");</script>';
            exit;
        }

        // Log the authorization code to the browser console
        echo '<script>console.log("Authorization code received: ' . $authorization_code . '");           </script>';

        // LinkedIn token exchange endpoint
        $token_url = 'https://www.linkedin.com/oauth/v2/accessToken';

        // Prepare token exchange request
        $token_params = array(
            'grant_type'    => 'authorization_code',
            'code'          => $authorization_code,
            'redirect_uri'  => $redirect_uri,
            'client_id'     => $client_id,
            'client_secret' => $client_secret,
        );

        // Perform a POST request to exchange the authorization code for tokens
        $response = wp_remote_post($token_url, array('body' => $token_params));
        $body = wp_remote_retrieve_body($response);
        $tokens = json_decode($body, true);

        // Check if tokens were successfully obtained
        if (isset($tokens['access_token']) && isset($tokens['id_token'])) {
            // Tokens obtained, now decode the ID token
            $id_token = $tokens['id_token'];
            $decoded_id_token = jwt_decode($id_token);

            // Extract user information
            $linkedin_user_id = $decoded_id_token->sub;
            $first_name = $decoded_id_token->given_name;
            $last_name = $decoded_id_token->family_name;
            $email = $decoded_id_token->email;

            // Now, you can use the user information as needed (e.g., create or log in the user)
            // ...

            // Redirect the user to a desired page (e.g., home page) after successful login
            wp_redirect(home_url('/'));
            exit;

        } else {
            // Handle the case where tokens were not obtained successfully
            echo 'Error obtaining tokens from LinkedIn.';
        }
     }

    // LinkedIn authorization URL
    $linkedin_login_url = 'https://www.linkedin.com/oauth/v2/authorization' .
        '?response_type=code' .
        '&client_id=' . $client_id .
        '&redirect_uri=' . $redirect_uri .
        '&state=' . $state .
        '&scope=' . $scopes;

    // HTML/PHP code for the button
    $button_html = '<a href="' . esc_url($linkedin_login_url) . '">
                        <button>Login with LinkedIn</button>
                    </a>';

    return $button_html;
    }

    // Register the shortcode
    add_shortcode('linkedin_login_button', 'linkedin_login_button_shortcode');

    function exchange_linkedin_code_for_tokens($authorization_code) {
    // LinkedIn App credentials
    $client_id = '[...]';
    $client_secret = '[...]';
    $redirect_uri = '[...]';

    // LinkedIn token exchange endpoint
    $token_url = 'https://www.linkedin.com/oauth/v2/accessToken';

    // Prepare token exchange request
    $token_params = array(
        'grant_type'    => 'authorization_code',
        'code'          => $authorization_code,
        'redirect_uri'  => $redirect_uri,
        'client_id'     => $client_id,
        'client_secret' => $client_secret,
    );

    // Perform a POST request to exchange the authorization code for tokens
    $response = wp_remote_post($token_url, array('body' => $token_params));
    $body = wp_remote_retrieve_body($response);

    // Check if the request was successful
    if (is_wp_error($response)) {
        // Handle the error (log it, display a message, etc.)
        return false;
    }

    // Decode the response body
    $tokens = json_decode($body, true);

    return $tokens;
    }

    function get_linkedin_profile($access_token) {
    // LinkedIn profile endpoint
    $profile_url = 'https://api.linkedin.com/v2/me';

    // Prepare the request headers
    $headers = array(
        'Authorization' => 'Bearer ' . $access_token,
        'Connection'    => 'Keep-Alive',
    );

    // Perform a GET request to retrieve the LinkedIn profile
    $response = wp_remote_get($profile_url, array('headers' => $headers));
    $body = wp_remote_retrieve_body($response);

    // Check if the request was successful
    if (is_wp_error($response)) {
        // Handle the error (log it, display a message, etc.)
        return false;
    }

    // Decode the response body
    $profile_data = json_decode($body, true);

    return $profile_data;
    }

    function get_user_by_linkedin_id($linkedin_user_id) {
    // Check if a user with the given LinkedIn ID exists
    $user_query = new WP_User_Query(array(
        'meta_key'    => 'linkedin_user_id',
        'meta_value'  => $linkedin_user_id,
        'number'      => 1, // Limit the query to 1 result
    ));

    $users = $user_query->get_results();

    if ($users) {
        // Return the first user found (you might need to handle multiple matches differently)
        return $users[0]->ID;
    }

    // No user found with the given LinkedIn ID
    return false;
    }

    function create_user_from_linkedin_profile($linkedin_profile) {
    // Extract relevant information from the LinkedIn profile
    $linkedin_user_id = $linkedin_profile['id'];
    $linkedin_email = $linkedin_profile['email'];
    $first_name = $linkedin_profile['first_name'];
    $last_name = $linkedin_profile['last_name'];

    // Generate a unique username based on LinkedIn user ID
    $username = 'linkedin_user_' . $linkedin_user_id;

    // Create a new user
    $user_id = wp_create_user($username, wp_generate_password(), $linkedin_email);

    if (!is_wp_error($user_id)) {
        // Update user meta with additional LinkedIn information
        update_user_meta($user_id, 'linkedin_user_id', $linkedin_user_id);
        update_user_meta($user_id, 'first_name', $first_name);
        update_user_meta($user_id, 'last_name', $last_name);

        // You can add more fields or customize this based on your needs

        return $user_id;
    } else {
        // Handle the error (you might want to log it or display a message to the user)
        return false;
    }
    }

    // Hook into the user registration process after LinkedIn login
    add_action('linkedin_login_after_register', 'store_linkedin_profile_info', 10, 2);

    function store_linkedin_profile_info($user_id, $linkedin_user_info) {
    // Store LinkedIn profile picture URL in user meta
    update_user_meta($user_id, 'linkedin_profile_picture',      $linkedin_user_info['profile_picture']);
    }

    // Check if the user is logged in
    if (is_user_logged_in()) {
    // Retrieve stored LinkedIn profile information
    $linkedin_profile_picture = get_user_meta(get_current_user_id(), 'linkedin_profile_picture',     true);

    // Check if the profile picture URL is available
    if ($linkedin_profile_picture) {
        echo '<div class="linkedin-profile-picture">';
        echo '<img src="' . esc_url($linkedin_profile_picture) . '" alt="LinkedIn Profile  Picture">';
        echo '</div>';
    }
    }

    // Check if the user clicked "Sign in with LinkedIn"
    if (isset($_GET['linkedin_login'])) {
    // Initiate LinkedIn OAuth flow
    $linkedin_oauth_url = get_linkedin_oauth_url();
    wp_redirect($linkedin_oauth_url);
    exit;
    }

    // Check if the LinkedIn callback is being processed
    if (isset($_GET['code'])) {
    // Exchange the authorization code for access tokens
    $linkedin_tokens = exchange_linkedin_code_for_tokens($_GET['code']);
    echo '<pre>';
    var_dump($linkedin_tokens);
    echo '</pre>';

    // Retrieve LinkedIn profile information
    $linkedin_profile = get_linkedin_profile($linkedin_tokens['access_token']);
    // Display LinkedIn profile information for debugging
    echo '<pre>';
    var_dump($linkedin_profile);
    echo '</pre>';

    // Check if the user exists
    $user_id = get_user_by_linkedin_id($linkedin_profile['id']);
    if (!$user_id) {
        // User doesn't exist, create a new account
        $user_id = create_user_from_linkedin_profile($linkedin_profile);
    }

    // Log the user in and update the session
    wp_set_auth_cookie($user_id, true);
    do_action('wp_login', get_user_by('id', $user_id)->user_login);

    // Redirect the user to the desired page (e.g., homepage)
    wp_redirect(home_url('/'));
    exit;
    }
0

There are 0 answers