diff --git a/src/AbpDesk/AbpDesk.Web.Mvc/AbpDeskWebMvcModule.cs b/src/AbpDesk/AbpDesk.Web.Mvc/AbpDeskWebMvcModule.cs index e6f0c74ec2..eea4fead9b 100644 --- a/src/AbpDesk/AbpDesk.Web.Mvc/AbpDeskWebMvcModule.cs +++ b/src/AbpDesk/AbpDesk.Web.Mvc/AbpDeskWebMvcModule.cs @@ -66,6 +66,20 @@ namespace AbpDesk.Web.Mvc //services.Configure(configuration); //Needed when we use Volo.Abp.Identity.HttpApi.Client + //Adding Facebook authentication + if (bool.Parse(configuration["Authentication:Facebook:IsEnabled"])) + { + services.AddAuthentication().AddFacebook(options => + { + options.AppId = configuration["Authentication:Facebook:AppId"]; + options.AppSecret = configuration["Authentication:Facebook:AppSecret"]; + + options.Scope.Add("email"); + options.Scope.Add("public_profile"); + }); + } + + services.AddAssemblyOf(); services.Configure(options => diff --git a/src/AbpDesk/AbpDesk.Web.Mvc/Startup.cs b/src/AbpDesk/AbpDesk.Web.Mvc/Startup.cs index 781db47760..a166afafb9 100644 --- a/src/AbpDesk/AbpDesk.Web.Mvc/Startup.cs +++ b/src/AbpDesk/AbpDesk.Web.Mvc/Startup.cs @@ -1,12 +1,10 @@ using System; -using System.IO; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Serilog; using Volo.Abp; -using Volo.Abp.Modularity.PlugIns; namespace AbpDesk.Web.Mvc { diff --git a/src/AbpDesk/AbpDesk.Web.Mvc/appsettings.json b/src/AbpDesk/AbpDesk.Web.Mvc/appsettings.json index 927ad8e6c7..fe44ab1467 100644 --- a/src/AbpDesk/AbpDesk.Web.Mvc/appsettings.json +++ b/src/AbpDesk/AbpDesk.Web.Mvc/appsettings.json @@ -7,5 +7,12 @@ "AbpIdentity": { "BaseUrl": "http://localhost:63290/" } + }, + "Authentication": { + "Facebook": { + "IsEnabled": "true", + "AppId": "911417875702990", + "AppSecret": "adea0bff222ae340d8fb0ce3e6275d6b" + } } } diff --git a/src/AbpDesk/Web_PlugIns/AbpDesk.MongoBlog.dll b/src/AbpDesk/Web_PlugIns/AbpDesk.MongoBlog.dll index e8b4663419..9dae5160a2 100644 Binary files a/src/AbpDesk/Web_PlugIns/AbpDesk.MongoBlog.dll and b/src/AbpDesk/Web_PlugIns/AbpDesk.MongoBlog.dll differ diff --git a/src/Volo.Abp.Account.Web/Pages/Account/ExternalLogin.cshtml b/src/Volo.Abp.Account.Web/Pages/Account/ExternalLogin.cshtml new file mode 100644 index 0000000000..5817404f38 --- /dev/null +++ b/src/Volo.Abp.Account.Web/Pages/Account/ExternalLogin.cshtml @@ -0,0 +1,29 @@ +@page +@model Volo.Abp.Account.Web.Pages.Account.ExternalLoginModel +@{ + ViewData["Title"] = "Register"; +} + +

@ViewData["Title"]

+

Associate your @Model.LoginProvider account.

+
+ +

+ You've successfully authenticated with @Model.LoginProvider. + Please enter an email address for this site below and click the Register button to finish + logging in. +

+ +
+
+
+
+
+ + + +
+ +
+
+
diff --git a/src/Volo.Abp.Account.Web/Pages/Account/ExternalLogin.cshtml.cs b/src/Volo.Abp.Account.Web/Pages/Account/ExternalLogin.cshtml.cs new file mode 100644 index 0000000000..6ea57b97a7 --- /dev/null +++ b/src/Volo.Abp.Account.Web/Pages/Account/ExternalLogin.cshtml.cs @@ -0,0 +1,138 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.Security.Claims; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Volo.Abp.Identity; +using Volo.Abp.Ui; +using Volo.Abp.Uow; + +namespace Volo.Abp.Account.Web.Pages.Account +{ + public class ExternalLoginModel : AccountModelBase + { + private readonly SignInManager _signInManager; + private readonly UserManager _userManager; + + public ExternalLoginModel( + SignInManager signInManager, + UserManager userManager) + { + _signInManager = signInManager; + _userManager = userManager; + } + + [BindProperty] + public InputModel Input { get; set; } + + public string LoginProvider { get; set; } + + public string ReturnUrl { get; set; } + + [TempData] + public string ErrorMessage { get; set; } + + public class InputModel + { + [Required] + [EmailAddress] + public string Email { get; set; } + } + + public IActionResult OnGetAsync() + { + return RedirectToPage("./Login"); + } + + [UnitOfWork] + public virtual IActionResult OnPost(string provider, string returnUrl = null) + { + // Request a redirect to the external login provider. + var redirectUrl = Url.Page("./ExternalLogin", pageHandler: "Callback", values: new { returnUrl }); + var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl); + return new ChallengeResult(provider, properties); + } + + [UnitOfWork] + public virtual async Task OnGetCallbackAsync(string returnUrl = null, string remoteError = null, string returnUrlHash = "") + { + if (remoteError != null) + { + ErrorMessage = $"Error from external provider: {remoteError}"; + return RedirectToPage("./Login"); + } + + var info = await _signInManager.GetExternalLoginInfoAsync(); + if (info == null) + { + return RedirectToPage("./Login"); + } + + // Sign in the user with this external login provider if the user already has a login. + var result = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false, bypassTwoFactor: true); + if (result.Succeeded) + { + return RedirectSafely(returnUrl, returnUrlHash); + } + + if (result.IsLockedOut) + { + throw new UserFriendlyException("Cannot proceed because user is locked out!"); + } + + ReturnUrl = returnUrl; + LoginProvider = info.LoginProvider; + + //User does not have an account, create an account. + var success = await CreateUserAsync(returnUrl, returnUrlHash); + + if (success) + { + return RedirectSafely(returnUrl, returnUrlHash); + } + + return Page(); + } + + [UnitOfWork] + public virtual async Task CreateUserAsync(string returnUrl = null, string returnUrlHash = null) + { + if (!ModelState.IsValid) + { + return false; + } + + // Get the information about the user from the external login provider + var info = await _signInManager.GetExternalLoginInfoAsync(); + if (info == null) + { + throw new ApplicationException("Error loading external login information during confirmation."); + } + + var user = new IdentityUser(GuidGenerator.Create(), info.Principal.FindFirstValue(ClaimTypes.Email)); + + var result = await _userManager.CreateAsync(user); + + //todo: needs to check identity errors? + //CheckIdentityErrors( await _userManager.CreateAsync(user)); + + if (result.Succeeded) + { + result = await _userManager.AddLoginAsync(user, info); + if (result.Succeeded) + { + await _signInManager.SignInAsync(user, false); + return true; + } + } + + foreach (var error in result.Errors) + { + ModelState.AddModelError(string.Empty, error.Description); + } + + return false; + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml b/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml index 24e84bfdab..62b8c70dfd 100644 --- a/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml +++ b/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml @@ -1,27 +1,61 @@ @page @model Volo.Abp.Account.Web.Pages.Account.LoginModel
-
-
-
- - +
+
+ +
+ + +
+
+ + +
+
+ +
+ + + + -
- - -
-
- -
- - - -
+ +
+
+

Use another service to log in.

+
+ @{ + if ((Model.ExternalLogins?.Count ?? 0) == 0) + { +
+

+ There are no external authentication services configured. See this article + for details on setting up this ASP.NET application to support logging in via external services. +

+
+ } + else + { +
+
+

+ @foreach (var provider in Model.ExternalLogins) + { + + } +

+
+
+ } + } +
+
+
\ No newline at end of file diff --git a/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml.cs b/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml.cs index 7a8fc7b14d..39a3e40bb7 100644 --- a/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml.cs +++ b/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml.cs @@ -1,6 +1,9 @@ using System; +using System.Collections.Generic; using System.ComponentModel.DataAnnotations; +using System.Linq; using System.Threading.Tasks; +using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using Volo.Abp.Identity; @@ -15,6 +18,8 @@ namespace Volo.Abp.Account.Web.Pages.Account public string ReturnUrlHash { get; set; } //TODO: Try to automatically bind from querystring! + public IList ExternalLogins { get; set; } + private readonly SignInManager _signInManager; public LoginModel(SignInManager signInManager) @@ -22,10 +27,11 @@ namespace Volo.Abp.Account.Web.Pages.Account _signInManager = signInManager; } - public void OnGet(string returnUrl = "", string returnUrlHash = "") + public async Task OnGetAsync(string returnUrl = "", string returnUrlHash = "") { ReturnUrl = returnUrl; ReturnUrlHash = returnUrl; + ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList(); } //TODO: Bind input to a property instead of getting as parameter..? diff --git a/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EfCoreIdentityUserRepository.cs b/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EfCoreIdentityUserRepository.cs index 70b301e125..8deac6bef1 100644 --- a/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EfCoreIdentityUserRepository.cs +++ b/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EfCoreIdentityUserRepository.cs @@ -37,7 +37,10 @@ namespace Volo.Abp.Identity public async Task FindByLoginAsync(string loginProvider, string providerKey, CancellationToken cancellationToken) { //TODO: This should be changed since loginProvider, providerKey are not PKs. - var userLogin = await DbContext.UserLogins.FindAsync(new object[] { loginProvider, providerKey }, cancellationToken); + var userLogin = await DbContext.UserLogins + .Where(login => login.LoginProvider == loginProvider && login.ProviderKey == providerKey) + .FirstOrDefaultAsync(cancellationToken); + if (userLogin == null) { return null;