【Android】Auth0 公式のクイックスタートを参考に Kotlin でユニバーサルログインを組み込んでみた

はじめに

テントの中から失礼します、IoT 事業部のてんとタカハシです!

Android アプリ開発も Auth0 も Kotlin も初心者なのですが、これらを使ってユニバーサルログインをアプリに組み込む機会がありましたので、手順を残しておこうと思います。

基本的には Auth0 公式のクイックスタートを参考にすれば何となく実装できそうなのですが、初心者の私はそこそこ躓きました。同じような初心者の方にとって参考になれば嬉しいです。

環境

  • macOS Catalina v10.15.7
  • Android Studio 4.2.1

参考

主に Auth0 公式のクイックスタートを参考にしました。

Android プロジェクトを作成する

Empty Activityを選択します。

プロジェクト名はsample-auth0-loginとしています。Auth0 を使用する場合は、Android の API レベルを 21 以上に設定する必要があることに注意してください。

Android API version 21 or newer is required.

Auth0 Docs > Auth0 Libraries > Auth0.Android

Auth0 アプリケーションを作成する

アプリケーション名はsample-auth0-loginとしています。アプリケーションタイプはNativeを選択します。

作成したアプリケーションの Settings タブを開くと、ドメイン名とクライアント ID が表示されます。この2つは後ほどログイン画面実装時に使用します。

下にスクロールして、Allowed Callback URLs と Allowed Logout URLs を設定します。

どちらもdemo://<Auth0 ドメイン名>/android/<Android プロジェクトのパッケージ名>/callback で入力してください。本記事の例では、Android プロジェクトのパッケージ名がcom.example.sample_auth0_loginになります。

その後は、ページ下部にある Save Changes ボタンをクリックして設定を保存してください。

Auth0 SDK を使用するための準備を行う

Auth0 Android SDK を追加する

app 内の build.gradle に下記を追記します。その後、Gradle ファイルを同期してください。

app/build.gradle

...

dependencies {

    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    implementation 'androidx.core:core-ktx:1.6.0'
    implementation 'androidx.appcompat:appcompat:1.3.1'
    implementation 'com.google.android.material:material:1.4.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
    testImplementation 'junit:junit:4.+'
    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
    
    // ★
    implementation 'com.auth0.android:auth0:2.+'
}

ビルド変数を設定する

Auth0 Android SDK の内部で使用されるビルド変数を設定します。

app/build.gradle

...

android {
    compileSdkVersion 30
    buildToolsVersion "30.0.3"

    defaultConfig {
        applicationId "com.example.sample_auth0_login"
        minSdkVersion 21
        targetSdkVersion 30
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

        // ★
        manifestPlaceholders = [auth0Domain: "@string/com_auth0_domain", auth0Scheme: "@string/com_auth0_scheme"]
    }

...

実際の値は strings.xml に記載します。Auth0 のクライアント ID、ドメイン名はそれぞれの環境に合わせて変更してください。本記事ではスキーマをdemoにします。

app/src/main/res/values/strings.xml

<resources>
    <string name="app_name">Auth0LoginSample</string>

    <!-- ★ -->
    <string name="com_auth0_client_id"><Auth0 のクライアント ID></string>
    <string name="com_auth0_domain"><Auth0 のドメイン名></string>
    <string name="com_auth0_scheme">demo</string>
</resources>

インターネットに接続するための権限を設定する

マニフェストファイルに権限を追記します。

app/src/main/AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.sample_auth0_login">
    
    <!-- ★ -->
    <uses-permission android:name="android.permission.INTERNET" />

    <application
    
    ...

ログイン/ログアウト処理を持つモジュールを実装する

後述するログイン用の画面、ログアウト用の画面から Auth0 の処理を呼び出せるようにモジュールを実装しておきます。

Auth0(context)としている箇所で、strings.xml に記載したcom_auth0_client_idcom_auth0_domainの値が自動的に読み込まれます。

app/src/main/java/com/example/sample_auth0_login/AuthModule.kt

package com.example.sample_auth0_login

import android.content.Context
import com.auth0.android.Auth0
import com.auth0.android.provider.WebAuthProvider

internal object AuthModule {
    fun loginAuthBuilder(context: Context): WebAuthProvider.Builder {
        return WebAuthProvider.login(Auth0(context))
            .withScheme(context.getString(R.string.com_auth0_scheme))
            .withScope("openid profile email")
    }

    fun logoutAuthBuilder(context: Context): WebAuthProvider.LogoutBuilder {
        return WebAuthProvider.logout(Auth0(context))
            .withScheme(context.getString(R.string.com_auth0_scheme))
    }
}

ログイン画面を実装する

MainActivity にログイン用のボタンを配置します。ログインボタンがクリックされると、ユニバーサルログイン画面が起動して、ログインが成功すれば、ContentActivity に画面遷移するようにしています。

app/src/main/java/com/example/sample_auth0_login/MainActivity.kt

package com.example.sample_auth0_login

import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import com.auth0.android.authentication.AuthenticationException
import com.auth0.android.callback.Callback
import com.auth0.android.result.Credentials

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // ログインボタンをクリックするとユニバーサルログイン画面が起動する
        val loginButton = findViewById<Button>(R.id.loginButton)
        loginButton.setOnClickListener {
            loginWithBrowser()
        }
    }

    private fun loginWithBrowser() {
        AuthModule.loginAuthBuilder(this)
            .start(this, object : Callback<Credentials, AuthenticationException> {
                override fun onSuccess(credentials: Credentials) {
                    // ログインに成功したら ContentActivity に遷移する
                    val intent = Intent(applicationContext, ContentActivity::class.java)
                    startActivity(intent)
                }

                override fun onFailure(exception: AuthenticationException) {}
            })
    }

}

ログアウト画面を実装する

ContentActivity にログアウト用のボタンを配置します。ログアウトボタンがクリックされると、ログアウト処理が走り、MainActivity に画面遷移するようにしています。

app/src/main/java/com/example/sample_auth0_login/ContentActivity.kt

package com.example.sample_auth0_login

import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import com.auth0.android.authentication.AuthenticationException
import com.auth0.android.callback.Callback

class ContentActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_content)

        // ログインボタンをクリックするとログアウト処理が走る
        val logoutButton = findViewById<Button>(R.id.logoutButton)
        logoutButton.setOnClickListener {
            logout()
        }
    }

    private fun logout() {
        AuthModule.logoutAuthBuilder(this)
            .start(this, object: Callback<Void?, AuthenticationException> {
                override fun onSuccess(payload: Void?) {
                    // ログアウトに成功したら MainActivity に遷移する
                    val intent = Intent(applicationContext, MainActivity::class.java)
                    startActivity(intent)
                }

                override fun onFailure(error: AuthenticationException) {}
            })
    }
}

デモ

ログインボタンをクリックすると、

ユニバーサルログイン画面が起動します。

ログインが成功すると、ログアウトボタンが設置されている画面に遷移します。

ログアウトボタンをクリックすると、ログイン画面に戻ります。

おわりに

Android アプリに Auth0 のユニバーサルログインを組み込むことができました。公式のクイックスタートには、プロフィールの変更などの対応についても記載がありますので、そちらの理解も深めていきたいなと思います。

今回は以上になります。最後まで読んで頂きありがとうございました!