Cómo utilizar el token devuelto por GoogleAuthUtil.getToken con mi parte posterior de App Engine

Lo que quiero hacer

Tengo un backend simple de Google App Engine y una aplicación Android sencilla y quiero hacer solicitudes autenticadas de la aplicación de Android al servidor. He leído acerca de Google Cloud Endpoints e incluso si es una API realmente buena, siento que es un poco excesivo lo que quiero hacer. Sólo quiero hacer una solicitud HTTP autenticada y obtener el texto de respuesta.

GET myappid.appspot.com/api/user 

Debe responder:

 Hello john.doe 

Si el usuario [email protected] hace la solicitud.

Parte posterior:

He creado un nuevo proyecto de App Engine:

 WEB_CLIENT_ID=123456789012.apps.googleusercontent.com 

Y registró una aplicación para Android ("Acceso a API directamente desde Android"):

 package name : com.myappid debug SHA1 fingerprint: 3a:e1:05:17:15:54:c6:c7:9b:ef:19:74:ae:5b:f7:0f:c3:d5:45:9d 

Y esto creó

 ANDROID_CLIENT_ID=123456789012-9f4sd525df3254s3d5s40s441df705sd.apps.googleusercontent.com 

App.yaml

 application: myappid version: 1 runtime: python27 api_version: 1 threadsafe: true handlers: - url: /api/.* secure: always script: api.APP libraries: - name: webapp2 version: latest - name: pycrypto version: latest 

Api.py

 import webapp2 from google.appengine.api import users from google.appengine.api import oauth class GetUser(webapp2.RequestHandler): def get(self): user = users.get_current_user() self.response.headers['Content-Type'] = 'text/plain' self.response.out.write('Hello, {}\n'.format('None' if user is None else user.nickname())) try: user = oauth.get_current_user() self.response.out.write('Hello OAuth, {}\n'.format('None' if user is None else user.nickname())) except Exception as e: self.response.out.write(str(e)+'\n') class SignIn(webapp2.RequestHandler): def get(self): if users.get_current_user() is None: self.redirect(users.create_login_url(self.request.uri)) APP = webapp2.WSGIApplication([ ('/api/user', GetUser), ('/api/signin', SignIn), ], debug = True) 

Lado de Android

 public class MainActivity extends Activity { private static final String CLIENT_ID = "123456789012.apps.googleusercontent.com"; private static final String SCOPE = "audience:server:client_id:" + CLIENT_ID; private static final int AUTH_REQUEST_CODE = 1; private Account mAccount; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mAccount = AccountManager.get(mActivity).getAccountsByType(GoogleAuthUtil.GOOGLE_ACCOUNT_TYPE)[0]; new GetAuthToken().execute(mAccount.name); } protected void log(String msg) { TextView tv = (TextView) mActivity.findViewById(R.id.textView); tv.setText(tv.getText() + "\n" + msg); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == AUTH_REQUEST_CODE) { if (resultCode == RESULT_OK) { new GetAuthToken().execute(mAccount.name); } } } private class GetAuthToken extends AsyncTask<String, Void, String> { @Override protected String doInBackground(String... params) { try { // Retrieve a token for the given account and scope. It will always return either // a non-empty String or throw an exception. String email = params[0]; String token = GoogleAuthUtil.getToken(mActivity, email, SCOPE); return token; } catch (GooglePlayServicesAvailabilityException playEx) { Dialog alert = GooglePlayServicesUtil.getErrorDialog(playEx.getConnectionStatusCode(), mActivity, AUTH_REQUEST_CODE); return "error - Play Services needed " + playEx; } catch (UserRecoverableAuthException userAuthEx) { // Start the user recoverable action using the intent returned by // getIntent() mActivity.startActivityForResult(userAuthEx.getIntent(), AUTH_REQUEST_CODE); return "error - Autorization needed " + userAuthEx; } catch (IOException transientEx) { // network or server error, the call is expected to succeed if you try again later. // Don't attempt to call again immediately - the request is likely to // fail, you'll hit quotas or back-off. return "error - Network error " + transientEx; } catch (GoogleAuthException authEx) { // Failure. The call is not expected to ever succeed so it should not be // retried. return "error - Other auth error " + authEx; } } @Override protected void onPostExecute(String result) { if (result.startsWith("error -")) { log(result); } else { log("Obtained token : " + result); new GetAuthedUserName().execute(result); } } } private class GetAuthedUserName extends AsyncTask<String, Void, String> { @Override protected String doInBackground(String... params) { try { String token = params[0]; URL url = new URL("https://myappid.appspot.com/api/user"); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); //conn.setRequestProperty("Authorization", "Bearer " + token); conn.addRequestProperty("Authorization", "OAuth " + token); InputStream istream = conn.getInputStream(); try { BufferedReader reader = new BufferedReader(new InputStreamReader(istream)); String line; StringBuilder sb = new StringBuilder(); while ((line = reader.readLine()) != null) { sb.append(line); } return sb.toString(); } catch (IOException e) { return "error - Unable to read from the connection"; } } catch (MalformedURLException e) { return "error - Malformed URL " + e; } catch (IOException e) { return "error - IO error " + e; } } @Override protected void onPostExecute(String result) { if (result.startsWith("error -")) { log(result); } else { log("Request result : " + result); } } } } 

Que funciona

Puedo usar mi navegador, para

 https://myappid.appspot.com/api/signin 

Ingresar como John Doe y luego

 https://myappid.appspot.com/api/user 

Y yo consigo

 Hello, john.doe 

Fantástico es exactamente lo que esperaba.

Lo que no funciona

Con Android, todos mis intentos resultaron en

 Hello, None 

Como puedes ver en el código de Android, uso GoogleAuthUtil para recuperar un token, pero realmente no entiendo lo que debo hacer con él.

 String token = GoogleAuthUtil.getToken(mActivity, email, SCOPE); 

Luego construyo la solicitud:

 URL url = new URL("https://myappid.appspot.com/api/user"); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); 

Y agrega el encabezado "Autorización":

 conn.setRequestProperty("Authorization", "Bearer " + token); 

También intenté:

 conn.addRequestProperty("Authorization", "OAuth " + token); 

Probablemente falte algo en Android o en el backend de App Engine, pero realmente no entiendo qué. ¿Hay un pedazo de API que simplifica esto?

Parece tan simple con un navegador …

TIA

Es posible enviar el token de acceso a tu aplicación de Google App Engine (o cualquier otra aplicación web) (como un token de portador, es todo lo que se necesita para reenviar credenciales) sin embargo, Google App Engine no reconocerá automáticamente el encabezado de "Autorización" Y establecer el objeto de usuario para usted (que es algo que los Endpoints pueden ayudarle con).

Puede elegir encontrar el token de acceso a través del objeto de encabezado de solicitud:

 access_token = self.request.headers['Authorization'] 

A continuación, envíe esto a una API de Google para verificar que es válida y obtener información sobre ese usuario (creo que esto incluye correo electrónico, siempre y cuando el correo electrónico sea un ámbito que originalmente solicitó un token de acceso).

Consulte Obtener información de usuario a través de la API de Google para obtener detalles sobre cómo hacerlo.

También debe comprobar que el token de acceso se ha emitido a su aplicación ( https://www.googleapis.com/oauth2/v1/tokeninfo?access_token= {access_token} – verificar el ID del cliente en la respuesta) – si no lo hace Que hace que sea muy fácil para otra aplicación que tiene permiso del usuario para obtener un token de acceso para realizar llamadas en su API privada.

Que todo lo dicho, otro mecanismo es conseguir un IDToken de Android, y enviarlo a su aplicación web – más detalles se pueden encontrar aquí: http://googledevelopers.blogspot.com/2013/05/cross-platform-sso-technology .html y https://developers.google.com/accounts/docs/CrossClientAuth

Ejemplo de cómo usar Google API Python Client para obtener información sobre un token emitido:

 from apiclient.discovery import build print build('oauth2', 'v1').tokeninfo(access_token=access_token).execute() # Result { 'issued_to': 'xxxxxx.apps.googleusercontent.com', 'user_id': 'yyyyyy', 'expires_in': 3457, 'access_type': 'online', 'audience': 'xxxxxx.apps.googleusercontent.com', 'scope': 'https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile', 'email': '[email protected]', 'verified_email': True } 
  • UNREGISTERED_ON_API_CONSOLE mientras obtiene el token OAuth2 en Android
  • Añadir Retrofit Requestinterceptor con Dagger en tiempo de ejecución
  • ¿GoogleAccountCredential proporciona token de actualización?
  • Las pestañas personalizadas de Chrome de Android / la API web de Fitbit no se redireccionarán si la aplicación ya está autorizada. (OAuth2.0)
  • ¿Cómo / dónde almacenar el token de actualización en Android?
  • Oauth en Appengine con los servicios de Google Play
  • Oauth 2.0 Android uso Primavera-Para-Android
  • Cómo manejar el error redirect_uri_mismatch cuando la aplicación Android obtiene acceso sin conexión para el back-end de la Web?
  • Integración con Android de Google+ - repetida UserRecoverableAuthException
  • Android Jtwitter, problemas de autenticación
  • Enviar objetos JSON utilizando métodos POST
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.