Oauth 2.0 авторизация для LinkedIn в Android

Несмотря на то, что такого андроида специфического sdk из linkedIn не существует (например, facebook и twitter sdk для android). Установить соединение linkedIn авторизация с Oauth 1.0 все еще была легкой, используя:

Но это не то же самое для авторизации с Oauth2.0. Не слишком много полезных библиотек или конкретных примеров для Android. Я попытался использовать их:

Я читал, что Oauth 2.0 намного проще реализовать, чем 1.0. Тем не менее я не могу этого сделать.

Любые указатели на внедрение Oauth2.0 для LinkedIn в Android?

Ответ 1

Аутентификация Oauth2.0 для LinkedIN.

Шаг 1:

  • Зарегистрируйте свое приложение со ссылкойIn, выполнив этот документ. И получите свои api_key и api_secret.

Шаг 2:

MainActivity:

public class MainActivity extends Activity {

/*CONSTANT FOR THE AUTHORIZATION PROCESS*/

/****FILL THIS WITH YOUR INFORMATION*********/
//This is the public api key of our application
private static final String API_KEY = "YOUR_API_KEY";
//This is the private api key of our application
private static final String SECRET_KEY = "YOUR_API_SECRET";
//This is any string we want to use. This will be used for avoiding CSRF attacks. You can generate one here: http://strongpasswordgenerator.com/
private static final String STATE = "E3ZYKC1T6H2yP4z";
//This is the url that LinkedIn Auth process will redirect to. We can put whatever we want that starts with http:// or https:// .
//We use a made up url that we will intercept when redirecting. Avoid Uppercases. 
private static final String REDIRECT_URI = "http://com.amalbit.redirecturl";
/*********************************************/

//These are constants used for build the urls
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 = "=";

private WebView webView;
private ProgressDialog pd;

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

    //get the webView from the layout
    webView = (WebView) findViewById(R.id.main_activity_web_view);

    //Request focus for the webview
    webView.requestFocus(View.FOCUS_DOWN);

    //Show a progress dialog to the user
    pd = ProgressDialog.show(this, "", this.getString(R.string.loading),true);

    //Set a custom web view client
    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;
        }
    });

    //Get the authorization Url
    String authUrl = getAuthorizationUrl();
    Log.i("Authorize","Loading Auth Url: "+authUrl);
    //Load the authorization URL into the webView
    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;
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.main, menu);
    return true;
}

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

    @Override
    protected void onPreExecute(){
        pd = ProgressDialog.show(MainActivity.this, "", MainActivity.this.getString(R.string.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());
                        //Convert the string result to a JSON Object
                        JSONObject resultJson = new JSONObject(result);
                        //Extract data from JSON Response
                        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");

                            //Calculate date of expiration
                            Calendar calendar = Calendar.getInstance();
                            calendar.add(Calendar.SECOND, expiresIn);
                            long expireDate = calendar.getTimeInMillis();

                            ////Store both expires in and access token in shared preferences
                            SharedPreferences preferences = MainActivity.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){
            //If everything went Ok, change to another activity.
            Intent startProfileActivity = new Intent(MainActivity.this, ProfileActivity.class);
            MainActivity.this.startActivity(startProfileActivity);
        }
    }

};
}

И xmlLayout:

 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

    <WebView
        android:id="@+id/main_activity_web_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>

Маркер сохраняется в файле sharedpreference.

Простой андроидный проект repo здесь, в github.

Ответ 2

У меня это работало, но мне потребовалось... некоторое время.

Я выполнил LinkedIn Authentication, чтобы управлять этим.
Я по-прежнему настоятельно рекомендую по-прежнему читать эту ссылку, так как я не охватываю все случаи в моих примерах (ошибки, обработка ошибок, лучшие оценки, использование параметров, точная документация...)

  • Во-первых, вам нужно иметь ключ и секретный ключ LinkedIn API. Если вы этого не сделаете, зарегистрируйте приложение на здесь.

  • Во-вторых, вам нужно действие в приложении, которое может получить код авторизации. Для этого он должен быть установлен как доступный для просмотра (запускаемый из браузера) в файле AndroidManifest.xml:

        <activity
          android:name=".ResultActivity"
          android:label="" >
          <intent-filter>
            <action android:name="android.intent.action.VIEW"/>
    
            <category android:name="android.intent.category.DEFAULT"/>
            <category android:name="android.intent.category.BROWSABLE"/>
          </intent-filter>
    

    Хотя это и не рекомендуется, можно использовать тег данных для извлечения URI с использованием пользовательской схемы:

      <data android:scheme="oauth"/>
    
  • После этого вам необходимо перенаправить пользователя в диалоговое окно авторизации LinkedIn, используя определенный URL-адрес:

    https://www.linkedin.com/uas/oauth2/authorization?response_type=code
                                       &client_id=YOUR_API_KEY
                                       &scope=SCOPE 
                                       &state=STATE
                                       &redirect_uri=YOUR_REDIRECT_URI
    

    Вы можете использовать WebView для непосредственного отображения его в своем приложении или позволить системе обрабатывать его с помощью Intent, например:

    Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(/* FULL URL */));
    startActivity(intent);
    

    Единственная проблема здесь в том, что API не принимает схемы, отличные от http или https, то есть вы не можете просто передать URI намерения в качестве параметра redirect_uri.

    Итак, я создал целевую страницу на моем сервере, и только цель - перенаправить приложение. Мы можем представить себе что-то вроде (в уродливом сокращении PHP) (Ссылка для намерения):

    header('Location: ' . "intent:#Intent;component=your.package/.ResultActivity;S.code=" . $_GET['code'] . ";S.state=" . $_GET['state'] . ";end");
    die();
    

    Итак, все! Теперь onCreate(Bundle) для ResultActivity:

    Intent intent = getIntent();
    String authorizationCode = intent.getStringExtra("code");
    

    Есть другой способ передать параметры здесь, если тег данных использовался ранее.

  • Почти там! Теперь вам просто нужно выполнить простой запрос POST по этому URL-адресу:

    https://www.linkedin.com/uas/oauth2/accessToken?grant_type=authorization_code
                                        &code=AUTHORIZATION_CODE
                                        &redirect_uri=YOUR_REDIRECT_URI
                                        &client_id=YOUR_API_KEY
                                        &client_secret=YOUR_SECRET_KEY
    

    Возврат объекта JSON при успешном выполнении:

    {"expires_in":5184000,"access_token":"AQXdSP_W41_UPs5ioT_t8HESyODB4FqbkJ8LrV_5mff4gPODzOYR"}

Et voilà! Теперь вы можете совершать вызовы API с помощью access_token. Не забудьте сохранить его где-то, чтобы вы не смогли выполнить эти шаги еще раз.

Надеюсь, это было не слишком долго, чтобы читать и что это может помочь некоторым людям.:)

Ответ 3

OAuth 2.0 намного проще 1.0 и может быть выполнен без какой-либо помощи из внешней библиотеки. Однако, если вы уже используете scribe-java, это будет еще проще.

Реализация прямолинейна. Вам нужно создать WebView, у которого есть пользовательский WebViewClient, который фиксирует и отменяет поведение загрузки для вашего URL обратного вызова. Таким образом, когда WebView пытается загрузить этот URL-адрес, вы можете перехватить процесс и извлечь верификатор. Верификатор может быть передан в scribe-java для обмена токеном доступа.

Чтобы запустить весь процесс, вам просто нужно указать вашему WebView загрузить URL авторизации.

У меня есть пример кода, размещенного здесь. Приложение аутентифицируется с помощью Buffer API, но большая часть кода может быть повторно использована. Вы можете быть заинтересованы в fragment, в котором размещается мой пользовательский WebView и фон job, который получает токен доступа.

Не стесняйтесь задавать мне какие-либо последующие вопросы.

Ответ 4

            @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;
        }

И в вашей AsyncTask:

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

    @Override
    protected void onPreExecute(){
        pd = ProgressDialog.show(MainActivity.this, "", MainActivity.this.getString(R.string.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());
                        //Convert the string result to a JSON Object
                        JSONObject resultJson = new JSONObject(result);
                        //Extract data from JSON Response
                        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");

                            //Calculate date of expiration
                            Calendar calendar = Calendar.getInstance();
                            calendar.add(Calendar.SECOND, expiresIn);
                            long expireDate = calendar.getTimeInMillis();

                            ////Store both expires in and access token in shared preferences
                            SharedPreferences preferences = MainActivity.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){
            //If everything went Ok, change to another activity.
            Intent startProfileActivity = new Intent(MainActivity.this, ProfileActivity.class);
            MainActivity.this.startActivity(startProfileActivity);
        }
    }

};