Android SSO through LinkedIn OAuth 2

The following are the steps for doing Single Sign On on your Android app using LinkedIn OAuth 2 authentication.

1. Create a project in Android Studio, choose the empty activity as a starting point.

2. Download the LinkedIn Android SDK

3. Unzip the android sdk, copy and paste the folder linkedin-sdk from the unzipped linkedin sdk folder to your android project root folder, it should be at the same directory where the app folder lives.

4. Update the settings.gradle file in Android Studio to the following.

include ':app', ':linkedin-sdk'

5. Add this in the dependencies tag in the app build.gradle file.

compile project(':linkedin-sdk')

6. Generate a debug hashkey on terminal, when prompted for password, enter android as it is the default password for default keystore file.

keytool -exportcert -keystore ~/.android/debug.keystore -alias androiddebugkey | openssl sha1 -binary | openssl base64

7. Create an application on LinkedIn developer portal and put your android project package name and the hashkey for the application.

8. The MainActivity.java, in the onCreate method, it first checks if the LinkedIn session is still valid, if it is not valid, launch the login screen and destroy the MainActivity.

import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import com.linkedin.platform.APIHelper;
import com.linkedin.platform.LISessionManager;
import com.linkedin.platform.errors.LIApiError;
import com.linkedin.platform.listeners.ApiListener;
import com.linkedin.platform.listeners.ApiResponse;
import com.squareup.picasso.Picasso;

import org.json.JSONException;
import org.json.JSONObject;
public class MainActivity extends AppCompatActivity {

    private ImageView ivProfileImage;
    private TextView tvName, tvHeadline;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if (!LISessionManager.getInstance(getApplicationContext()).getSession().isValid()) {
            startActivity(new Intent(this, LoginActivity.class));
            finish();
        }

        setContentView(R.layout.activity_main);
        ivProfileImage = (ImageView) findViewById(R.id.iv_profile_image);
        tvName = (TextView) findViewById(R.id.tv_name);
        tvHeadline = (TextView) findViewById(R.id.tv_headline);

        loadLinkedInProfileData();
    }


    private void loadLinkedInProfileData() {
        String url = "https://api.linkedin.com/v1/people/~:(id,first-name,last-name,headline,formatted-name,email-address,picture-url)";

        APIHelper apiHelper = APIHelper.getInstance(getApplicationContext());
        apiHelper.getRequest(this, url, new ApiListener() {
            @Override
            public void onApiSuccess(ApiResponse apiResponse) {
                JSONObject jsonObject = apiResponse.getResponseDataAsJson();
                displayProfileData(jsonObject);
            }

            @Override
            public void onApiError(LIApiError liApiError) {
                liApiError.printStackTrace();
                Toast.makeText(getApplicationContext(), "Error: " + liApiError.toString(), Toast.LENGTH_SHORT).show();
            }
        });
    }

    private void displayProfileData(JSONObject jsonObject) {
        try {
            tvName.setText(jsonObject.get("formattedName").toString());
            tvHeadline.setText(jsonObject.getString("headline"));
            Picasso.with(getApplicationContext()).load(jsonObject.getString("pictureUrl")).into(ivProfileImage);
        } catch (JSONException e) {
            e.printStackTrace();
        }

    }
}

9. The LoginActivity.java

import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Toast;

import com.linkedin.platform.LISessionManager;
import com.linkedin.platform.errors.LIAuthError;
import com.linkedin.platform.listeners.AuthListener;
import com.linkedin.platform.utils.Scope;

public class LoginActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        // Handle responses from the LinkedIn mobile app
        LISessionManager.getInstance(getApplicationContext()).onActivityResult(this, requestCode, resultCode, data);
    }

    public void linkedinLogin(View view) {
        LISessionManager.getInstance(getApplicationContext()).init(this, buildScope(), new AuthListener() {
            @Override
            public void onAuthSuccess() {
                String token = LISessionManager.getInstance(getApplicationContext()).getSession().getAccessToken().toString();
                Toast.makeText(getApplicationContext(), "Auth Success, Token: " + token, Toast.LENGTH_SHORT).show();
                startActivity(new Intent(LoginActivity.this, MainActivity.class));
                finish();
            }

            @Override
            public void onAuthError(LIAuthError error) {
                Toast.makeText(getApplicationContext(), "Auth Failed, Token: " + error.toString(), Toast.LENGTH_SHORT).show();
            }
        }, true);
    }

    // Build the list of member permissions our LinkedIn session requires
    private static Scope buildScope() {
        return Scope.build(Scope.R_BASICPROFILE, Scope.W_SHARE);
    }
}

10. The activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical">

    <ImageView
        android:id="@+id/iv_profile_image"
        android:layout_width="100dp"
        android:layout_height="100dp"/>

    <TextView
        android:id="@+id/tv_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text=""/>

    <TextView
        android:id="@+id/tv_headline"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="" />

</LinearLayout>

11. The activity_login.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.linkedinsignin.MainActivity">

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@drawable/linkedin_signin"
        android:onClick="linkedinLogin"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

12. Don’t forget to register the activities in the mainfiest file.

<activity android:name=".MainActivity">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />

        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

<activity android:name=".LoginActivity"/>

13. The image loader dependency

// Image Loader
compile 'com.squareup.picasso:picasso:2.5.2'

References:
https://developer.linkedin.com/docs/android-sdk
https://developer.linkedin.com/docs/android-sdk-auth
https://developer.linkedin.com/downloads#androidsdk

Search within Codexpedia

Custom Search

Search the entire web

Custom Search