How to implement “Login with LinkedIn” with “OAuth

2020-07-10 05:37发布

问题:

In OAuth1.0 "Login with Linkedin" Working fine but before few days Linkedin make some changes in their policy, please refer below link for more detail, https://engineering.linkedin.com/blog/2018/12/developer-program-updates

I also tried some GitHub example and other reference but unfortunately not work for me, I also tried below link but it did not fulfill the exact requirement.

Oauth 2.0 authorization for LinkedIn in Android

You can also refer below link, may it give some result, I also know Linkedin SDK not work here as they declared here, https://developer.linkedin.com/docs/android-sdk We have to call manual URL and open into Webview.

https://docs.microsoft.com/en-us/linkedin/consumer/integrations/self-serve/sign-in-with-linkedin?context=linkedin/consumer/context

I also check 3-4 apps which earlier had LinkedIn support for authentication but now they removed it but I check one app namely "Nuzzel" in that I found Linked in authentication and it's working fine so it means there is some way to do it properly. Nuzzel App Link:- https://play.google.com/store/apps/details?id=com.nuzzel.android&hl=en_GB

Thanks

回答1:

After a little effort I got it working, but implementing it every time you start a new project will waste a lot of time.

So I created a light weight library for the same.

Just add the dependency to your app level build.gradle file

dependencies {
    implementation 'com.shantanudeshmukh:linkedinsdk:1.0.0'
}

Initiate Login Request.

LinkedInBuilder.getInstance(MainActivity.this)
        .setClientID("<YOUR_CLIENT_ID_HERE>")
        .setClientSecret("<YOUR_CLIENT_SECRET_HERE>")
        .setRedirectURI("<YOUR_REDIRECT_URL_HERE>")
        .authenticate(LINKEDIN_REQUEST_CODE);

Handle the response:

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (requestCode == LINKEDIN_REQUEST_CODE && data != null) {
            if (resultCode == RESULT_OK) {
                //Successfully signed in
                LinkedInUser user = data.getParcelableExtra("social_login");

                //acessing user info
                Log.i("LinkedInLogin", user.getFirstName());

            } else {

                if (data.getIntExtra("err_code", 0) == LinkedInBuilder.ERROR_USER_DENIED) {
                    //Handle : user denied access to account

                } else if (data.getIntExtra("err_code", 0) == LinkedInBuilder.ERROR_FAILED) {

                    //Handle : Error in API : see logcat output for details
                    Log.e("LINKEDIN ERROR", data.getStringExtra("err_message"));
                }
            }
        }

    }

I'm using this library in multiple production apps, so I'll try to keep it as updated as possible. You can find other details here.



回答2:

I created a small library to implement LinkedIn Authentication via OAuth2

Library - https://github.com/Sumudu-Sahan/LinkedInManager

Steps

  1. Add the below maven dependency to your project level build.gradle file

    allprojects { repositories { ... maven { url 'https://jitpack.io' } } }

  2. add the below maven dependency to your app level build.gradle file

    dependencies { implementation 'com.github.Sumudu-Sahan:LinkedInManager:1.00.02' }

  3. Inherit your activity, fragment from LinkedInManagerResponse

    public class MainActivity extends AppCompatActivity implements LinkedInManagerResponse

  4. Initiate the LinkedInRequestManager object instance for login process

    LinkedInRequestManager linkedInRequestManager = new LinkedInRequestManager(Activity, LinkedInManagerResponse, "CLIENT ID", "CLIENT SECRET", "REDIRECTION URL");

  5. Start Authenticate with below statement

linkedInRequestManager.showAuthenticateView(LinkedInRequestManager.MODE_BOTH_OPTIONS);

Available Modes

LinkedInRequestManager.MODE_EMAIL_ADDRESS_ONLY
LinkedInRequestManager.MODE_LITE_PROFILE_ONLY
LinkedInRequestManager.MODE_BOTH_OPTIONS


回答3:

Here is my code, to implement sign in with "0Auth2.0"

class NewLinkedInIntegration extends Activity {

    private static final String API_KEY = "your client id";
    private static final String SECRET_KEY = "your secret key";
    private static final String STATE = "DCEeFWf45A53sdfKef424";

    private static final String REDIRECT_URI = "your url";
    private static final String AUTHORIZATION_URL = "https://www.linkedin.com/uas/oauth2/authorization";

    private static final String ACCESS_TOKEN_URL = "https://www.linkedin.com/uas/oauth2/accessToken";

    private static final String SECRET_KEY_PARAM = "client_secret";

    private static final String RESPONSE_TYPE_PARAM = "response_type";

    private static final String GRANT_TYPE_PARAM = "grant_type";

    private static final String GRANT_TYPE = "authorization_code";

    private static final String RESPONSE_TYPE_VALUE = "code";

    private static final String CLIENT_ID_PARAM = "client_id";

    private static final String STATE_PARAM = "state";
    private static final String REDIRECT_URI_PARAM = "redirect_uri";
    private static final String QUESTION_MARK = "?";
    private static final String AMPERSAND = "&";
    private static final String EQUALS = "=";

    String profileUrl = "https://api.linkedin.com/v2/me?projection=(id,firstName,lastName,profilePicture(displayImage~:playableStreams))";
    String accessToken;
    String linkedInUserEmailAddress;
    SharedPreferences sharedPreferences;
    String emailAddress = "https://api.linkedin.com/v2/emailAddress?q=members&projection=(elements*(handle~))";
    private WebView webView;
    TransparentDialog mProgressBarHandler;
    private ProgressDialog pd;
    String deviceId, location, country;
    String linkedInUserId, linkedInUserFirstName, linkedInUserLastName, linkedInUserProfile;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_linked);
        //get the webView from the layout
        webView = (WebView) findViewById(R.id.main_activity_web_view);
        deviceId = getIntent().getStringExtra("deviceId");
        location = getIntent().getStringExtra("location");
        country = getIntent().getStringExtra("country");
        //Request focus for the webview
        webView.requestFocus(View.FOCUS_DOWN);
        webView.clearHistory();
        webView.clearCache(true);
        sharedPreferences = MyApplication.preference;
        pd = ProgressDialog.show(this, "", "Loadingg...", true);
        webView.setWebViewClient(new WebViewClient() {
            @Override
            public void onPageFinished(WebView view, String url) {
                //This method will be executed each time a page finished loading.
                //The only we do is dismiss the progressDialog, in case we are showing any.
                if (pd != null && pd.isShowing()) {
                    pd.dismiss();
                }
            }

            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String authorizationUrl) {
                //This method will be called when the Auth proccess redirect to our RedirectUri.
                //We will check the url looking for our RedirectUri.
                if (authorizationUrl.startsWith(REDIRECT_URI)) {
                    Log.i("Authorize", "");
                    Uri uri = Uri.parse(authorizationUrl);
                    //We take from the url the authorizationToken and the state token. We have to check that the state token returned by the Service is the same we sent.
                    //If not, that means the request may be a result of CSRF and must be rejected.
                    String stateToken = uri.getQueryParameter(STATE_PARAM);
                    if (stateToken == null || !stateToken.equals(STATE)) {
                        Log.e("Authorize", "State token doesn't match");
                        return true;
                    }

                    //If the user doesn't allow authorization to our application, the authorizationToken Will be null.
                    String authorizationToken = uri.getQueryParameter(RESPONSE_TYPE_VALUE);
                    if (authorizationToken == null) {
                        Log.i("Authorize", "The user doesn't allow authorization.");
                        return true;
                    }
                    Log.i("Authorize", "Auth token received: " + authorizationToken);

                    //Generate URL for requesting Access Token
                    String accessTokenUrl = getAccessTokenUrl(authorizationToken);
                    //We make the request in a AsyncTask
                    new PostRequestAsyncTask().execute(accessTokenUrl);

                } else {
                    //Default behaviour
                    Log.i("Authorize", "Redirecting to: " + authorizationUrl);
                    webView.loadUrl(authorizationUrl);
                }
                return true;
            }
        });
        String authUrl = getAuthorizationUrl();
        Log.i("Authorize", "Loading Auth Url: " + authUrl);
        webView.loadUrl(authUrl);
    }

    /**
     * Method that generates the url for get the access token from the Service
     *
     * @return Url
     */
    private static String getAccessTokenUrl(String authorizationToken) {
        return ACCESS_TOKEN_URL
                + QUESTION_MARK
                + GRANT_TYPE_PARAM + EQUALS + GRANT_TYPE
                + AMPERSAND
                + RESPONSE_TYPE_VALUE + EQUALS + authorizationToken
                + AMPERSAND
                + CLIENT_ID_PARAM + EQUALS + API_KEY
                + AMPERSAND
                + REDIRECT_URI_PARAM + EQUALS + REDIRECT_URI
                + AMPERSAND
                + SECRET_KEY_PARAM + EQUALS + SECRET_KEY;
    }

    /**
     * Method that generates the url for get the authorization token from the Service
     *
     * @return Url
     */
    private static String getAuthorizationUrl() {
        return AUTHORIZATION_URL
                + QUESTION_MARK + RESPONSE_TYPE_PARAM + EQUALS + RESPONSE_TYPE_VALUE
                + AMPERSAND + CLIENT_ID_PARAM + EQUALS + API_KEY
                + AMPERSAND + STATE_PARAM + EQUALS + STATE
                + AMPERSAND + REDIRECT_URI_PARAM + EQUALS + REDIRECT_URI + "&scope=r_liteprofile%20r_emailaddress%20w_member_social";
    }


    private class PostRequestAsyncTask extends AsyncTask<String, Void, Boolean> {

        @Override
        protected void onPreExecute() {
            pd = ProgressDialog.show(NewLinkedInIntegration.this, "", "Loading", true);
        }

        @Override
        protected Boolean doInBackground(String... urls) {
            if (urls.length > 0) {
                String url = urls[0];
                HttpClient httpClient = new DefaultHttpClient();
                HttpPost httpost = new HttpPost(url);
                try {
                    HttpResponse response = httpClient.execute(httpost);
                    if (response != null) {
                        //If status is OK 200
                        if (response.getStatusLine().getStatusCode() == 200) {
                            String result = EntityUtils.toString(response.getEntity());
                            JSONObject resultJson = new JSONObject(result);
                            int expiresIn = resultJson.has("expires_in") ? resultJson.getInt("expires_in") : 0;
                            String accessToken = resultJson.has("access_token") ? resultJson.getString("access_token") : null;
                            Log.e("Tokenm", "" + accessToken);
                            if (expiresIn > 0 && accessToken != null) {
                                Log.i("Authorize", "This is the access Token: " + accessToken + ". It will expires in " + expiresIn + " secs");
                                Calendar calendar = Calendar.getInstance();
                                calendar.add(Calendar.SECOND, expiresIn);
                                long expireDate = calendar.getTimeInMillis();
                                SharedPreferences preferences = NewLinkedInIntegration.this.getSharedPreferences("user_info", 0);
                                SharedPreferences.Editor editor = preferences.edit();
                                editor.putLong("expires", expireDate);
                                editor.putString("accessToken", accessToken);
                                editor.commit();

                                return true;
                            }
                        }
                    }
                } catch (IOException e) {
                    Log.e("Authorize", "Error Http response " + e.getLocalizedMessage());
                } catch (ParseException e) {
                    Log.e("Authorize", "Error Parsing Http response " + e.getLocalizedMessage());
                } catch (JSONException e) {
                    Log.e("Authorize", "Error Parsing Http response " + e.getLocalizedMessage());
                }
            }
            return false;
        }

        @Override
        protected void onPostExecute(Boolean status) {
            if (pd != null && pd.isShowing()) {
                pd.dismiss();
            }
            if (status) {
                SharedPreferences preferences = NewLinkedInIntegration.this.getSharedPreferences("user_info", 0);
                accessToken = preferences.getString("accessToken", null);
                try {
                    if (accessToken != null) {
                        new GetProfileRequestAsyncTask().execute(profileUrl);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }

    }

    public void sendGetRequest(String urlString, String accessToken) throws Exception {
        URL url = new URL(urlString);
        HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
        con.setRequestMethod("GET");
        con.setRequestProperty("Authorization", "Bearer " + accessToken);
        con.setRequestProperty("cache-control", "no-cache");
        con.setRequestProperty("X-Restli-Protocol-Version", "2.0.0");
        BufferedReader br = new BufferedReader(new InputStreamReader(con.getInputStream()));
        StringBuilder jsonString = new StringBuilder();
        String line;
        while ((line = br.readLine()) != null) {
            jsonString.append(line);
        }
        JSONObject jsonObject = new JSONObject(jsonString.toString());
        Log.d("Complete json object", jsonObject.toString());
        try {
            linkedInUserId = jsonObject.getString("id");
            String country = jsonObject.getJSONObject("firstName").getJSONObject("preferredLocale").getString("country");
            String language = jsonObject.getJSONObject("firstName").getJSONObject("preferredLocale").getString("language");
            String getFirstnameKey = language + "_" + country;
            linkedInUserFirstName = jsonObject.getJSONObject("firstName").getJSONObject("localized").getString(getFirstnameKey);
            linkedInUserLastName = jsonObject.getJSONObject("firstName").getJSONObject("localized").getString(getFirstnameKey);
            linkedInUserProfile = jsonObject.getJSONObject("profilePicture").getJSONObject("displayImage~").getJSONArray("elements").getJSONObject(0).getJSONArray("identifiers").getJSONObject(0).getString("identifier");

        } catch (JSONException e) {
            e.printStackTrace();
        }

    }

    private void sendGetRequestForEmail(String urlString, String accessToken) throws Exception {
        URL url = new URL(urlString);
        HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
        con.setRequestMethod("GET");
        con.setRequestProperty("Authorization", "Bearer " + accessToken);
        con.setRequestProperty("cache-control", "no-cache");
        con.setRequestProperty("X-Restli-Protocol-Version", "2.0.0");
        BufferedReader br = new BufferedReader(new InputStreamReader(con.getInputStream()));
        StringBuilder jsonString = new StringBuilder();
        String line;
        while ((line = br.readLine()) != null) {
            jsonString.append(line);
        }
        JSONObject jsonObject = new JSONObject(jsonString.toString());
        linkedInUserEmailAddress = jsonObject.getJSONArray("elements").getJSONObject(0).getJSONObject("handle~").getString("emailAddress");
        Log.d("email json object", jsonObject.toString());
        sendRequestToServerForLinkwedInIntegration();


    }

    public void sendRequestToServerForLinkwedInIntegration() {

        if (AppUtils.isInternetOn(NewLinkedInIntegration.this)) {

            JSONObject userJsonObject = new JSONObject();
            try {
                userJsonObject.put(NetworkKeys.EMAIL, linkedInUserEmailAddress);
                userJsonObject.put(NetworkKeys.USERNAME, linkedInUserFirstName + " " + linkedInUserLastName);
                userJsonObject.put(NetworkKeys.CONTACTNO, "");
                userJsonObject.put(NetworkKeys.UID, linkedInUserId);
                userJsonObject.put(NetworkKeys.PROVIDER, "LinkedIn");
                userJsonObject.put(NetworkKeys.IMAGE, linkedInUserProfile);
                userJsonObject.put(NetworkKeys.DEVICE_TOKEN, deviceId);
                userJsonObject.put(NetworkKeys.LOCATION, location);
                userJsonObject.put(NetworkKeys.COUNTRY, country);
            } catch (JSONException e) {
                e.printStackTrace();
            }
            String url = Constants.WebServices.SOCIAL_MEDIA_LOGIN;
            CallWebService.getInstance(NewLinkedInIntegration.this, true).hitJSONObjectVolleyWebServicemanageclubdetailsWithoutAccessToken(Request.Method.POST, url, deviceId, userJsonObject, new CallBackInterfaceVolley() {
                @Override
                public void onJsonObjectSuccess(JSONObject object) {
                    pd.dismiss();
                    try {
                        boolean success = object.getBoolean(NetworkKeys.SUCCESS);

                        if (success) {
                            JSONObject userInfoJsonObject = object.getJSONObject(NetworkKeys.USERJSONOBJECT);
                            String userId = userInfoJsonObject.getString(NetworkKeys.SIGN_IN_USERID);
                            String userEmail = userInfoJsonObject.getString(NetworkKeys.SIGN_IN_USER_EMAIL);
                            String userImage = userInfoJsonObject.getString(NetworkKeys.SIGN_IN_USER_IMAGE);
                            String userName = userInfoJsonObject.getString(NetworkKeys.SIGN_IN_USER_NAME);
                            String userCity = userInfoJsonObject.getString(NetworkKeys.USER_CITY);
                            String contactNo = userInfoJsonObject.getString(NetworkKeys.CONTACT_NO);
                            String userCountry = userInfoJsonObject.getString(NetworkKeys.USER_COUNTRY);
                            String isNotificationOn = userInfoJsonObject.getString(NetworkKeys.ISNOTIFICATION);
                            String userLocation = userInfoJsonObject.getString(NetworkKeys.SIGN_IN_USER_LOCATION);
                            String signInUserType = userInfoJsonObject.getString(NetworkKeys.SIGN_IN_USER_PROVIDER);
                            String userAuthToken = userInfoJsonObject.getString(NetworkKeys.SIGN_IN_USER_AUTHTOKEN);
                            SharedPreferences.Editor editor = sharedPreferences.edit();
                            editor.putString(NetworkKeys.SIGN_IN_USERID, userId);
                            editor.putString(NetworkKeys.SIGN_IN_USER_EMAIL, userEmail);
                            editor.putString(NetworkKeys.SIGN_IN_USER_IMAGE, userImage);
                            editor.putString(NetworkKeys.SIGN_IN_USER_NAME, userName);
                            editor.putString(NetworkKeys.USER_CITY, userCity);
                            editor.putString(NetworkKeys.USER_COUNTRY, userCountry);
                            editor.putString(NetworkKeys.SIGN_IN_USER_MOBILE, contactNo);
                            editor.putString(NetworkKeys.SIGN_IN_USER_LOCATION, userLocation);
                            editor.putString(NetworkKeys.SIGN_IN_USER_PROVIDER, signInUserType);
                            editor.putString(NetworkKeys.ISNOTIFICATION, isNotificationOn);
                            editor.putString(NetworkKeys.SIGN_IN_USER_AUTHTOKEN, userAuthToken);
                            editor.putBoolean(NetworkKeys.IS_USER_LOGIN_FROM_SOCIAL_MEDIA, true);
                            editor.putBoolean(NetworkKeys.SIGN_IN_USER_SUCCESSFULLY, true);
                            editor.apply();
                            Intent intent = new Intent(NewLinkedInIntegration.this, CardSelctionActivity.class);
                            intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
                            startActivity(intent);
                            finish();
                        } else {
                            pd.dismiss();
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }

                @Override
                public void onJsonArrarSuccess(JSONArray array) {

                }

                @Override
                public void onFailure(String str) {
                    pd.dismiss();
                }
            });
        } else {
            AppUtils.showToast(NewLinkedInIntegration.this, getResources().getString(R.string.internet_connection));
        }
    }


    private class GetProfileRequestAsyncTask extends AsyncTask<String, Void, JSONObject> {

        @Override
        protected void onPreExecute() {
            pd = ProgressDialog.show(NewLinkedInIntegration.this, "", "Loading..", true);
        }

        @Override
        protected JSONObject doInBackground(String... urls) {
            if (urls.length > 0) {
                try {
                    sendGetRequest(profileUrl, accessToken);
                    sendGetRequestForEmail(emailAddress, accessToken);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            return null;
        }

        @Override
        protected void onPostExecute(JSONObject data) {
            if (pd != null && pd.isShowing()) {
                pd.dismiss();
            }
            if (data != null) {

                try {
                    String welcomeTextString = String.format("Welcome %1$s %2$s, You are a %3$s", data.getString("firstName"), data.getString("lastName"), data.getString("headline"));

                } catch (JSONException e) {
                    Log.e("Authorize", "Error Parsing json " + e.getLocalizedMessage());
                }
            }
        }

    }
}

If you want further assistance go to below link explained how to sign in with Linkedin with auth 2.0. Have a look

linkedin integration with oauth20 -v2-in-complete implementation ain android



回答4:

Inspired by @Shantanu, I've restructured/updated his "unofficial" SDK for Android, you can use it from [this GitHub repo](https://github.com/AbdAllahAbdElFattah13/LinkedIn-SDK-Android.

It's now Kotlin based and follows clean architecture principles for better understanding and modifications.

We also use it in our production apps, so it should be working fine. :)