From ebed4d2c50f64e48a5e9a3cba1dbf3ee544ba7f1 Mon Sep 17 00:00:00 2001 From: Paul Schneider Date: Sun, 29 May 2016 03:11:38 +0200 Subject: [PATCH] =?UTF-8?q?r=C3=A9active=20oidc,=20d=C3=A9finitivement.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Yavsc/Views/Shared/SignIn.cshtml | 46 +++++- Yavsc/Views/Shared/_Layout.cshtml | 6 +- Yavsc/Views/Shared/_LoginPartial.cshtml | 9 +- Yavsc/src/Controllers/AccountController.cs | 140 ++++++++++++------ Yavsc/src/Providers/OAuthProvider.cs | 128 ++++++++++++++++ Yavsc/src/Startup.cs | 120 ++++++++++----- .../src/ViewModels/Account/LoginViewModel.cs | 5 + 7 files changed, 356 insertions(+), 98 deletions(-) create mode 100644 Yavsc/src/Providers/OAuthProvider.cs diff --git a/Yavsc/Views/Shared/SignIn.cshtml b/Yavsc/Views/Shared/SignIn.cshtml index 31b3538e..c66fac42 100644 --- a/Yavsc/Views/Shared/SignIn.cshtml +++ b/Yavsc/Views/Shared/SignIn.cshtml @@ -1,17 +1,53 @@  @using Microsoft.AspNet.Http.Authentication -@model IEnumerable +@using Yavsc.ViewModels.Account +@model LoginViewModel

Authentication

-

Sign in using one of these external providers:

+
+

Use a local account to log in

+
- @foreach (var description in Model) { +
+
+ +
+ + +
+
+
+ +
+ + +
+
+
+
+ +
+
+

+ Register as a new user? +

+

+ Forgot your password? +

+ + @Html.AntiForgeryToken() + +
+ +
+

Sign in using one of these external providers:

+ @foreach (var description in Model.ExternalProviders) {
- - + + @Html.AntiForgeryToken()
}
\ No newline at end of file diff --git a/Yavsc/Views/Shared/_Layout.cshtml b/Yavsc/Views/Shared/_Layout.cshtml index 4fab0ecd..97d70fa5 100755 --- a/Yavsc/Views/Shared/_Layout.cshtml +++ b/Yavsc/Views/Shared/_Layout.cshtml @@ -44,9 +44,9 @@ diff --git a/Yavsc/Views/Shared/_LoginPartial.cshtml b/Yavsc/Views/Shared/_LoginPartial.cshtml index 206664ae..b4e1a57b 100755 --- a/Yavsc/Views/Shared/_LoginPartial.cshtml +++ b/Yavsc/Views/Shared/_LoginPartial.cshtml @@ -6,10 +6,10 @@ @@ -18,8 +18,9 @@ else { } + \ No newline at end of file diff --git a/Yavsc/src/Controllers/AccountController.cs b/Yavsc/src/Controllers/AccountController.cs index 787f0e5f..270eea70 100644 --- a/Yavsc/src/Controllers/AccountController.cs +++ b/Yavsc/src/Controllers/AccountController.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Security.Claims; using System.Threading.Tasks; using Microsoft.AspNet.Authorization; +using Microsoft.AspNet.Http.Authentication; using Microsoft.AspNet.Identity; using Microsoft.AspNet.Mvc; using Microsoft.AspNet.Mvc.Rendering; @@ -30,7 +31,7 @@ namespace Yavsc.Controllers SmtpSettings _smtpSettings; TwilioSettings _twilioSettings; - // TwilioSettings _twilioSettings; + // TwilioSettings _twilioSettings; public AccountController( UserManager userManager, @@ -42,22 +43,38 @@ namespace Yavsc.Controllers { _userManager = userManager; _signInManager = signInManager; - // _userManager.RegisterTokenProvider("SMS",new UserTokenProvider()); - // _userManager.RegisterTokenProvider("Phone", new UserTokenProvider()); + // _userManager.RegisterTokenProvider("SMS",new UserTokenProvider()); + // _userManager.RegisterTokenProvider("Phone", new UserTokenProvider()); _emailSender = emailSender; _siteSettings = siteSettings.Value; _smtpSettings = smtpSettings.Value; _twilioSettings = twilioSettings.Value; _logger = loggerFactory.CreateLogger(); } + [HttpGet("~/signin")] + public ActionResult SignIn(string returnUrl = "/") + { + return View("SignIn", new LoginViewModel + { + ReturnUrl = returnUrl, + ExternalProviders = _signInManager.GetExternalAuthenticationSchemes() + }); + /* When using an external login provider : + // Request a redirect to the external login provider. + var redirectUrl = returnUrl ?? "/"; + var properties = _signInManager.ConfigureExternalAuthenticationProperties(OpenIdConnectDefaults.AuthenticationScheme, redirectUrl); + return new ChallengeResult(OpenIdConnectDefaults.AuthenticationScheme, properties); + */ + } - // - // GET: /Account/Login - [HttpGet] - public IActionResult Login(string returnUrl = null) + [HttpGet("~/signout"), HttpPost("~/signout")] + public async Task SignOut(string returnUrl = "/") { - ViewData["ReturnUrl"] = returnUrl; - return View(); + // Instruct the cookies middleware to delete the local cookie created when the user agent + // is redirected from the identity provider after a successful authorization flow and + // to redirect the user agent to the identity provider to sign out. + await _signInManager.SignOutAsync(); + return Redirect(returnUrl); } public IActionResult Forbidden() @@ -65,15 +82,45 @@ namespace Yavsc.Controllers return View(); } - // POST: /Account/Login - [HttpPost] - [ValidateAntiForgeryToken] - public async Task Login(LoginViewModel model, string returnUrl = null) + [HttpPost("~/signin")] + public async Task SignIn(string provider, string returnUrl) { - ViewData["ReturnUrl"] = returnUrl; - if (ModelState.IsValid) + + // Note: the "provider" parameter corresponds to the external + // authentication provider choosen by the user agent. + if (string.IsNullOrEmpty(provider)) { + _logger.LogWarning("null provider"); + ModelState.AddModelError("provider", "provider cannot be null"); + return new BadRequestObjectResult(ModelState); + } + + // Note: the "returnUrl" parameter corresponds to the endpoint the user agent + // will be redirected to after a successful authentication and not + // the redirect_uri of the requesting client application. + if (string.IsNullOrEmpty(returnUrl)) + { + _logger.LogWarning($"null returnUrl ({provider}) "); + ModelState.AddModelError("returnUrl", "returnUrl cannot be null"); + return new BadRequestObjectResult(ModelState); + } + + // Instruct the middleware corresponding to the requested external identity + // provider to redirect the user agent to its own authorization endpoint. + // Note: the authenticationScheme parameter must match the value configured in Startup.cs + // Request a redirect to the external login provider. + + var redirectUrl = Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl }); + var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl); + return new ChallengeResult(provider, properties); + } + + [HttpPost("~/login")] + public async Task LocalLogin(LoginViewModel model) + { + if (ModelState.IsValid) + { // This doesn't count login failures towards account lockout // To enable password failures to trigger account lockout, set lockoutOnFailure: true var result = await _signInManager.PasswordSignInAsync(model.UserName, model.Password, model.RememberMe, lockoutOnFailure: false); @@ -81,11 +128,11 @@ namespace Yavsc.Controllers { _logger.LogInformation(1, "User logged in."); - return RedirectToLocal(returnUrl); + return RedirectToLocal(model.ReturnUrl); } if (result.RequiresTwoFactor) { - return RedirectToAction(nameof(SendCode), new { ReturnUrl = returnUrl, RememberMe = model.RememberMe }); + return RedirectToAction(nameof(SendCode), new { ReturnUrl = model.ReturnUrl, RememberMe = model.RememberMe }); } if (result.IsLockedOut) { @@ -98,11 +145,9 @@ namespace Yavsc.Controllers return View(model); } } - // If we got this far, something failed, redisplay form return View(model); } - // // GET: /Account/Register [HttpGet] @@ -127,7 +172,7 @@ namespace Yavsc.Controllers // Send an email with this link var code = await _userManager.GenerateEmailConfirmationTokenAsync(user); var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: HttpContext.Request.Scheme); - await _emailSender.SendEmailAsync(_siteSettings, _smtpSettings, model.Email, "Confirm your account", + await _emailSender.SendEmailAsync(_siteSettings, _smtpSettings, model.Email, "Confirm your account", "Please confirm your account by clicking this link: link"); // await _signInManager.SignInAsync(user, isPersistent: false); _logger.LogInformation(3, "User created a new account with password."); @@ -171,7 +216,7 @@ namespace Yavsc.Controllers var info = await _signInManager.GetExternalLoginInfoAsync(); if (info == null) { - return RedirectToAction(nameof(Login)); + return RedirectToAction(nameof(SignIn)); } // Sign in the user with this external login provider if the user already has a login. @@ -198,20 +243,23 @@ namespace Yavsc.Controllers var email = info.ExternalPrincipal.FindFirstValue(ClaimTypes.Email); var name = info.ExternalPrincipal.FindFirstValue(ClaimTypes.Name); var avatar = info.ExternalPrincipal.FindFirstValue("urn:google:profile"); - /* var phone = info.ExternalPrincipal.FindFirstValue(ClaimTypes.HomePhone); - var mobile = info.ExternalPrincipal.FindFirstValue(ClaimTypes.MobilePhone); - var postalcode = info.ExternalPrincipal.FindFirstValue(ClaimTypes.PostalCode); - var locality = info.ExternalPrincipal.FindFirstValue(ClaimTypes.Locality); - var country = info.ExternalPrincipal.FindFirstValue(ClaimTypes.Country);*/ + /* var phone = info.ExternalPrincipal.FindFirstValue(ClaimTypes.HomePhone); + var mobile = info.ExternalPrincipal.FindFirstValue(ClaimTypes.MobilePhone); + var postalcode = info.ExternalPrincipal.FindFirstValue(ClaimTypes.PostalCode); + var locality = info.ExternalPrincipal.FindFirstValue(ClaimTypes.Locality); + var country = info.ExternalPrincipal.FindFirstValue(ClaimTypes.Country);*/ foreach (var claim in info.ExternalPrincipal.Claims) - _logger.LogWarning("# {0} Claim: {1} {2}",info.LoginProvider,claim.Type,claim.Value); + _logger.LogWarning("# {0} Claim: {1} {2}", info.LoginProvider, claim.Type, claim.Value); var access_token = info.ExternalPrincipal.FindFirstValue("access_token"); var token_type = info.ExternalPrincipal.FindFirstValue("token_type"); var expires_in = info.ExternalPrincipal.FindFirstValue("expires_in"); - return View("ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel { Email = email, - Name = name }); + return View("ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel + { + Email = email, + Name = name + }); } } @@ -358,13 +406,13 @@ namespace Yavsc.Controllers // // GET: /Account/SendCode - [HttpGet,AllowAnonymous] + [HttpGet, AllowAnonymous] public async Task SendCode(string returnUrl = null, bool rememberMe = false) { var user = await _signInManager.GetTwoFactorAuthenticationUserAsync(); if (user == null) { - return View("Error",new Exception("No Two factor authentication user" )); + return View("Error", new Exception("No Two factor authentication user")); } var userFactors = await _userManager.GetValidTwoFactorProvidersAsync(user); @@ -376,7 +424,7 @@ namespace Yavsc.Controllers // // POST: /Account/SendCode [HttpPost] - [ValidateAntiForgeryToken,AllowAnonymous] + [ValidateAntiForgeryToken, AllowAnonymous] public async Task SendCode(SendCodeViewModel model) { if (!ModelState.IsValid) @@ -386,26 +434,26 @@ namespace Yavsc.Controllers var user = await _signInManager.GetTwoFactorAuthenticationUserAsync(); if (user == null) { - return View("Error",new Exception("user is null")); + return View("Error", new Exception("user is null")); } // Generate the token and send it var code = await _userManager.GenerateTwoFactorTokenAsync(user, model.SelectedProvider); if (string.IsNullOrWhiteSpace(code)) { - return View("Error",new Exception("Code is empty")); + return View("Error", new Exception("Code is empty")); } var message = "Your security code is: " + code; if (model.SelectedProvider == Constants.MobileAppFactor) { - return View("Error",new Exception("No SMS service was activated")); + return View("Error", new Exception("No SMS service was activated")); } else // if (model.SelectedProvider == Constants.EMailFactor || model.SelectedProvider == "Default" ) if (model.SelectedProvider == Constants.SMSFactor) { - return View("Error",new Exception("No SMS service was activated")); - // await _smsSender.SendSmsAsync(_twilioSettings, await _userManager.GetPhoneNumberAsync(user), message); + return View("Error", new Exception("No SMS service was activated")); + // await _smsSender.SendSmsAsync(_twilioSettings, await _userManager.GetPhoneNumberAsync(user), message); } else // if (model.SelectedProvider == Constants.EMailFactor || model.SelectedProvider == "Default" ) { @@ -423,7 +471,7 @@ namespace Yavsc.Controllers var user = await _signInManager.GetTwoFactorAuthenticationUserAsync(); if (user == null) { - return View("Error",new Exception("user is null")); + return View("Error", new Exception("user is null")); } return View(new VerifyCodeViewModel { Provider = provider, ReturnUrl = returnUrl, RememberMe = rememberMe }); } @@ -442,7 +490,7 @@ namespace Yavsc.Controllers // The following code protects for brute force attacks against the two factor codes. // If a user enters incorrect codes for a specified amount of time then the user account // will be locked out for a specified amount of time. - _logger.LogWarning("Signin with code: {0} {1}",model.Provider, model.Code); + _logger.LogWarning("Signin with code: {0} {1}", model.Provider, model.Code); var result = await _signInManager.TwoFactorSignInAsync(model.Provider, model.Code, model.RememberMe, model.RememberBrowser); if (result.Succeeded) { @@ -461,13 +509,13 @@ namespace Yavsc.Controllers } } - [HttpGet,Authorize] + [HttpGet, Authorize] public IActionResult Delete() { return View(); } - [HttpPost,Authorize] + [HttpPost, Authorize] public async Task Delete(UnregisterViewModel model) { if (!ModelState.IsValid) @@ -477,13 +525,13 @@ namespace Yavsc.Controllers var user = await _userManager.FindByIdAsync(User.GetUserId()); var result = await _userManager.DeleteAsync(user); if (!result.Succeeded) - { - AddErrors(result); - return new BadRequestObjectResult(ModelState); - } + { + AddErrors(result); + return new BadRequestObjectResult(ModelState); + } await _signInManager.SignOutAsync(); - return RedirectToAction("Index","Home"); + return RedirectToAction("Index", "Home"); } #region Helpers diff --git a/Yavsc/src/Providers/OAuthProvider.cs b/Yavsc/src/Providers/OAuthProvider.cs new file mode 100644 index 00000000..a60b32c8 --- /dev/null +++ b/Yavsc/src/Providers/OAuthProvider.cs @@ -0,0 +1,128 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using AspNet.Security.OpenIdConnect.Extensions; +using AspNet.Security.OpenIdConnect.Server; +using Microsoft.Data.Entity; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Yavsc.Models; + +namespace Yavsc.Providers { + public sealed class AuthorizationProvider : OpenIdConnectServerProvider { + + private ILogger _logger; + + public AuthorizationProvider(ILoggerFactory loggerFactory) { + _logger = loggerFactory.CreateLogger(); + } + public override Task MatchEndpoint(MatchEndpointContext context) { + // Note: by default, OpenIdConnectServerHandler only handles authorization requests made to the authorization endpoint. + // This context handler uses a more relaxed policy that allows extracting authorization requests received at + // /connect/authorize/accept and /connect/authorize/deny (see AuthorizationController.cs for more information). + if (context.Options.AuthorizationEndpointPath.HasValue && + context.Request.Path.StartsWithSegments(context.Options.AuthorizationEndpointPath)) { + context.MatchesAuthorizationEndpoint(); + } + + return Task.FromResult(null); + } + + public override async Task ValidateAuthorizationRequest(ValidateAuthorizationRequestContext context) { + // Note: the OpenID Connect server middleware supports the authorization code, implicit and hybrid flows + // but this authorization provider only accepts response_type=code authorization/authentication requests. + // You may consider relaxing it to support the implicit or hybrid flows. In this case, consider adding + // checks rejecting implicit/hybrid authorization requests when the client is a confidential application. + if (!context.Request.IsAuthorizationCodeFlow()) { + context.Rejected( + error: OpenIdConnectConstants.Errors.UnsupportedResponseType, + description: "Only the authorization code flow is supported by this authorization server"); + + return; + } + + var database = context.HttpContext.RequestServices.GetRequiredService(); + _logger.LogInformation($"Searching fo app id {context.ClientId}"); + + // Retrieve the application details corresponding to the requested client_id. + var application = await (from entity in database.Applications + where entity.ApplicationID == context.ClientId + select entity).SingleOrDefaultAsync(context.HttpContext.RequestAborted); + + if (application == null) { + context.Rejected( + error: OpenIdConnectConstants.Errors.InvalidClient, + description: "Application not found in the database: ensure that your client_id is correct"); + + return; + } + + if (!string.IsNullOrEmpty(context.Request.RedirectUri) && + !string.Equals(context.Request.RedirectUri, application.RedirectUri, StringComparison.Ordinal)) { + context.Rejected( + error: OpenIdConnectConstants.Errors.InvalidClient, + description: "Invalid redirect_uri"); + + return; + } + + context.Validated(); + } + + public override async Task ValidateTokenRequest(ValidateTokenRequestContext context) { + // Note: the OpenID Connect server middleware supports authorization code, refresh token, client credentials + // and resource owner password credentials grant types but this authorization provider uses a safer policy + // rejecting the last two ones. You may consider relaxing it to support the ROPC or client credentials grant types. + if (!context.Request.IsAuthorizationCodeGrantType() && !context.Request.IsRefreshTokenGrantType()) { + context.Rejected( + error: OpenIdConnectConstants.Errors.UnsupportedGrantType, + description: "Only authorization code and refresh token grant types " + + "are accepted by this authorization server"); + + return; + } + + // Note: client authentication is not mandatory for non-confidential client applications like mobile apps + // (except when using the client credentials grant type) but this authorization server uses a safer policy + // that makes client authentication mandatory and returns an error if client_id or client_secret is missing. + // You may consider relaxing it to support the resource owner password credentials grant type + // with JavaScript or desktop applications, where client credentials cannot be safely stored. + // In this case, call context.Skip() to inform the server middleware the client is not trusted. + if (string.IsNullOrEmpty(context.Request.ClientId) || string.IsNullOrEmpty(context.Request.ClientSecret)) { + context.Rejected( + error: OpenIdConnectConstants.Errors.InvalidRequest, + description: "Missing credentials: ensure that your credentials were correctly " + + "flowed in the request body or in the authorization header"); + + return; + } + + var database = context.HttpContext.RequestServices.GetRequiredService(); + + // Retrieve the application details corresponding to the requested client_id. + var application = await (from entity in database.Applications + where entity.ApplicationID == context.ClientId + select entity).SingleOrDefaultAsync(context.HttpContext.RequestAborted); + + if (application == null) { + context.Rejected( + error: OpenIdConnectConstants.Errors.InvalidClient, + description: "Application not found in the database: ensure that your client_id is correct"); + + return; + } + + if (!string.Equals(context.Request.ClientSecret, application.Secret, StringComparison.Ordinal)) { + context.Rejected( + error: OpenIdConnectConstants.Errors.InvalidClient, + description: "Invalid credentials: ensure that you specified a correct client_secret"); + + return; + } + + context.Validated(); + } + + + } +} \ No newline at end of file diff --git a/Yavsc/src/Startup.cs b/Yavsc/src/Startup.cs index 8b6b986f..791699b3 100755 --- a/Yavsc/src/Startup.cs +++ b/Yavsc/src/Startup.cs @@ -35,8 +35,10 @@ using Microsoft.Extensions.PlatformAbstractions; using Microsoft.Extensions.WebEncoders; using Microsoft.Net.Http.Headers; using Yavsc.Auth; +using Yavsc.Extensions; using Yavsc.Formatters; using Yavsc.Models; +using Yavsc.Providers; using Yavsc.Services; @@ -66,8 +68,6 @@ namespace Yavsc "~/bower_components/dropzone/dist/min/basic.min.css", "~/bower_components/dropzone/dist/min/dropzone.min.css" )); - - } } @@ -155,7 +155,8 @@ namespace Yavsc RSAKeyUtils.GetKeyParameters(keyParamsFileInfo.Name) : RSAKeyUtils.GenerateKeyAndSave(keyParamsFileInfo.Name); key = new RsaSecurityKey(keyParams); - services.Configure(options => { + services.Configure(options => + { options.SignInScheme = "ServerCookie"; }); services.Configure( @@ -187,8 +188,10 @@ namespace Yavsc configure.PersistKeysToFileSystem( new DirectoryInfo(Configuration["DataProtection:Keys:Dir"])); }); - services.AddAuthentication(); - + + services.AddAuthentication(options => { + options.SignInScheme = "ServerCookie"; } + ); // Add framework services. services.AddEntityFramework() .AddNpgsql() @@ -227,12 +230,13 @@ namespace Yavsc // Add the system clock service services.AddSingleton(); - + services.AddAuthorization(options => { options.AddPolicy("AdministratorOnly", policy => policy.RequireRole(Constants.AdminGroupName)); options.AddPolicy("FrontOffice", policy => policy.RequireRole(Constants.FrontOfficeGroupName)); - options.AddPolicy("API", policy => { + options.AddPolicy("API", policy => + { policy.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme); policy.RequireClaim(OpenIdConnectConstants.Claims.Scope, "api-resource-controller"); }); @@ -319,7 +323,7 @@ namespace Yavsc { // For more details on creating database during deployment see http://go.microsoft.com/fwlink/?LinkID=615859 - app.UseExceptionHandler("/Home/Error"); + app.UseExceptionHandler("/Home/Error"); try { using (var serviceScope = app.ApplicationServices.GetRequiredService() @@ -334,18 +338,12 @@ namespace Yavsc if (ex.InnerException is InvalidOperationException) // nothing to do ? { -// TODO Send an email to the Admin - } + // TODO Send an email to the Admin + } else throw ex; } } - // Create a new branch where the registered middleware will be executed only for API calls. - /* MapWhenExtensions.MapWhen(app,(Func)(context => - context.Request.Path.StartsWithSegments((PathString)new PathString((string)"/api"))), -(Action)( branch => { })); - -*/ var googleOptions = new GoogleOptions { @@ -376,19 +374,6 @@ namespace Yavsc googleOptions.Scope.Add("https://www.googleapis.com/auth/calendar"); - var udirinfo = new DirectoryInfo(Configuration["Site:UserFiles:RootDir"]); - if (!udirinfo.Exists) - throw new Exception($"Configuration value for Site:UserFiles:RootDir : {udirinfo.FullName}"); - - app.UseFileServer(new FileServerOptions() - { - FileProvider = new PhysicalFileProvider( - udirinfo.FullName), - RequestPath = new PathString(Constants.UserFilesRequestPath), - EnableDirectoryBrowsing = true - }); - - app.UseIISPlatformHandler(options => options.AuthenticationDescriptions.Clear()); app.UseStaticFiles().UseWebSockets(); @@ -397,28 +382,83 @@ namespace Yavsc app.UseIdentity(); - // Create a new branch where the registered middleware will be executed only for non API calls. - app.UseCookieAuthentication(options => { + app.UseWhen(context => context.Request.Path.StartsWithSegments(new PathString("/api")), branch => + { + branch.UseJwtBearerAuthentication(options => + { + options.AutomaticAuthenticate = true; + options.AutomaticChallenge = true; + options.RequireHttpsMetadata = false; + options.Audience = siteSettings.Value.Audience; + options.Authority = siteSettings.Value.Authority; + }); + }); + + // Create a new branch where the registered middleware will be executed only for API calls. + app.UseWhen(context => !context.Request.Path.StartsWithSegments(new PathString("/api")), branch => + { + // Create a new branch where the registered middleware will be executed only for non API calls. + branch.UseCookieAuthentication(options => + { options.AutomaticAuthenticate = true; options.AutomaticChallenge = true; options.AuthenticationScheme = "ServerCookie"; options.CookieName = CookieAuthenticationDefaults.CookiePrefix + "ServerCookie"; options.ExpireTimeSpan = TimeSpan.FromMinutes(5); - options.LoginPath = new PathString("/Account/Login"); + options.LoginPath = new PathString("/signin"); + options.LogoutPath = new PathString("/signout"); }); - app.UseMiddleware(googleOptions); + branch.UseMiddleware(googleOptions); // Facebook - app.UseFacebookAuthentication(options => - { - options.AppId = Configuration["Authentication:Facebook:AppId"]; - options.AppSecret = Configuration["Authentication:Facebook:AppSecret"]; - options.Scope.Add("email"); - options.UserInformationEndpoint = "https://graph.facebook.com/v2.5/me?fields=id,name,email,first_name,last_name"; - }); + branch.UseFacebookAuthentication(options => + { + options.AppId = Configuration["Authentication:Facebook:AppId"]; + options.AppSecret = Configuration["Authentication:Facebook:AppSecret"]; + options.Scope.Add("email"); + options.UserInformationEndpoint = "https://graph.facebook.com/v2.5/me?fields=id,name,email,first_name,last_name"; + }); + + }); + app.UseOpenIdConnectServer(options => + { + options.Provider = new AuthorizationProvider(loggerFactory); + + // Register the certificate used to sign the JWT tokens. + /* options.SigningCredentials.AddCertificate( + assembly: typeof(Startup).GetTypeInfo().Assembly, + resource: "Mvc.Server.Certificate.pfx", + password: "Owin.Security.OpenIdConnect.Server"); */ + + // options.SigningCredentials.AddKey(key); + // Note: see AuthorizationController.cs for more + // information concerning ApplicationCanDisplayErrors. + options.ApplicationCanDisplayErrors = true; + options.AllowInsecureHttp = true; + options.AutomaticChallenge = true; + options.AuthorizationEndpointPath = new PathString("/connect/authorize"); + options.TokenEndpointPath = new PathString("/connect/authorize/accept"); + options.UseSlidingExpiration = true; + options.AllowInsecureHttp = true; + options.AuthenticationScheme = "oidc"; // was = OpenIdConnectDefaults.AuthenticationScheme; + options.LogoutEndpointPath = new PathString("/connect/logout"); + /* options.ValidationEndpointPath = new PathString("/connect/introspect"); */ + }); + + var udirinfo = new DirectoryInfo(Configuration["Site:UserFiles:RootDir"]); + if (!udirinfo.Exists) + throw new Exception($"Configuration value for Site:UserFiles:RootDir : {udirinfo.FullName}"); + + app.UseFileServer(new FileServerOptions() + { + FileProvider = new PhysicalFileProvider( + udirinfo.FullName), + RequestPath = new PathString(Constants.UserFilesRequestPath), + EnableDirectoryBrowsing = true + }); /* Generic OAuth (here GitHub): options.Notifications = new OAuthAuthenticationNotifications diff --git a/Yavsc/src/ViewModels/Account/LoginViewModel.cs b/Yavsc/src/ViewModels/Account/LoginViewModel.cs index 883e09cb..bc8f90b6 100755 --- a/Yavsc/src/ViewModels/Account/LoginViewModel.cs +++ b/Yavsc/src/ViewModels/Account/LoginViewModel.cs @@ -1,5 +1,7 @@ +using System.Collections.Generic; using System.ComponentModel.DataAnnotations; +using Microsoft.AspNet.Http.Authentication; namespace Yavsc.ViewModels.Account { @@ -14,5 +16,8 @@ namespace Yavsc.ViewModels.Account [Display(Name = "Remember me?")] public bool RememberMe { get; set; } + public string ReturnUrl { get; set; } + + public IEnumerable ExternalProviders { get; set; } } }