réactive oidc, définitivement.
This commit is contained in:
@ -1,17 +1,53 @@
|
||||
|
||||
@using Microsoft.AspNet.Http.Authentication
|
||||
@model IEnumerable<AuthenticationDescription>
|
||||
@using Yavsc.ViewModels.Account
|
||||
@model LoginViewModel
|
||||
|
||||
<div class="jumbotron">
|
||||
<h1>Authentication</h1>
|
||||
<p class="lead text-left">Sign in using one of these external providers:</p>
|
||||
<hr/>
|
||||
<h2 class="lead text-left">Use a local account to log in</h2>
|
||||
<form action="/login" method="post" class="form-horizontal" role="form">
|
||||
|
||||
@foreach (var description in Model) {
|
||||
<div asp-validation-summary="ValidationSummary.All" class="text-danger"></div>
|
||||
<div class="form-group">
|
||||
<label for="UserName" class="col-md-2 control-label">User name</label>
|
||||
<div class="col-md-10">
|
||||
<input asp-for="UserName" class="form-control" />
|
||||
<span asp-validation-for="UserName" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="Password" class="col-md-2 control-label">Password</label>
|
||||
<div class="col-md-10">
|
||||
<input asp-for="Password" class="form-control" />
|
||||
<span asp-validation-for="Password" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-md-offset-2 col-md-10">
|
||||
<button type="submit" class="btn btn-lg btn-success">Login</button>
|
||||
</div>
|
||||
</div>
|
||||
<p>
|
||||
<a asp-action="Register">Register as a new user?</a>
|
||||
</p>
|
||||
<p>
|
||||
<a asp-action="ForgotPassword">Forgot your password?</a>
|
||||
</p>
|
||||
<input type="hidden" name="ReturnUrl" value="@Model.ReturnUrl" />
|
||||
@Html.AntiForgeryToken()
|
||||
|
||||
</form>
|
||||
|
||||
<hr/>
|
||||
<h2 class="lead text-left">Sign in using one of these external providers:</h2>
|
||||
@foreach (var description in Model.ExternalProviders) {
|
||||
<form action="/signin" method="post">
|
||||
<input type="hidden" name="Provider" value="@description.AuthenticationScheme" />
|
||||
<input type="hidden" name="ReturnUrl" value="@ViewBag.ReturnUrl" />
|
||||
|
||||
<input type="hidden" name="ReturnUrl" value="@Model.ReturnUrl" />
|
||||
<button class="btn btn-lg btn-success" type="submit">Connect using @description.DisplayName</button>
|
||||
@Html.AntiForgeryToken()
|
||||
</form>
|
||||
}
|
||||
</div>
|
@ -44,9 +44,9 @@
|
||||
</div>
|
||||
<div class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav">
|
||||
<li><a asp-controller="Home" asp-action="Index">@SR["Home"]</a></li>
|
||||
<li><a asp-controller="Home" asp-action="About">@SR["About"] @SiteSettings.Value.Title</a> </li>
|
||||
<li><a asp-controller="Home" asp-action="Contact">@SR["Contact"]</a></li>
|
||||
<li><a asp-controller="Home" asp-action="Index" class="navbar-link">@SR["Home"]</a></li>
|
||||
<li><a asp-controller="Home" asp-action="About" class="navbar-link">@SR["About"] @SiteSettings.Value.Title</a> </li>
|
||||
<li><a asp-controller="Home" asp-action="Contact" class="navbar-link">@SR["Contact"]</a></li>
|
||||
</ul>
|
||||
@await Html.PartialAsync("_LoginPartial")
|
||||
</div>
|
||||
|
@ -6,10 +6,10 @@
|
||||
<language-layout>
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li>
|
||||
<a asp-controller="Manage" asp-action="Index" title="Manage">@SR["Hello"] @User.GetUserName()!</a>
|
||||
<a asp-controller="Manage" class="navbar-link" asp-action="Index" title="Manage">@SR["Hello"] @User.GetUserName()!</a>
|
||||
</li>
|
||||
<li>
|
||||
<button type="submit" class="btn btn-link navbar-btn navbar-link">@SR["Logout"]</button>
|
||||
<button type="submit" class="navbar-link">@SR["Logout"]</button>
|
||||
</li>
|
||||
</ul>
|
||||
</language-layout>
|
||||
@ -18,8 +18,9 @@
|
||||
else
|
||||
{ <language-layout>
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li><a asp-controller="Account" asp-action="Register">@SR["Register"]</a></li>
|
||||
<li><a asp-controller="Account" asp-action="Login">@SR["Login"]</a></li>
|
||||
<li><a class="navbar-link" asp-controller="Account" asp-action="Register">@SR["Register"]</a></li>
|
||||
<li><a class="navbar-link" asp-controller="Account" asp-action="SignIn">@SR["Login"]</a></li>
|
||||
</ul>
|
||||
</language-layout>
|
||||
}
|
||||
|
@ -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<ApplicationUser> 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<AccountController>();
|
||||
}
|
||||
|
||||
//
|
||||
// GET: /Account/Login
|
||||
[HttpGet]
|
||||
public IActionResult Login(string returnUrl = null)
|
||||
[HttpGet("~/signin")]
|
||||
public ActionResult SignIn(string returnUrl = "/")
|
||||
{
|
||||
ViewData["ReturnUrl"] = returnUrl;
|
||||
return View();
|
||||
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);
|
||||
*/
|
||||
}
|
||||
|
||||
[HttpGet("~/signout"), HttpPost("~/signout")]
|
||||
public async Task<IActionResult> SignOut(string returnUrl = "/")
|
||||
{
|
||||
// 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<IActionResult> Login(LoginViewModel model, string returnUrl = null)
|
||||
[HttpPost("~/signin")]
|
||||
public async Task<IActionResult> SignIn(string provider, string returnUrl)
|
||||
{
|
||||
|
||||
// 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<IActionResult> LocalLogin(LoginViewModel model)
|
||||
{
|
||||
ViewData["ReturnUrl"] = returnUrl;
|
||||
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: <a href=\"" + callbackUrl + "\">link</a>");
|
||||
// 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<ActionResult> 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<IActionResult> 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<IActionResult> 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
|
||||
|
128
Yavsc/src/Providers/OAuthProvider.cs
Normal file
128
Yavsc/src/Providers/OAuthProvider.cs
Normal file
@ -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<AuthorizationProvider>();
|
||||
}
|
||||
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<object>(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<ApplicationDbContext>();
|
||||
_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<ApplicationDbContext>();
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
@ -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<SharedAuthenticationOptions>(options => {
|
||||
services.Configure<SharedAuthenticationOptions>(options =>
|
||||
{
|
||||
options.SignInScheme = "ServerCookie";
|
||||
});
|
||||
services.Configure<TokenAuthOptions>(
|
||||
@ -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<ISystemClock, SystemClock>();
|
||||
|
||||
|
||||
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<IServiceScopeFactory>()
|
||||
@ -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<Microsoft.AspNet.Http.HttpContext, bool>)(context =>
|
||||
context.Request.Path.StartsWithSegments((PathString)new PathString((string)"/api"))),
|
||||
(Action<IApplicationBuilder>)( branch => { }));
|
||||
|
||||
*/
|
||||
|
||||
var googleOptions = new GoogleOptions
|
||||
{
|
||||
@ -376,6 +374,80 @@ namespace Yavsc
|
||||
|
||||
googleOptions.Scope.Add("https://www.googleapis.com/auth/calendar");
|
||||
|
||||
app.UseIISPlatformHandler(options => options.AuthenticationDescriptions.Clear());
|
||||
|
||||
app.UseStaticFiles().UseWebSockets();
|
||||
|
||||
app.UseRequestLocalization(localizationOptions.Value, (RequestCulture)new RequestCulture((string)"fr"));
|
||||
|
||||
app.UseIdentity();
|
||||
|
||||
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("/signin");
|
||||
options.LogoutPath = new PathString("/signout");
|
||||
});
|
||||
|
||||
|
||||
branch.UseMiddleware<GoogleMiddleware>(googleOptions);
|
||||
|
||||
// Facebook
|
||||
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}");
|
||||
@ -388,38 +460,6 @@ namespace Yavsc
|
||||
EnableDirectoryBrowsing = true
|
||||
});
|
||||
|
||||
|
||||
app.UseIISPlatformHandler(options => options.AuthenticationDescriptions.Clear());
|
||||
|
||||
app.UseStaticFiles().UseWebSockets();
|
||||
|
||||
app.UseRequestLocalization(localizationOptions.Value, (RequestCulture)new RequestCulture((string)"fr"));
|
||||
|
||||
app.UseIdentity();
|
||||
|
||||
// Create a new branch where the registered middleware will be executed only for non API calls.
|
||||
app.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");
|
||||
});
|
||||
|
||||
|
||||
app.UseMiddleware<GoogleMiddleware>(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";
|
||||
});
|
||||
|
||||
|
||||
|
||||
/* Generic OAuth (here GitHub): options.Notifications = new OAuthAuthenticationNotifications
|
||||
{
|
||||
|
@ -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<AuthenticationDescription> ExternalProviders { get; set; }
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user