13

Python で記述され、Endpoints API を使用して Google App Engine アプリケーションを開発しています。併せて、Endpoints API とやり取りするための Chrome 拡張機能を作成しています。Endpoints API と認証に関して、多くの問題に直面しています。現在、ここに私のセットアップがあります:

エンドポイント API (Python)

from google.appengine.ext import endpoints
from protorpc import message_types
from protorpc import remote

ALLOWED_CLIENT_IDS = ['client_id_from_google_api_console',
                      endpoints.API_EXPLORER_CLIENT_ID]

@endpoints.api(name='my_api',version='v1', description='My API',
               allowed_client_ids=ALLOWED_CLIENT_IDS)
class MyApi(remote.Service):

    @endpoints.method(message_types.VoidMessage, DeviceListResponse,
                      name='user.device.list', path='user/device/list', 
                      http_method='GET')
    def user_device_list(self, request):
        current_user = endpoints.get_current_user()
        if current_user is None:
            raise endpoints.UnauthorizedException('You must authenticate first.')
        if current_user.user_id() is None:
            raise endpoints.NotFoundException("Your user id was not found.")

        return DeviceListResponse(devices=[]) #Hypothetically return devices

api_service = endpoints.api_server([MyApi], restricted=False)

Google API コンソール

JS オリジンには以下が含まれます: chrome-extensions://chrome_app_id

クロム拡張 (JS)

var apiRoot = "https://my_app_id.appspot.com/_ah/api";
var clientID = "client_id_from_google_api_console";
var oauthScopes = ["https://www.googleapis.com/auth/userinfo.email"];
var responseType = "token id_token";

//Helper method to log to the console
function l(o){console.log(o);}

function oauthSignin(mode) {
    gapi.auth.authorize({client_id: clientID, scope: oauthScopes,
    immediate: mode, response_type: responseType}, function() {
        var request = gapi.client.oauth2.userinfo.get();
        request.execute(function(resp) {
            authenticated = !resp.code;
            if(authenticated) {
                var token = gapi.auth.getToken();
                token.access_token = token.id_token;
                gapi.auth.setToken(token);
                l("Successfully authenticated. Loading device list");
                gapi.client.my_api.user.device.list({}).execute(function(resp) {
                    if(resp.code) {
                        l("Response from device list: " + resp.message);
                    }
                    l(resp);
                });
            }
        });
    });
}


//This get's called when the page and js library has loaded.
function jsClientLoad() {
    l("JS Client Libary loaded. Now loading my_api and oauth2 APIs.");
    var apisToLoad;
    var callback = function() {
        if (--apisToLoad == 0) {
            l("APIs have loaded.")
            oauthSignin(true);
        } else {
            l("Waiting for " + apisToLoad + " API" + (apisToLoad>1?"s":"") + " to load.");
        }
    }

    apisToLoad = 2; // must match number of calls to gapi.client.load()
    gapi.client.load('my_api', 'v1', callback, apiRoot);
    gapi.client.load('oauth2', 'v2', callback);
}

結果

コードのメイン チャンクを表示したので (コード全体をアップロードせずに意味をなすように少し変更する必要がありました)、Google API Explorer に移動してそのメソッドを実行すると、200 応答が返されます。Chrome 拡張機能で実行すると、「ユーザー ID が見つかりませんでした」というメッセージを含む 404 コードが表示されます。

4

3 に答える 3

19

なぜ/いつこれが200;になるのかは不明です。すべきではありません。Function User.getUserId() in Cloud endpoint api で述べ​​たように、null ではないユーザー オブジェクトに対して null が返されます。これは既知の問題です。

TLDR;

から返される結果に が入力されることはありませuser_idん。回避策があります。ユーザー オブジェクトをデータストアに格納してから取得すると ( を使用している場合は、新しい get を使用して)、値が取り込まれます。endpoints.get_current_user()ndbuser_id()

App Engine ユーザー ID の代わりに、アカウントに関連付けられた Google プロファイル ID を使用することを強く検討する必要があります。

歴史/説明:

endpointsBearer トークンと ID トークン (Android 用) の両方で使用することを意図しています。ID トークンは、デバイスの暗号化と組み合わせて署名された特殊なタイプの JWT (JSON Web トークン) です。その結果、これらのトークンからユーザーを解析しても、そのトークンにエンコードされた情報しか判別できません (詳細については、クラウド エンドポイントの oauth2 エラーを参照してください)。

これらのトークンは、App Engine の外部にある一般的な Google Auth プロバイダー (OAuth 2.0) によって生成されるため、App Engine ユーザー ID はこのサービスでは認識/共有されません。その結果、ID トークンを使用してリクエストに署名するときに、を設定することはできません。user_id()

標準の Bearer トークンが使用される場合 (Chrome アプリケーションには適しています)、App Engine OAuth APIが使用されます。OAuth API の呼び出し時

oauth.get_current_user(some_scope)

(どこにoauthあるgoogle.appengine.api.oauth)、

oauth.oauth_api._maybe_call_get_oauth_user(_scope=None)

メソッドが呼び出されます。これにより、トークンから現在のユーザーを取得できるサービスを提供する共有 App Engine レイヤーへのRPCが作成されます。この場合user_id()返されたユーザーの設定されますが、ユーザーの値は保持されendpoints.get_current_userず、電子メールと認証ドメインのみが保持されます。

別の回避策:

呼び出しは、RPCoauth.get_current_user()を行う場合にのみ高価です。このメソッドは最後の呼び出しからの値を保存するため、2 回目の呼び出しでは、 Python から値を検索するための数ナノ秒を除いて、ネットワーク/速度のオーバーヘッドは発生しません。_maybe_call_get_oauth_useroauth.get_current_user()dict

endpoints.get_current_user()の呼び出しを使用して Bearer トークンのユーザーを決定するため、これは非常に重要ですoauth.get_current_user()。そのため、もう一度呼び出したい場合は、そのパフォーマンスが心配になります。

ID トークンを使用しないことがわかっている場合、またはそのような状況を簡単に判断できる場合は、両方を呼び出すようにコードを変更できます。

endpoints_user = endpoints.get_current_user()
if endpoints_user is None:
    raise endpoints.UnauthorizedException(...)

oauth_user = oauth.get_current_user(known_scope)
if oauth_user is None or oauth_user.user_id() is None:
    # This should never happen
    raise endpoints.NotFoundException(...)

endpoints.get_current_user():トークンが、許可した特定のスコープの 1 つと、アプリケーションと通信するためにホワイトリストに登録した特定のクライアント ID の 1 つに対してのみ作成されていることを常に確認するため、まだ呼び出す必要があります。

: 値known_scopeは、考えられるスコープのどれがトークンに一致するかによって異なります。スコープのリストはいずれかのendpoints.get_current_user() ヘルパー メソッドでループされ、これが成功すると、最終的に一致するスコープが として保存されos.getenv('OAUTH_LAST_SCOPE')ます。にこの値を使用することを強くお勧めしますknown_scope

より良い代替手段:

前述のように、App Engine ユーザー ID を ID トークンから暗示することはできませんが (現時点では)、App Engine ユーザー ID の代わりにGoogle プロファイル IDを使用できます。(この ID は多くの場合、Google+ ID と見なされますが、これは多くのサービスで一貫しています。)

この値が Bearer OR ID トークンに関連付けられていることを確認するには、 APIuserinfo.emailに関連付けられた非スコープのいずれかもリクエストしてください。userinfo

  • https://www.googleapis.com/auth/plus.login
  • https://www.googleapis.com/auth/plus.me
  • https://www.googleapis.com/auth/userinfo.email
  • https://www.googleapis.com/auth/userinfo.profile

(このリストは、2013 年 5 月 20 日の執筆時点で最新のものです。)

ベアラー トークンの場合の App Engine ユーザー ID と同様に、この Google プロファイル ID は によって破棄されendpoints.get_current_user()ます、両方の種類のトークンで使用できます。

サンプルの一部であるget_google_plus_user_id() メソッドappengine-picturesque-pythonは、ヘルパー メソッドの 1 つにパッチをendpoints.get_current_user()適用してこのデータを保持し、要求からベアラーまたは ID トークンを検証するために使用される高価なネットワーク呼び出しを繰り返さなくても、この値を使用できるようにします。

于 2013-05-21T03:43:39.423 に答える
0

私は最近、この頭痛に遭遇しました。ただし、いくつかのテストの後、1.9.2ではローカル開発サーバーにuser.user_id()戻るようです。Noneただし、デプロイすると、値が返されます。ただし、これが App Engine ID なのか Google+ ID なのかはわかりません。とにかく調べるには?

ただし、ドキュメントを考えると、返される ID は永続的なレコードに安全に使用できると思います。

于 2014-04-13T15:48:13.603 に答える