チュートリアル: ネイティブ認証を使用して iOS アプリにサインインとサインアウトを追加する
適用対象: iOS (Swift) macOS (Swift)
このチュートリアルでは、ネイティブ認証を使用して、iOS アプリでメールのワンタイム パスコードまたはユーザー名とパスワードを使用してユーザーをサインインおよびサインアウトする方法を説明します。
このチュートリアルでは、次の作業を行う方法について説明します。
- メールのワンタイム パスコードまたはユーザー名 (メール アドレス) とパスワードを使ってユーザーのサインインを行います。
- ユーザーのサインアウト。
- サインイン エラーを処理する
前提条件
- チュートリアル: ネイティブ認証用に iOS アプリを準備します。
- パスワード付きメールを使用してサインインする場合は、サインアップおよびサインインのユーザー フローを作成するときに、パスワード付きメールを使用するようにユーザー フローを構成します。
ユーザーのサインイン
メールのワンタイム パスコード フローを使用してユーザーをサインインさせるには、メールをキャプチャし、ユーザーのメールを検証するためのワンタイム パスコードを含むメールを送信します。 有効なワンタイム パスコードを入力すると、アプリはユーザーをサインインさせます。
パスワード付きメール フローを使用してユーザーをサインインさせるには、メール アドレスとパスワードをキャプチャします。 ユーザー名とパスワードが有効な場合、アプリによってユーザーのサインインが行われます。
ユーザーのサインインを行うには、次の操作を行う必要があります。
以下にユーザー インターフェイス (UI) を作成します。
- ユーザーからメール アドレスを収集します。 入力に検証を追加して、ユーザーが有効なメール アドレスを入力していることを確認します。
- ユーザー名 (メール アドレス) とパスワードを使ってサインインを行う場合は、パスワードを収集します。
- 電子メール ワンタイム パスコードでサインインする場合は、ユーザーから電子メールのワンタイム パスコードを収集します。
- メール ワンタイム パスコードでサインインした場合に、ユーザーがワンタイム パスコードを再送信できるようにするボタンを追加します。
UI で、次のコード スニペットに示すように select イベントによってサインインを開始するボタンを追加します。
@IBAction func signInPressed(_: Any) { guard let email = emailTextField.text else { resultTextView.text = "email not set" return } nativeAuth.signIn(username: email, delegate: self) }
メール ワンタイム パスコード フローを使用してユーザーをサインインさせるには、次のコード スニペットを使用します。
nativeAuth.signIn(username: email, delegate: self)
渡されたデリゲート オブジェクトのメソッドの 1 つを呼び出すことによって非同期的に応答する
signIn(username:delegate)
メソッドは、SignInStartDelegate
プロトコルを実装する必要があります。 ユーザーがメール送信フォームで指定したメール アドレスを渡し、self
をデリゲートとして渡します。パスワード付きメール フローを使用してユーザーをサインインさせるには、次のコード スニペットを使用します。
nativeAuth.signIn(username: email, password: password, delegate: self)
signIn(username:password:delegate)
メソッドでは、ユーザーが指定したメール アドレスとパスワードを渡し、SignInStartDelegate
プロトコルに準拠するデリゲート オブジェクトを渡します。 この例では、self
を渡します。メール ワンタイム パスコード フローを使用するときに
SignInStartDelegate
プロトコルを実装するには、次のコード スニペットを使用します。extension ViewController: SignInStartDelegate { func onSignInStartError(error: MSAL.SignInStartError) { resultTextView.text = "Error signing in: \(error.errorDescription ?? "no description")" } func onSignInCodeRequired( newState: MSAL.SignInCodeRequiredState, sentTo: String, channelTargetType: MSAL.MSALNativeAuthChannelType, codeLength: Int ) { resultTextView.text = "Verification code sent to \(sentTo)" } }
signIn(username:delegate)
によってデリゲート メソッドが呼び出されます。 最も一般的なシナリオでは、ユーザーのメール アドレスを検証するためのコードが送信されたことを示すために、onSignInCodeRequired(newState:sentTo:channelTargetType:codeLength)
が呼び出されます。 このデリゲート メソッドには、コードが送信された場所と、コードに含まれる桁数の詳細に加えて、SignInCodeRequiredState
型のnewState
パラメータもあります。このパラメータにより、次の 2 つの新しいメソッドにアクセスできます。submitCode(code:delegate)
resendCode(delegate)
submitCode(code:delegate)
を使用して、ユーザーがワンタイム パスコード フォームに指定するワンタイム パスコードを送信するには、次のコード スニペットを使用します。newState.submitCode(code: userSuppliedCode, delegate: self)
submitCode(code:delegate)
は、ワンタイム パスコードとデリゲート パラメータを受け付けます。 コードを送信した後、SignInVerifyCodeDelegate
プロトコルを実装してワンタイム パスコードを検証する必要があります。SignInVerifyCodeDelegate
プロトコルをクラスの拡張機能として実装するには、次のコード スニペットを使用します。extension ViewController: SignInVerifyCodeDelegate { func onSignInVerifyCodeError(error: MSAL.VerifyCodeError, newState: MSAL.SignInCodeRequiredState?) { resultTextView.text = "Error verifying code: \(error.errorDescription ?? "no description")" } func onSignInCompleted(result: MSALNativeAuthUserAccountResult) { resultTextView.text = "Signed in successfully." result.getAccessToken(delegate: self) } }
最も一般的なシナリオでは、ユーザーがサインインしたことを示す
onSignInCompleted(result)
の呼び出しを受け取ります。 結果を使用して、access token
を取得できます。getAccessToken(delegate)
はデリゲート パラメータを受け取るため、CredentialsDelegate
プロトコルで必要なメソッドを実装する必要があります。最も一般的なシナリオでは、ユーザーが
access token
を取得したことを示すonAccessTokenRetrieveCompleted(result)
の呼び出しを受け取ります。extension ViewController: CredentialsDelegate { func onAccessTokenRetrieveError(error: MSAL.RetrieveAccessTokenError) { resultTextView.text = "Error retrieving access token" } func onAccessTokenRetrieveCompleted(result: MSALNativeAuthTokenResult) { resultTextView.text = "Signed in. Access Token: \(result.accessToken)" } }
パスワード付きメール フローを使用するときに
SignInStartDelegate
プロトコルを実装するには、次のコード スニペットを使用します。extension ViewController: SignInStartDelegate { func onSignInStartError(error: MSAL.SignInStartError) { resultTextView.text = "Error signing in: \(error.errorDescription ?? "no description")" } func onSignInCompleted(result: MSAL.MSALNativeAuthUserAccountResult) { // User successfully signed in } }
最も一般的なシナリオでは、ユーザーがサインインしたことを示す
onSignInCompleted(result)
の呼び出しを受け取ります。 結果を使用して、access token
を取得できます。getAccessToken(delegate)
はデリゲート パラメータを受け取るため、CredentialsDelegate
プロトコルで必要なメソッドを実装する必要があります。最も一般的なシナリオでは、ユーザーが
access token
を取得したことを示すonAccessTokenRetrieveCompleted(result)
の呼び出しを受け取ります。extension ViewController: CredentialsDelegate { func onAccessTokenRetrieveError(error: MSAL.RetrieveAccessTokenError) { resultTextView.text = "Error retrieving access token" } func onAccessTokenRetrieveCompleted(result: MSALNativeAuthTokenResult) { resultTextView.text = "Signed in. Access Token: \(result.accessToken)" } }
サインイン エラーを処理する
サインイン中、すべてのアクションが成功するわけではありません。 たとえば、ユーザーが存在しないメール アドレスでサインインしようとしたり、無効なコードを送信したりすることがあります。
signIn(username)
またはsignIn(username, password)
メソッドでエラーを処理するには、次のコード スニペットを使用します。func onSignInStartError(error: MSAL.SignInStartError) { if error.isUserNotFound || error.isInvalidUsername { resultTextView.text = "Invalid username" } else { resultTextView.text = "Error signing in: \(error.errorDescription ?? "no description")" } }
submitCode()
メソッドでエラーを処理するには、次のコード スニペットを使用します:func onSignInVerifyCodeError(error: MSAL.VerifyCodeError, newState: MSAL.SignInCodeRequiredState?) { if error.isInvalidCode { // Inform the user that the submitted code was incorrect and ask for a new code to be supplied let userSuppliedCode = retrieveNewCode() newState?.submitCode(code: userSuppliedCode, delegate: self) } else { resultTextView.text = "Error verifying code: \(error.errorDescription ?? "no description")" } }
ユーザーが誤ったメール確認コードを入力した場合、エラー ハンドラーには
SignInCodeRequiredState
への参照が含まれており、それを更新されたコードの送信に使用することができます。 以前にSignInVerifyCodeDelegate
プロトコルを実装した際は、onSignInVerifyCodeError(error:newState)
デリゲート関数を処理したときに単にエラーを表示していました。
ID トークン要求を読み取る
アプリが ID トークンを獲得したら、現在のアカウントに関連付けられている要求を取得できます。 これを行うには、次のコード スニペットを使用します。
func onSignInCompleted(result: MSAL.MSALNativeAuthUserAccountResult) {
let claims = result.account.accountClaims
let preferredUsername = claims?["preferred_username"] as? String
}
要求値へのアクセスに使用するキーは、ユーザー属性をトークン要求として追加するときに指定する名前です。
組み込みおよびカスタム属性をトークン要求として追加する方法については、「トークン要求にユーザー属性を追加する」記事を参照してください。
ユーザーをサインアウトする
ユーザーをサインアウトさせるには、onSignInCompleted
コールバックで受け取った MSALNativeAuthUserAccountResult
への参照を使用するか、getNativeAuthUserAccount()
を使用して、サインインしているアカウントをキャッシュから取得し、accountResult
メンバー変数に参照を格納します。
こちらの説明に従って、プロジェクトのキーチェーン グループを構成します。
ViewController
クラスに新しいメンバー変数var accountResult: MSALNativeAuthUserAccountResult?
を追加します。nativeAuth
が正常に初期化された後に次の行を追加して、キャッシュされたアカウントを取得するようにviewDidLoad
を更新します:accountResult = nativeAuth.getNativeAuthUserAccount()
アカウントの結果を格納するように
signInCompleted
ハンドラーを更新します。func onSignInCompleted(result: MSALNativeAuthUserAccountResult) { resultTextView.text = "Signed in successfully" accountResult = result }
サインアウト ボタンを追加し、次のコードを使用してユーザーをサインアウトさせます。
@IBAction func signOutPressed(_: Any) { guard let accountResult = accountResult else { print("Not currently signed in") return } accountResult.signOut() self.accountResult = nil resultTextView.text = "Signed out" }
アプリでユーザーをサインアウトさせるために必要なすべての手順を正常に完了しました。 アプリケーションをビルドして実行します。 問題がなければ、サインアウト ボタンを選択して正常にサインアウトできます。
カスタム クレーム プロバイダーの構成
外部システムからのクレームをアプリに発行されるトークンに追加する場合は、カスタム クレーム プロバイダーを使用します。 カスタム クレーム プロバイダーは、外部システムからクレームを取り込むために、外部 REST API を呼び出すカスタム認証拡張機能で構成されています。
カスタム クレーム プロバイダーの構成の手順に従って、外部システムからのクレームをセキュリティ トークンに追加します。