前置き
Android端末において、Googleログインの実装方法として、よく使われているのはGoogle Play開発者サービスを利用するやり方です。しかし、Google Play開発者サービスが無効になった場合、Googleログインができなくなります。また、国や地域、メーカーによって、そもそもAndroid端末にGoogle Play開発者サービスが入っていないことがあります。
AppAuth for Androidを利用するメリット
- GoogleのOAuthでは、webviewをサポートしなくなりました。AppAuth for AndroidはChrome Custom Tabをサポートしているので、GoogleのOAuthを利用できます。(https://developers-jp.googleblog.com/2016/09/modernizing-oauth-interactions-in-native-apps.html)
- Google Play開発者サービスがいりません。
- 独自実装より楽です。
AppAuth for Androidの導入方法
AppAuth for Androidの使い方で基本的な使い方を解説しました。ここではGoogleログインに特化した導入方法を説明します。
Google Cloud Platformの登録
- Google Cloud Platformでプロジェクトを作成します。
- [APIとサービス]→[OAuth同意画面]に入り、OAuth 同意画面の設定をします。スコープもここで設定します。
- [APIとサービス]→[認証情報]に入り、[認証情報を作成]→[OAuthクライアントID]をクリックし、[アプリケーションの種類]で“Android”を選び、その他の必要な情報を設定します。
- 設定が終わったら、クライアントIDが表示されます。このクライアントIDはソースコードで使われます。
ライブラリの導入
appモジュールのbuild.gradleに
- AppAuth for Android(net.openid:appauth)
-
JWT Decoding library for Android(com.auth0.android:jwtdecode)
を追加します。
build.gradle
// AppAuth SDK:最新バージョンはhttps://search.maven.org/artifact/net.openid/appauth
implementation 'net.openid:appauth:0.11.1'
// For decode ID Token:最新バージョンはhttps://mvnrepository.com/artifact/com.auth0.android/jwtdecode
implementation 'com.auth0.android:jwtdecode:2.0.0'
AndroidManifest.xmlの編集
AndroidManifest.xmlに次のようにリダイレクト用のactivityを追加します。
AndroidManifest.xml
...
<activity
android:name="net.openid.appauth.RedirectUriReceiverActivity"
tools:node="replace">
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="{アプリのパッケージ名}"/>
</intent-filter>
</activity>
...
AuthStateとAuthorizationServiceConfigurationを用意
空のAuthStateとAuthorizationServiceConfigurationを生成します。
private val appAuthState: AuthState = AuthState.jsonDeserialize("{}")
private val config = AuthorizationServiceConfiguration(
Uri.parse("https://accounts.google.com/o/oauth2/v2/auth"),
Uri.parse("https://www.googleapis.com/oauth2/v4/token")
)
サインイン
サインイン画面を表示
- AuthorizationRequestを生成します。
- スコープを設定します。
- AuthorizationServiceのgetAuthorizationRequestIntentで、サインインのIntentを作ります。
- startActivityForResultでサインインのIntentを起動します。
fun signIn() {
val authorizationRequest = AuthorizationRequest
.Builder(
config,
{クライアントID},
ResponseTypeValues.CODE,
Uri.parse({アプリのパッケージ名}:/{適当なパス})
)
.setScope({Google Cloud Platformで設定したスコープ。例:openid email profile})
.build()
val intent = AuthorizationService(context).getAuthorizationRequestIntent(authorizationRequest)
startActivityForResult(intent, requestCode)
}
サインインの認証結果を取得
- onActivityResultでサインインの認証結果を受け取ります。
- AuthStateを更新します。
認証成功の条件
AuthorizationResponseが空ではなく、かつAuthorizationExceptionが空である
認証失敗の条件
AuthorizationResponseが空、またはAuthorizationExceptionが空ではない
fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
val authorizationResponse = AuthorizationResponse.fromIntent(data)
val exception = AuthorizationException.fromIntent(data)
appAuthState.update(authorizationResponse, exception)
if (exception != null || authorizationResponse == null) {
// 認証失敗
} else {
// 認証成功
}
}
各トークンを取得
- AuthorizationResponseを使ってトークンを要求します。
- (1)のレスポンスを使ってAuthStateを更新します。
- AuthStateのaccessTokenにアクセストークンが入っています。
- AuthStateのrefreshTokenにリフレッシュトークンが入っています。
- AuthStateのidTokenにIDトークンが入っています。
AuthorizationService(context)
.performTokenRequest(authorizationResponse.createTokenExchangeRequest()) { tokenResponse, authorizationException ->
appAuthState.update(tokenResponse, authorizationException)
tokenResponse?.let {
// アクセストークン=appAuthState.accessToken
// リフレッシュトークン=appAuthState.refreshToken
// IDトークン=appAuthState.idToken
}
}
IDトークンをユーザー情報に変換
JWTでIDトークンを解凍したら、次のユーザー情報が取得できます。
val jwt = JWT(idToken)
// Open ID
val openId = jwt.subject
// 姓名
val name = jwt.claims["name"]?.asString()
// 姓
val familyName = jwt.claims["family_name"]?.asString()
// 名
val givenName = jwt.claims["given_name"]?.asString()
// プロフィール画像のURL
val pictureUrl = jwt.claims["picture"]?.asString()
// メールアドレス
val email = jwt.claims["email"]?.asString()
// メールアドレスは認証済みかどうか
val emailVerified = jwt.claims["email_verified"]?.asBoolean()