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:
- Cómo utilizar OAuth 2.0 con Retrofit Android
- Intento del navegador y regreso a la actividad correcta (ficha abierta)
- La mejor manera de usar las API de Google con OAuth 2.0 en Android
- ¿Cómo puedo enviar un mensaje a un dispositivo usando C2DM desde un servidor que ha sido autenticado con OAuth2?
- ¿Cómo evitar la "Acción completa con" emergente con devoluciones de llamada de OAuth de Google?
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
- ¿Dónde está la lista de alias de Token de autorización de Android para las API de Google?
- ¿Cómo obtener la huella digital del certificado de firma (SHA1) para OAuth 2.0 en Android?
- Android Retrofit2 Actualizar Oauth 2 Token
- Token de actualización de accountmanager (acceso sin conexión)
- Api de Rails y autenticación nativa de aplicaciones para móviles
- ¿Ejecutar una secuencia de comandos de Google Apps desde la aplicación para Android?
- Acceso a Linkedin para una aplicación de Cordova
- Acceso a Google API - GoogleAccountCredential.usingOAuth2 vs GoogleAuthUtil.getToken ()
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 }