チュートリアル: ネイティブ認証を使用して Android アプリにサインインを追加する

このチュートリアルでは、Android モバイル アプリでネイティブ認証を使用し、メールのワンタイム パスコードまたはユーザー名とパスワードを使ってユーザーのサインインとサインアウトを行う方法でについて説明します。

このチュートリアルでは、次の作業を行う方法について説明します。

  • メールのワンタイム パスコードまたはユーザー名 (メール アドレス) とパスワードを使ってユーザーのサインインを行います。
  • ユーザーのサインアウト。
  • サインイン エラーを処理する

前提条件

ユーザーのサインイン

ワンタイム パスコードを使用してユーザーのサインインを行うには、メール アドレスを収集し、ユーザーが自身のメール アドレスを検証できるようにワンタイム パスコードを含むメールを送信します。 有効なワンタイム パスコードを入力すると、アプリはユーザーをサインインさせます。

ユーザー名 (メール アドレス) とパスワードを使用してユーザーのサインインを行うには、ユーザーからメール アドレスとパスワードを収集します。 ユーザー名とパスワードが有効な場合、アプリによってユーザーのサインインが行われます。

ユーザーのサインインを行うには、次の操作を行う必要があります。

  1. 以下にユーザー インターフェイス (UI) を作成します。

    • ユーザーからメール アドレスを収集します。 入力に検証を追加して、ユーザーが有効なメール アドレスを入力していることを確認します。
    • ユーザー名 (メール アドレス) とパスワードを使ってサインインを行う場合は、パスワードを収集します。
    • 電子メール ワンタイム パスコードでサインインする場合は、ユーザーから電子メールのワンタイム パスコードを収集します。
    • 電子メールのワンタイム パスコードでサインインする場合は、ワンタイム パスコードを再送信します (推奨)。
  2. UI で、次のコード スニペットに示すように select イベントによってサインインを開始するボタンを追加します。

     CoroutineScope(Dispatchers.Main).launch {
         val actionResult = authClient.signIn(
             username = emailAddress
             //password = password, Pass 'password' param if you sign in with username (email) and password
         )
         if (actionResult is SignInResult.CodeRequired) {
             val nextState = actionResult.nextState
             val submitCodeActionResult = nextState.submitCode(
                 code = code
             )
             if (submitCodeActionResult is SignInResult.Complete){
                 // Handle sign in success
                 val accountState = submitCodeActionResult.resultValue
                 val accessTokenResult = accountState.getAccessToken()
                 if (accessTokenResult is GetAccessTokenResult.Complete) {
                     val accessToken = accessTokenResult.resultValue.accessToken
                     val idToken = accountState.getIdToken()
                 }
             }
         }
     }
    

    例えば、ユーザーが電子メールとパスワードでサインインするなど、ユーザーがパスコードを送信する必要がない場合は以下のコード スニペットを使用します。

        CoroutineScope(Dispatchers.Main).launch {
            val actionResult = authClient.signIn(
                username = emailAddress,
                password = password
            )
            if (actionResult is SignInResult.Complete) -> {
                // Handle sign in success
                val accountState = actionResult.resultValue
                val accessTokenResult = accountState.getAccessToken()
                if (accessTokenResult is GetAccessTokenResult.Complete) {
                        val accessToken = accessTokenResult.resultValue.accessToken
                        val idToken = accountState.getIdToken()
                    }
            }
        }
    
    • サインイン フローを開始するには、SDK の signIn(username) または signIn(username, password) メソッドを使用します。
    • メソッドのパラメータである username は、ユーザーから収集したメール アドレスです。
    • サインイン方法がユーザー名 (メール アドレス) とパスワードである場合、メソッドのパラメーター password はユーザーから収集したパスワードです。
    • 最も一般的なシナリオでは、signIn(username) または signIn(username, password) によって結果 SignInResult.CodeRequired が返されます。これは、ユーザーのメール アドレスに送信されたメールのワンタイム パスコードをアプリから送信することを SDK で想定していることを示します。
    • SignInResult.CodeRequired オブジェクトには新しい状態参照が含まれており、actionResult.nextState を通じて取得できます。
    • 新しい状態により、次の 2 つの新しいメソッドにアクセスできるようになります。
      • submitCode() では、アプリによってユーザーから収集するメールのワンタイム パスコードを送信します。
      • resendCode() では、ユーザーがコードを受信しなかった場合にメールのワンタイム パスコードを再送信します。

サインイン エラーを処理する

サインイン中、すべてのアクションが成功するわけではありません。 たとえば、ユーザーが存在しないメール アドレスでサインインしようとしたり、無効なコードを送信したりすることがあります。

サインインの開始エラーを処理する

signIn(username) または signIn(username, password) メソッドでエラーを処理するには、次のコード スニペットを使用します。

val actionResult = authClient.sign(
    username = emailAddress
    //password = password, Pass 'password' param if you sign in with username (email) and password
)
if (actionResult is SignInResult.CodeRequired) {
    // Next step: submit code
} else if (actionResult is SignInError) {
    // Handle sign in errors
    when {
         actionResult.isUserNotFound() -> {
             // Handle "user not found" error
         }
         actionResult.isAuthNotSupported() -> {
         // Handle "authentication type not support" error
         }
         actionResult.isInvalidCredentials() -> {
             // Handle specific errors
         }
         else -> {
             // Handle other errors
         }
     }
}
  • SignInError は、signIn() によって返された失敗したアクション結果を示します。そのため、アクション結果には新しい状態への参照が含まれません。
  • actionResult is SignUpError の場合、Android SDK には、特定のエラーをさらに分析できるようにするユーティリティ メソッドが用意されています。
    • メソッド isUserNotFound() では、存在しないユーザー名 (メール アドレス) を使用してユーザーがサインインしているかどうかを確認します。
    • メソッド isBrowserRequired() では、認証フローを完了するためにブラウザー (Web フォールバック) が必要であるかどうかを確認します。 このシナリオは、認証フローを完了するのにネイティブ認証だけでは十分ではない場合に発生します。 たとえば、管理者は認証方法としてメール アドレスとパスワードを構成しても、アプリが password をチャレンジ型として送信できないか、単にそれをサポートしていない場合などです。 これが発生するシナリオを処理するには、Android アプリでの Web フォールバックのサポートに関する記事の手順を使用します。
    • メソッド isAuthNotSupported() は、Microsoft Entra でサポートされていないチャレンジ型 (oob および password 以外のチャレンジ型の値) をアプリで送信しているかどうかを確認します。 チャレンジ型の詳細を参照してください。
    • ユーザー名 (メール アドレス) とパスワードのサインインの場合、メソッド isInvalidCredentials() を使ってユーザー名とパスワードの組み合わせが間違っているかどうかを確認します。

送信コード エラーを処理する

submitCode() メソッドでエラーを処理するには、次のコード スニペットを使用します:

val submitCodeActionResult = nextState.submitCode(
    code = code
)
if (submitCodeActionResult is SignInResult.Complete) {
    // Sign in flow complete, handle success state.
} else if (submitCodeActionResult is SubmitCodeError && submitCodeActionResult.isInvalidCode()) {
    // Handle "invalid code" error
}
  • SubmitCodeError エラーは、submitCode() によって返されたアクションが失敗した結果を示します。そのため、アクションの結果には新しい状態への参照は含まれません。
  • isInvalidCode() では特定のエラーがチェックされます。 この場合、アクションを再実行するには、前の状態参照を使用する必要があります。

新しいメールのワンタイム パスコードを取得するには、以下のコード スニペットを使用します。

val submitCodeActionResult = nextState.submitCode(
    code = code
)
if (submitCodeActionResult is SignInError && submitCodeActionResult.isInvalidCode) {
    // Inform the user that the submitted code was incorrect or invalid, then ask them to input a new email one-time passcode
    val newCode = retrieveNewCode()
    nextState.submitCode(
        code = newCode
    )
}

アプリでユーザーを正常にサインアップさせるために必要なすべての手順を完了しました。 アプリケーションをビルドして実行します。 問題がなければ、メール アドレスを指定し、コードをメールで受け取り、それを使用してユーザーのサインインを完了することができます。

ID トークン要求を読み取る

アプリが ID トークンを獲得したら、現在のアカウントに関連付けられている要求を取得できます。 これを行うには、次のコード スニペットを使用します。

val preferredUsername = accountState.getClaims()?.get("preferred_username")
val city = accountState.getClaims()?.get("City")
val givenName = accountState.getClaims()?.get("given_name")
//custom attribute
val loyaltyNumber = accountState.getClaims()?.get("loyaltyNumber")

要求値へのアクセスに使用するキーは、ユーザー属性をトークン要求として追加するときに指定する名前です。

組み込みおよびカスタム属性をトークン要求として追加する方法については、「トークン要求にユーザー属性を追加する」記事を参照してください。

ユーザーのサインアウト

ユーザーをサインアウトさせるには、キャッシュに現在保存されているアカウントを削除する必要があります。

  1. 以下を含むカスタム ユーザー インターフェイス (UI) を作成します。

    • サインアウト要求を送信するためにユーザーが選択するサインアウト ボタン。
  2. ユーザーをサインアウトさせるには、次のコードを使用します。

    private fun performSignOut(accountState: AccountState) {
         CoroutineScope(Dispatchers.Main).launch {
            val accountResult = authClient.getCurrentAccount()
             if (accountResult is GetAccountResult.AccountFound) {
                 val signOutResult = accountResult.resultValue.signOut()
                 if (signOutResult is SignOutResult.Complete) {
                     // Show sign out successful UI
                 }
             }
         }
     }
    

サインアウト エラーを処理する

サインアウトでエラーが発生しないようにする必要があります。 エラーが発生した場合は、次のコード スニペットを使用してエラー結果を検査します。

val actionResult = accountResult.signOut()
if (actionResult is SignOutResult.Complete) {
    // Show sign out successful UI
} else {
    // Handle errors
}

必ず import ステートメントを含めます。 Android Studio では、自動的に import ステートメントが含められます。

アプリでユーザーを正常にサインアウトさせるために必要なすべての手順を完了しました。 アプリケーションをビルドして実行します。 問題がなければ、サインアウト ボタンを選択して正常にサインアウトできます。

カスタム クレーム プロバイダーの構成

外部システムからのクレームをアプリに発行されるトークンに追加する場合は、カスタム クレーム プロバイダーを使用します。 カスタム クレーム プロバイダーは、外部システムからクレームを取り込むために、外部 REST API を呼び出すカスタム認証拡張機能で構成されています。

カスタム クレーム プロバイダーの構成の手順に従って、外部システムからのクレームをセキュリティ トークンに追加します。