Как использовать токен, возвращаемый GoogleAuthUtil.getToken с моим движком приложения

что я хочу сделать

у меня есть простой бэкэнд Google App Engine и простое приложение для Android, и я хочу сделать аутентифицированные запросы из приложения Android на сервер. Я читал о конечных точках Google Cloud, и даже если это действительно хороший API, я чувствую, что это немного перебор для того, что я хочу сделать. Я просто хочу сделать аутентифицированный HTTP-запрос и получить текст ответа.

GET myappid.appspot.com/api/user

следует ответ:

Hello john.doe

если пользователь john.doe@gmail.com выполняет запрос.

серверной стороне:

я создал новый проект App Engine:

WEB_CLIENT_ID=123456789012.apps.googleusercontent.com

и зарегистрировал приложение для Android ("доступ к API непосредственно с 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

и

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)

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

что работает

я могу использовать свой браузер, чтобы к

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

войдите как Джон Доу, а затем

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

и я

Hello, john.doe

фантастика это именно то, что я ожидал.

не работает

С Android, я все мои попытки привели к

Hello, None

как вы можете видеть в коде Android, я использую GoogleAuthUtil для получения токена, но я действительно не понимаю, что я должен с ним делать.

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

затем я строю запрос:

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

и добавьте заголовок" Authorization":

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

я тоже пробовал:

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

вероятно, чего-то не хватает на Android или на бэкэнде App Engine, но я действительно не понимаю, что. Есть ли часть API, которая упрощает это ?

это кажется так просто с браузером...

ТИА

1 ответов


можно отправить маркер доступа в приложение Google App Engine (или любое другое веб-приложение) (в качестве маркера носителя, это все, что нужно для пересылки учетных данных), однако Google App Engine не будет автоматически распознавать заголовок "авторизация" и установить объект пользователя для вас (это то, что конечные точки могут помочь вам).

Вы можете выбрать, чтобы найти маркер доступа через заголовки запроса объекта:

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

затем отправьте это в Google API для проверки его действия и получения информации об этом пользователе (я думаю, что это включает электронную почту, если электронная почта-это область, для которой вы изначально запросили маркер доступа).

посмотреть получить информацию о пользователе через Google API подробнее о том, как это сделать.

вы также должны проверить, что маркер доступа был выдан вашему приложению (https://www.googleapis.com/oauth2/v1/tokeninfo?access_token={access_token} - проверьте идентификатор клиента в ответе) - если вы этого не сделаете, это очень легко для другого приложения, которое имеет разрешение от пользователя получить маркер доступа для выполнения вызовов против вашего частного API.

все сказанное, другой механизм-получить IDToken от Android и отправить его в ваше веб-приложение - более подробную информацию можно найти здесь: http://googledevelopers.blogspot.com/2013/05/cross-platform-sso-technology.html и https://developers.google.com/accounts/docs/CrossClientAuth

пример использования клиента Google API Python для получения информации о выданном токене:

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': 'xxxxx@yyyyy.com',
  'verified_email': True
}