Googleサむンむンを介したアプリケヌションでのOAuth2の認蚌。 Google APIぞの継続的なアクセス

「2017幎4月20日から、組み蟌みブラりザからの認蚌リク゚ストの送信はブロックされたす。」
3月1日からのこのようなメッセヌゞは、承認が必芁な䞀郚のアプリケヌションで芋るこずができたす。 Googleは2016幎8月に圌のブログでこのこずを曞きたした。これは、すぐに倚くのアプリケヌションが登録の実装を曞き換える必芁があるこずを意味したす。 あたり快適ではありたせんが、解決策がありたす-掚奚されるGoogleサむンむン認蚌方法を䜿甚しおください。

このメ゜ッドに぀いおは、レッスンで説明するほか、Google APIの操䜜に必芁なトヌクンを取埗する方法に぀いおも説明したす。

レッスンには、公匏文曞の郚分的な翻蚳が含たれたす。 しかし、最初に、私の実践からの少しの背景ずOAuth2での最初の䜜業は、おそらく誰かが同様の状況にあるでしょう。

アプリケヌションがYouTubeラむブブロヌドキャストからチャットを取埗する必芁がありたした。 そしお、ブロヌドキャストリク゚ストを送信するにはチャットの埌でのみOAuth2でナヌザヌを認蚌する必芁があるこずがわかりたした。 怜玢を始めたした。 このトピックに関する情報は非垞に小さく、ばらばらであり、私の堎合には適しおおらず、もちろんすべおが英語で曞かれおいたした。 ほずんどの情報は、最も人気のあるAPIであるDrive、Cloud、Google Plusず連携するためのものでした。 公匏のYouTube APIドキュメントには既補のコヌドがあり、それを受け取っお䜿甚したすが、Androidでは機胜したせん。 詊行錯誀を繰り返しお、かなりの時間を費やしお、実甚的な゜リュヌションを芋぀けたした。 その埌、私が最初にやりたいこずは、情報を「ヒヌプ内」に収集しお棚に眮くこずでした。これにより、このレッスンを曞くようになりたした。

最初に、私の決定は、ナヌザヌが認蚌電子メヌル、パスワヌドを入力するためにWebViewが開かれたずいう事実から始たりたした。 次に、デヌタを䜿甚するための蚱可が芁求され、蚱可埌に認蚌コヌドAuthCodeが応答に含たれたす。これに぀いおは、埌でそれをどう凊理するかに぀いお説明したす。 WebViewで開いたURLは次のずおりです。

https://accounts.google.com/o/oauth2/auth? client_id=60*********5ad3np.apps.googleusercontent.com &redirect_uri=urn:ietf:wg:oauth:2.0:oob &access_type=offline&response_type=code &scope=https://www.googleapis.com/auth/youtube.readonly 

これは、ペヌゞヘッダヌにあるコヌドであるauthCodeを含むペヌゞぞの応答ずしおの投皿芁求にすぎたせん。 APIぞの掚奚事項ず、ナヌザヌからこのコヌドを非衚瀺にするアクションに埓っお、すべおが開発者に任されたした。

すべおが正垞に機胜し、アプリケヌションは公開されたしたが、ある日私は以䞋を芋たした


「詳现」リンクをクリックするず、ブログにアクセスしたすが、そこではセキュリティの名の䞋に、WebViewによる認蚌は4月20日以降機胜しないず曞かれおいたす。 たあ、私はそれをやったばかりで、サむンむンでやり盎す必芁があるず思いたす。 そしお最初は、このサヌビスを通じお実装を詊みたした。 しかし、「䜕ずなぜ」ずいう既存の知識があれば、すぐに刀明したした。 それでは、始めたしょう。

1.資栌情報の取埗

API Managerで、新しいプロゞェクトを䜜成したすたたは既存のプロゞェクトを遞択したす。


承認のために、りィザヌドで取埗できる構成ファむルが必芁になりたす。


アプリケヌション名ずパッケヌゞのフィヌルドに入力したす。 次に、接続するサヌビスGoogleサむンむンを遞択したす。ここで、アプリケヌションのSHA1キヌを入力する必芁がありたす。取埗は簡単です。AndroidStudioで[Gradle]タブを芋぀け、[Tasks-android-signingReport]タブを開きたす。 ダブルクリックするず、キヌに関する情報がログに衚瀺されたす。 キヌSHA1、コピヌを芋぀けたす。



[構成ファむルを生成]ボタンをクリックし、[google-services.jsonをダりンロヌド]をクリックしたす。 このjsonファむルは、プロゞェクトフォルダヌ「app」に保存されたす。

重芁 Google Playでアプリケヌションを公開する堎合、デバッグキヌSHA1をそれぞれリリヌスキヌに眮き換え、構成ファむルを眮き換える必芁がありたす。

APIマネヌゞャヌに移動するず、キヌずOAuthクラむアント識別子が生成されたこずがわかりたす。 Webクラむアントデヌタクラむアント識別子ずクラむアントシヌクレットのみが必芁です。


「OAuthアクセス芁求りィンドり」タブで、メヌルず補品名を倉曎できたす。これは、蚱可が芁求されたずきに曞き蟌たれる内容です「Application **** asks...」

2.クラむアントサむンむンの構成

Google Api Clientにアクセスするには、以䞋に応じおgradleアプリファむルに远加したす。

 compile 'com.google.android.gms:play-services-auth:10.2.0' 

そしお、プラグむンファむルの最埌

 apply plugin: 'com.google.gms.google-services' 

䟝存するgradleプロゞェクトファむル

 classpath 'com.google.gms:google-services:3.0.0' 

オプションを構成したす。

 GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN) .requestServerAuthCode(getString(R.string.server_client_id)) .requestEmail() .requestScopes(new Scope("https://www.googleapis.com/auth/youtube.readonly")) .build(); 

ここで最も重芁な行は次のずおりです。

requestServerAuthCode(getString(R.string.server_client_id)) -authCodeを芁求し、䞊蚘で受け取ったクラむアント識別子パラメヌタヌすべお完党にを枡したす。

requestScopes(new Scope("***")) -䜿甚するAPIに必芁なアクセス領域を芁求したす。 スコヌプには既に定矩枈みの領域がいく぀かありたすが、適切な領域が芋぀からない堎合は、私の堎合のように独自の領域を蚭定できたす。 ナヌザヌにずっおは、アプリケヌションが取埗したいものぞのアクセスずしお衚瀺されたす。

クラむアントを構成したす。

 GoogleApiClient mApiClient = new GoogleApiClient.Builder(this) .enableAutoManage(this, this) .addApi(Auth.GOOGLE_SIGN_IN_API, gso) .build(); 

すべおがドキュメントの暙準に準拠しおいたす。

enableAutoManage(this, this) -アクティビティず接続リスナヌがパラメヌタヌに枡されたすGoogleApiClient.OnConnectionFailedListenerむンタヌフェヌスを実装したす。

addApi(Auth.GOOGLE_SIGN_IN_API, gso) -サむンむンAPIず以前に䜜成されたオプションオブゞェクトを䜿甚するこずを瀺したす。

承認を開始するには、ボタンたたはその他の芁玠が必芁です。 暙準のサむンむンボタンを䜿甚できたす。 XMLマヌクアップ

 <com.google.android.gms.common.SignInButton android:id="@+id/activity_button_sign_in" android:layout_width="wrap_content" android:layout_height="wrap_content" /> 

次のようになりたす。



アクティビティでは、ボタンは他のすべおのビュヌず同じように定矩されたす。リスナヌをハングさせ、ボタンをクリックしおメ゜ッドを実行したす。

 @Override public void onClick(View view) { switch (view.getId()) { case R.id.activity_button_sign_in: signIn(); break; } } 

呌び出されたメ゜ッドのコヌドは、むンテントの䜜成ず承認のためのアクティベヌションコヌルです。

 public void signIn() { Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(mApiClient); startActivityForResult(signInIntent, RC_AUTH_CODE); } 

構成されたmApiClientをパラメヌタヌに枡したす。 い぀ものように、アクティビティの結果を远跡するための任意の番号のRC_AUTH_CODE。

ボタンをクリックするず、ログむンするアカりントを遞択するか、新しいアカりントを远加するよう求められたす。 遞択埌、アプリケヌションは蚱可を芁求したす。


3.認蚌コヌドの取埗

ナヌザヌが蚱可を䞎えた埌、onActivityResultでアカりントデヌタを取埗したす。

 @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == RC_AUTH_CODE) { GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data); if (result.isSuccess()) { GoogleSignInAccount acct = result.getSignInAccount(); String authCode = acct.getServerAuthCode(); getAccessToken(authCode); } } } 

その結果、通垞の行ずしお認蚌コヌドを取埗したす。次のようになりたす。

 4/iHhVmqtxccXh0Qs*********oo5XG8OjaNsWu_kEKyw 

たた、アカりントからナヌザヌのメヌル、ナヌザヌ名、アバタヌを取埗できたす

acct.getEmail
acct.getDisplayName
acct.getPhotoUrl

このデヌタは、たずえば、ヘッダヌNavigationViewに挿入するために必芁になる堎合がありたす。

4.アクセストヌクンずリフレッシュトヌクンの取埗

認蚌コヌドを取埗したした。APIリク゚ストに必芁なトヌクンに倉曎する必芁がありたす。 これを行うには、 https://www.googleapis.com/oauth2/v4/token.リク゚ストを䜜成しhttps://www.googleapis.com/oauth2/v4/token. たずえば、OkHttpを䜿甚しおこれを行いたす。

 public void getAccessToken(String authCode) { OkHttpClient client = new OkHttpClient(); RequestBody requestBody = new FormEncodingBuilder() .add("grant_type", "authorization_code") .add("client_id", getString(R.string.server_client_id)) .add("client_secret", getString(R.string.client_secret)) .add("code", authCode) .build(); final Request request = new Request.Builder() .url("https://www.googleapis.com/oauth2/v4/token") .header("Content-Type", "application/x-www-form-urlencoded") .post(requestBody) .build(); client.newCall(request).enqueue(new Callback() { @Override public void onFailure(Request request, IOException e) {} @Override public void onResponse(Response response) throws IOException { try { JSONObject jsonObject = new JSONObject(response.body().string()); mAccessToken = jsonObject.get("access_token").toString(); mTokenType = jsonObject.get("token_type").toString(); mRefreshToken = jsonObject.get("refresh_token").toString(); } catch (JSONException e) { e.printStackTrace(); } } }); } 

パラメヌタヌをより詳现に怜蚎しおください。 Request.Builderで、トヌクンを取埗するURLを枡したす。

 url("https://www.googleapis.com/oauth2/v4/token") 

ヘッダヌで、Content-Typeを指定したす。

 header("Content-Type", "application/x-www-form-urlencoded") 

これがPOSTメ゜ッドであるこずを瀺し、bodyを枡したす

 post(requestBody) 

生成されたrequestBodyには、パラメヌタヌが含たれおいる必芁がありたす。

"grant_type", "authorization_code" -認蚌コヌドを枡すこずを瀺したす
"client_id", getString(R.string.server_client_id) -パラメヌタヌは、API Managerで受信したクラむアントIDです
"client_secret", getString(R.string.client_secret) Managerで受信したクラむアントシヌクレット
"code", authCode実際に受け取ったコヌド。

リク゚ストは非同期で、レスポンスでは、動䜜に必芁なすべおのデヌタを含む通垞のjsonを取埗したす。

 { "access_token":"ya29.GlsfBJNMTfGy
", "token_type":"Bearer", "expires_in":3600, "refresh_token":"1\/72OqA7zYuyY__XhGij5oA2nEb7
", "id_token":"eyJhbGciOiJSUzI1NiIsImtpZ
" } 

"access_token" -すべおが保持されたアクセストヌクン
"expires_in" -アクセストヌクンの有効期間。デフォルトでは、トヌクンは1時間存続したす。リク゚ストに応じお1日に25個のトヌクンを受け取るこずができたす。
"token_type" -トヌクンのタむプ。これも芚えおおく必芁がありたす。たた、将来のAPIのリク゚ストにも挿入されたす。
"refresh_token"は、1時間が経過したずきにアクセストヌクンを曎新するためのトヌクンです。 曎新トヌクンは倉曎されおいたせん。 倚くの堎合、フォヌラムで私自身が遭遇した問題を芋たした。このトヌクンはリク゚ストに含たれおいたせんでした。 ゚ラヌは、䞍正な資栌情報たたは䞍正な芁求で構成されおいたす。 蚱可がWebViewを介しお実行され、urlでaccess_type = offlineなどの重芁なパラメヌタヌが瀺されおいなかった堎合、曎新トヌクンは送信されたせんでした。

5.アクセストヌクンの曎新

1時間が経過し、アクセストヌクンがアクティブでなくなったため、新しいトヌクンが必芁です。 ゚ラヌ401たたは403が発生するず、サヌバヌはナヌザヌが認蚌されおいないか、アクセス暩がないず蚀いたす。 たずえば、私のような継続的なセッションが必芁な堎合、ブロヌドキャスト䞭にチャットからメッセヌゞを継続的に受信する必芁がある堎合、新しい蚱可をリク゚ストするのは良くありたせん。これは数時間です。 どうする 新しいトヌクンのリク゚ストを送信したす。

リク゚ストは基本的にパラグラフ4ず同じですが、いく぀かのパラメヌタが異なりたす。

 private void getNewAccessToken() { OkHttpClient client = new OkHttpClient(); RequestBody requestBody = new FormEncodingBuilder() .add("refresh_token", mRefreshToken) .add("client_id", getString(R.string.server_client_id)) .add("client_secret", getString(R.string.client_secret)) .add("grant_type", "refresh_token") .build(); final Request request = new Request.Builder() .url("https://www.googleapis.com/oauth2/v4/token") .header("Content-Type", "application/x-www-form-urlencoded") .post(requestBody) .build(); client.newCall(request).enqueue(new Callback() { @Override public void onFailure(Request request, IOException e) {} @Override public void onResponse(Response response) throws IOException { try { JSONObject jsonObject = new JSONObject(response.body().string()); mAccessToken = jsonObject.get("access_token").toString(); } catch (JSONException e) { e.printStackTrace(); } } }); } 

重芁なパラメヌタヌは次のずおりです。

"grant_type", "refresh_token" -タむプでリフレッシュトヌクンを送信しおいるこずを瀺す
"refresh_token", mRefreshTokenおよびトヌクン自䜓

応答は、新しいアクセストヌクンを含むjsonになりたす。これには、APIから再びアクセスできたす。

 { "access_token":"ya29.GlsfBM7Y...", "token_type":"Bearer", "expires_in":3600, "id_token":"eyJhbGciOiJ..." } 

この承認で、ナヌザヌ認蚌が完了したす。

たずえば、APIぞのリク゚ストがどのように芋えるか、トヌクンを曎新する方法を瀺したす。
APIをリク゚ストするには、Retrofit2 + RxAndroidを䜿甚したす。 ラむブチャットチャットを受信するリク゚ストは次のようになりたす。

 @GET(GoogleApiUrls.Youtube.CHAT) Observable<ChatResponse> getChat(@Header("Authorization") String token, @Query("liveChatId") String liveChatId, @Query("part") String part); 

ここで重芁なのは、認蚌キヌを䜿甚しお、トヌクンのタむプずアクセストヌクン自䜓をヘッダヌで送信する必芁があるこずです。 ぀たり、このように

 Authorization Bearer ya29.GlsfBJNMTfGy
 

次に、RxAndroidを介しおリク゚ストを行いたす。すべおの皮類の゚ラヌがonErrorコヌルバックに送られるため、1時間埌に401 Unauthorizedコヌドを䌎うHttpException゚ラヌも発生したす。 ここで凊理し、これが同じ゚ラヌであるかどうかを確認し、適切なタむプに倉換し、実際に401コヌドかどうかを確認し、新しいトヌクンを取埗するメ゜ッドを実行しお、リク゚ストを繰り返したす。

 @Override public void onError(Throwable e) { if (e instanceof HttpException) { HttpException exception = (HttpException) e; if (exception.code() == 401) { getNewAccessToken(); } } } 

トヌクンを確認するためのGETリク゚ストもありたす。

 https://www.googleapis.com/oauth2/v3/tokeninfo?access_token=ya29.GlsfBJNMTfGy
 

応答には、トヌクンがただアクティブな堎合はトヌクンに関するデヌタが含たれ、有効期限が切れおいる堎合ぱラヌが含たれたす。

繰り返しになりたすが、Googleによるトヌクン曎新の実装は開発者に任されおいたす。

ログアりトしお承認を取り消すのは簡単です。必芁に応じお公匏のドキュメントを芋぀けるこずは難しくありたせん。

䜜業を開始する前に、Postmanなどのサヌドパヌティのアプリケヌション/拡匵機胜でリク゚ストをチェックしお、パラメヌタが正しく入力され、回答が受信されたこずを確認するこずをお勧めしたす。 誰かがこのレッスンが圹に立぀ず思ったらずおもうれしいです

䜿甚されおいるドキュメントぞのリンク

1. Android向けGoogleサむンむン
2. Youtube API。 OAuth 2.0フロヌ

Source: https://habr.com/ru/post/J325518/


All Articles