ASP.NET CoreでOAuthを試す(3)
前回は外部認証のページを表示,利用するところまでやりました.
しかし認証の結果を用いて自分のアプリケーション側でどうするかという処理(コールバック)の部分を書いていないので,認証後にエラー画面が表示されていました.
今回は外部認証後のコールバックの部分についてです.
コールバックメソッドの名前は今回はExernalLoginCallback
です.
これは外部認証を利用する際に予め指定していたものです.
[AllowAnonymous] [HttpPost] [Route("[action]")] public IActionResult ExternalLogin(LoginViewModel viewModel, string provider, string returnUrl) { //Google側の認証が終わったらどのURLに遷移させるかを指定している.コールバックの指定 var redirectUrl = Url.Action("ExternalLoginCallbac", "Account", new { ReturnUrl = returnUrl }); var properties = signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl); return new ChallengeResult(provider, properties);//Googleの認証ページが開く }
さて,コールバック全体は以下のとおりです.
/// <summary> /// Googleの認証が終わったらこちら. /// </summary> /// <param name="returnUrl"></param> /// <param name="remoteError"></param> /// <returns></returns> [AllowAnonymous] [Route("[action]")] public async Task<IActionResult> ExternalLoginCallback(string returnUrl = null, string remoteError = null) { //returnUrlがnullだったらルートUrlを返す returnUrl = returnUrl ?? Url.Content("~/"); LoginViewModel loginViewModel = new LoginViewModel { ReturnUrl = returnUrl, ExternalLogins = (await signInManager.GetExternalAuthenticationSchemesAsync()).ToList() }; //外部認証プロバイダからエラーが帰ってきている場合は, //ログインページに戻す if (remoteError != null) { ModelState.AddModelError(string.Empty, $"Error from external provider:{remoteError}"); return View("Login", loginViewModel); } //signInManagerがOAuthの認証結果を持っているので,それを取得する var info = await signInManager.GetExternalLoginInfoAsync(); //空だったらログインページ行きだね if (info == null) { ModelState.AddModelError(string.Empty, "Error loading external login information."); return View("Login", loginViewModel); } //これはこのアプリケーション側のAspNetUserLoginsテーブルにログインユーザーとして登録するためか? var signInResult = await signInManager .ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false, bypassTwoFactor: true); //わかった. // AspNetUserLoginsテーブルは外部認証によるログイン情報が格納されるんだ. //初回は記録がないからfalse if (signInResult.Succeeded) { //以前にその外部認証でログインしたことがある. //問題なし return LocalRedirect(returnUrl); } else { //signInResultがfalseってどういうとき? //初めて外部認証でログインしたユーザーの場合だ.こっちは. //メールアドレスを取得する. var email = info.Principal.FindFirstValue(ClaimTypes.Email); // このアプリにアカウントを持っているかどうかをEmailで検索する. //内部のユーザー情報を検索する if (email != null) { var user = await userManager.FindByEmailAsync(email); //user==nullとは,そんなユーザーはいない...の場合 //つまり,このアプリを初めて使うユーザーで, //外部認証を使った人. if (user == null) { user = new IdentityUser { UserName = info.Principal.FindFirstValue(ClaimTypes.Email), Email = info.Principal.FindFirstValue(ClaimTypes.Email) }; //このアプリ用のユーザー作成 await userManager.CreateAsync(user); } //AspNetUserLoginsテーブルに追加. await userManager.AddLoginAsync(user, info); //このアプリにおけるサインイン状態にする await signInManager.SignInAsync(user, isPersistent: false); return LocalRedirect(returnUrl); } } return View("Login", loginViewModel); }
流れとしては以下のとおりです.
- Google側の認証でエラーだったり,結果が空だったりしたらログイン画面へ戻す.
- 外部認証ユーザーとして登録されているかをチェックする.(AspNetUserLoginsテーブル,ExternalLoginSignInAsyncメソッドを使って).登録されていたらログイン扱いされる.アプリ利用可.終了.
- 登録されていない場合は,アプリのユーザーとして登録されているかチェック
- 登録されていない場合は新規作成
- AspNetUserLoginsに登録
- アプリでサインイン状態にする
と,このようになります.
ここで重要なことは外部の認証を利用していますが,これによって外部サービスにログインしているわけではありません.
そして,外部サービスにアプリ側のユーザー情報が渡されるわけでもありません.
外部認証は外部サービスが認証(IDとパスワードの一致)をチェックしてその結果を返してくれるだけです.
ライブラリも,外部認証を呼び出す手続きを簡略かし,認証結果を受け取るインターフェースを提供しているだけで,それ以上のことはしていません.
なので,その結果を受け取って,アプリ側でログイン処理を記述する必要があります.