0
0

More than 3 years have passed since last update.

Android端末におけるHUAWEI IDログイン機能の詳細実装手順7-GMS端末でログイン1

Last updated at Posted at 2021-04-01

REST APIによるログイン機能の実装方法

ログイン機能は3つの処理に分けられます。
1.ログイン画面を表示する処理
2.ログイン結果(認証コード)を受け取る処理
3.認証コードをIDトークンに交換する処理

ログイン画面を表示する処理

まず、Webログイン画面のリンクを生成します。
リンク:https://oauth-login.cloud.huawei.com/oauth2/v3/authorize
パラメータ:

パラメータ名
client_id AppGallery Connectのプロジェクト設定に表示されるApp IDです。
AGCUtils.getAppId()を使えば、agconnect-services.jsonの中の"app_id"の値を取得できます
scope "openid+profile"
response_type "code"
access_type 必須ではないが、"offline"を渡せばリフレッシュトークンも返ってきます
redirect_uri AppGallery ConnectのAccount Kitに設定したリダイレクトURIです

こちらのページも合わせてご参照ください:

NonHmsHuaweiIdLogic.kt
companion object {
    private const val AUTH_LINK = "https://oauth-login.cloud.huawei.com/oauth2/v3/authorize"
    private const val SCOPE = "openid+profile"
    const val REDIRECT_URI = "http://www.hmsaccountkit.com"
}

override fun signIn(activity: Activity) {
    activity.startActivity(generateSignInIntent(activity.applicationContext))
}

private fun generateSignInIntent(context: Context): Intent {
    return Intent(context, WebViewActivity::class.java).apply {
        putExtra("link", createAuthLink(context))
    }
}

private fun mapToString(map: Map<String, String>): String {
    var query = ""
    var count = 0
    map.forEach { (key, value) ->
        if (count > 0) {
            query += "&"
        }
        query += "$key=$value"
        count++
    }

    return query
}

private fun createAuthLink(context: Context): String {
    val map: Map<String, String> = hashMapOf(
        "client_id" to AGCUtils.getAppId(context),
        "scope" to SCOPE,
        "response_type" to "code",
        "access_type" to "offline",
        "redirect_uri" to REDIRECT_URI
    )

    return AUTH_LINK + "?" + mapToString(map)
}

Webログイン画面のリンクをWebViewの中に表示します。

ログイン結果(認証コード)を受け取る処理

ログイン結果はリダイレクトURIのパラメータとして返ってきます。そのため、次のようにWebViewActivityクラスを作ります。

まず、レイアウトはWebViewのみで大丈夫です。

web_view_activity.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/main_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >

        <WebView
            android:id="@+id/web_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            />

    </androidx.constraintlayout.widget.ConstraintLayout>

</layout>

次はWebViewActivityクラスです。
まず、webviewのJavaScriptを有効にします。
さらに、shouldOverrideUrlLoading()の中で、開くリンクがリダイレクトURIと一致しているかどうかをチェックし、一致していれば、それを開かずに、パラメータ(認証コード)と一緒にMainActivityに転送します。

WebViewActivity.kt
class WebViewActivity : AppCompatActivity() {

    private lateinit var binding: WebViewActivityBinding

    @SuppressLint("SetJavaScriptEnabled")
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.inflate<WebViewActivityBinding>(
                layoutInflater,
                R.layout.web_view_activity,
                null,
                false
        )
        setContentView(binding.root)

        with(binding) {
            webView.settings.javaScriptEnabled = true
            webView.settings.domStorageEnabled = true

            webView.webViewClient = object : WebViewClient() {
                override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean {
                    request?.url?.let { url ->
                        if (url.toString().contains(NonHmsHuaweiIdLogic.REDIRECT_URI)) {
                            val intent = Intent(applicationContext, MainActivity::class.java).apply {
                                data = url
                                flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
                            }
                            startActivity(intent)
                            return true
                        }

                        view?.loadUrl(url.toString())
                    }

                    return false
                }
            }

            intent?.getStringExtra("link")?.let { url ->
                webView.loadUrl(url)
            }
        }

        window.setFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED)
    }

}

認証コードはIntentのdataに格納されます。

intent.data?.getQueryParameter("code")

認証コードをIDトークンに交換する処理

認証コードはそのまま使えません。ユーザー情報が入っているIDトークンに交換する必要があります。

まずIntentから認証コードを取得します

MainActivity.kt
override fun onStart() {
    super.onStart()

    huaweiIdLogic.update(applicationContext, intent)
}
NonHmsHuaweiIdLogic.kt
override fun update(context: Context, intent: Intent) {
    intent.data?.getQueryParameter("code")?.let { code ->
        signInWithCode(context, code)
    }
}

次はAPIを呼び出し、認証コードをIDトークンに交換します。
API:https://oauth-login.cloud.huawei.com/oauth2/v3/token
メソッド:POST
パラメータ:

パラメータ名
grant_type "authorization_code"
client_id AppGallery Connectのプロジェクト設定に表示されるApp IDです。
AGCUtils.getAppId()を使えば、agconnect-services.jsonの中の"app_id"の値を取得できます
client_secret AppGallery Connectのプロジェクト設定に表示されるApp Secretです。
code 認証コード
redirect_uri AppGallery ConnectのAccount Kitに設定したリダイレクトURIです

こちらのページも合わせてご参照ください:

NonHmsHuaweiIdLogic.kt
companion object {
    private const val GET_ID_TOKEN_URL = "https://oauth-login.cloud.huawei.com/oauth2/v3/token"
}

private fun signInWithCode(context: Context, code: String) {
    val context: Context = context ?: return
    getIdToken(
        context,
        code,
        object : Response.Listener<String> {
            override fun onResponse(response: String?) {
                response?.let { response ->
                    val jsonObject = JSONObject(response)
                    if (jsonObject.has("id_token")) {
                        // 権限を取り消すときにアクセストークンが必要
                        val accessToken = jsonObject.getString("access_token")

                        // IDトークンにユーザー情報が入っている
                        val idToken = jsonObject.getString("id_token")
                    }
                }
            }
        },
        object : Response.ErrorListener {
            override fun onErrorResponse(error: VolleyError?) {
                error?.printStackTrace()
            }
        }
    )
}

private fun getIdToken(
    context: Context,
    authorizationCode: String,
    listener: Response.Listener<String>,
    errorListener: Response.ErrorListener
) {
    val queue = Volley.newRequestQueue(context)

    val postRequest: StringRequest = object : StringRequest(
        Method.POST,
        GET_ID_TOKEN_URL,
        listener,
        errorListener
    ) {
        override fun getBodyContentType(): String {
            return "application/x-www-form-urlencoded"
        }

        override fun getParams(): Map<String, String> {
            val params: MutableMap<String, String> = HashMap()
            params["grant_type"] = "authorization_code"
            params["client_id"] = AGCUtils.getAppId(context) ?: ""
            params["client_secret"] = APP_SECRET
            params["code"] = authorizationCode
            params["redirect_uri"] = REDIRECT_URI
            return params
        }
    }

    queue.add(postRequest)
}

これでIDトークンが取得できます。
また、アクセストークンも同時に返ってきます。アクセストークンは権限取り消すときに必要です。

シリーズ

GitHub

参考

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0