Autorización Oauth 2.0 para LinkedIn en Android

A pesar de que no hay tal sdk específicos de android de linkedIn (como facebook y twitter sdk para android) .Configurar la autorización linkedIn con Oauth 1.0 todavía era fácil de usar:

  • Scribe-java
  • Social-auth para android.
  • Y la lista de herramientas aquí.

Pero no es la misma historia para la autorización con Oauth2.0. No hay demasiadas bibliotecas útiles o ejemplos específicos de Android. He intentado usar estos:

  • Android Oauth-cliente
  • Tnj .

He leído que Oauth 2.0 es mucho más simple de implementar que el 1.0. Todavía no puedo hacerlo.

¿Alguna sugerencia para implementar Oauth2.0 para LinkedIn en Android?

Autenticación Oauth2.0 para LinkedIN.

Paso 1:

  • Registre su aplicación con linkedIn siguiendo este documento. Y obtener su api_key y api_secret.

Paso 2:

Actividad principal:

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

Y el 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> 

El token se guarda en el archivo de preferencias compartidas.

Repo del proyecto androide simple aquí en github .

OAuth 2.0 es mucho más simple que 1.0 y se puede hacer sin ninguna ayuda de una biblioteca externa. Sin embargo, si ya está utilizando scribe-java, será aún más fácil.

La implementación es sencilla. WebViewClient crear un WebView que tenga un WebViewClient personalizado que capture y WebViewClient el comportamiento de carga de su URL de devolución de llamada. Por lo tanto, cuando el WebView intenta cargar esa URL, puede interceptar el proceso y extraer un verificador. El verificador se puede pasar a scribe-java para intercambiar por un token de acceso.

Para iniciar todo el proceso, sólo tiene que indicarle a su WebView que cargue la URL de autorización.

Tengo código de ejemplo alojado aquí . La aplicación se autentica con la API de Buffer, pero la mayoría del código se puede volver a utilizar. Puede que le interese el fragmento que hospeda mi WebView personalizado y el trabajo de fondo que obtiene el token de acceso.

Siéntase libre de hacerme cualquier pregunta de seguimiento.

Lo conseguí trabajando, pero me llevó … algún tiempo.

Seguí LinkedIn autenticación para gestionar eso.
Todavía estoy fuertemente asesorado para seguir leyendo este enlace, ya que no cubrí todos los casos en mis ejemplos (errores, manejo de errores, mejores prácticas, uso de parámetros, documentación precisa …)

  • En primer lugar, necesita tener su clave de API de LinkedIn y su clave secreta. Si no lo hace, registre una aplicación aquí.

  • En segundo lugar, necesita una actividad en la aplicación que puede recibir el código de autorización. Para ello, debe configurarse como navegable (lanzable desde un navegador) en el archivo 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> 

    Aunque no se recomienda, es posible utilizar una etiqueta de datos para recuperar URIs mediante un esquema personalizado:

      <data android:scheme="oauth"/> 
  • Después de eso, debe redirigir al usuario al diálogo de autorización de LinkedIn, utilizando una URL específica:

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

    Puede usar un WebView para mostrarlo directamente en su aplicación o dejar que el sistema lo maneje a través de un intento como:

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

    El único problema aquí es que la API no acepta esquemas distintos de http o https, lo que significa que no puede pasar el URI de intención como el parámetro redirect_uri.

    Así que creé una página de destino en mi servidor, con el único propósito es redirigir a la aplicación. Podemos imaginar algo como (en feo acortar PHP) (Intent ref.) :

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

    Así que todo está listo! Ahora el onCreate(Bundle) de la ResultActivity:

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

    Hay otra manera de pasar parámetros aquí, si la etiqueta de datos se usó anteriormente.

  • ¡Casi allí! Ahora sólo necesita realizar una solicitud POST simple en esa 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 

    Devolución de un objeto JSON en caso de éxito:

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

Y voilà Ahora puede realizar sus llamadas a la API mediante el access_token. No olvide guardarlo en algún lugar para que no tenga estos pasos de nuevo.

Espero que esto no sea demasiado largo para leer y que pueda ayudar a algunas personas. 🙂

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

Y en su AsyncTask:

Private class PostRequestAsyncTask extends AsyncTask {

  @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); } } }; 
  • Android - cómo obtener token de acceso de google plus?
  • Autenticación con OAuth2 para una aplicación * y * un sitio web
  • Oauth 2 no puede intercambiar código por token de acceso, devuelve "invalid_grant"
  • Error "No se puede crear una conexión fiable con el servidor" al iniciar sesión con google plus
  • Validación y uso de auth_token de Android en el lado del servidor
  • Acceso a Google API - GoogleAccountCredential.usingOAuth2 vs GoogleAuthUtil.getToken ()
  • Android: verifica el nombre de la cuenta google del dispositivo en el servidor
  • Javamail api en android usando XOauth
  • Seguridad de Primavera Angularjs + Android REST
  • Google Plus SignIn / oAuth2 - lanzamiento del lado del servidor TokenResponseException: 401 no autorizado
  • ¿Cómo puedo enviar un mensaje a un dispositivo usando C2DM desde un servidor que ha sido autenticado con OAuth2?
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.