oidc reloaded

main
Paul Schneider 10 years ago
parent adfaa1a587
commit c65985477e
22 changed files with 558 additions and 255 deletions

@ -9,6 +9,7 @@
<div class="jumbotron"> <div class="jumbotron">
<h1>@ViewData["Title"]</h1> <h1>@ViewData["Title"]</h1>
<hr/> <hr/>
<h2 class="lead text-left">@SR["Use a local account to log in"]</h2> <h2 class="lead text-left">@SR["Use a local account to log in"]</h2>
<form action="/login" method="post" class="form-horizontal" role="form"> <form action="/login" method="post" class="form-horizontal" role="form">
@ -46,8 +47,8 @@
<p> <p>
<a asp-action="ForgotPassword" asp-controller="Account">@SR["Forgot your password"]?</a> <a asp-action="ForgotPassword" asp-controller="Account">@SR["Forgot your password"]?</a>
</p> </p>
<input type="hidden" name="Provider" value="LOCAL" />
<input type="hidden" name="ReturnUrl" value="@Model.AfterLoginRedirectUrl" /> <input type="hidden" name="ReturnUrl" value="@Model.ReturnUrl" />
@Html.AntiForgeryToken() @Html.AntiForgeryToken()
</form> </form>
@ -68,7 +69,7 @@
@foreach (var description in Model.ExternalProviders) { @foreach (var description in Model.ExternalProviders) {
<form action="/signin" method="post"> <form action="/signin" method="post">
<input type="hidden" name="Provider" value="@description.AuthenticationScheme" /> <input type="hidden" name="Provider" value="@description.AuthenticationScheme" />
<input type="hidden" name="AfterLoginRedirectUrl" value="@Model.AfterLoginRedirectUrl" /> <input type="hidden" name="ReturnUrl" value="@Url.Action("ExternalLoginCallback","Account", new { returnUrl = Model.ReturnUrl })" />
<button class="btn btn-lg btn-success" type="submit">@SR["Connect using"] @description.DisplayName</button> <button class="btn btn-lg btn-success" type="submit">@SR["Connect using"] @description.DisplayName</button>
@Html.AntiForgeryToken() @Html.AntiForgeryToken()
</form> </form>

@ -13,6 +13,14 @@
</li> </li>
</ul> </ul>
</form> </form>
<form enctype="application/x-www-form-urlencoded" method="post" class="navbar-right">
@Html.AntiForgeryToken()
<label for="username">username:</label><input name="username" placeholder="(Votre Nom d'utilisateur)"/>
<label for="password">password:</label><input name="password" placeholder="(Votre mot de passe)" type="password"/>
<input formaction="/api/token/post" class="btn btn-lg btn-success" name="Getatoken" type="submit" value="Getatoken" />
</form>
} }
else else
{ {

@ -0,0 +1,16 @@
@using AspNet.Security.OpenIdConnect.Extensions
@using Microsoft.IdentityModel.Protocols.OpenIdConnect
@model AuthorisationView
<div class="jumbotron">
<h1>Authorization</h1>
<p class="lead text-left">Get a token</p>
<form enctype="application/x-www-form-urlencoded" method="post">
@Html.AntiForgeryToken()
<input formaction="/api/token/get" class="btn btn-lg btn-success" name="Getatoken" type="submit" value="Getatoken" />
</form>
</div>

@ -72,6 +72,7 @@
"Microsoft.Extensions.DependencyInjection.Abstractions": "1.0.0-rc1-final", "Microsoft.Extensions.DependencyInjection.Abstractions": "1.0.0-rc1-final",
"Microsoft.AspNet.Authentication.Facebook": "1.0.0-rc1-final", "Microsoft.AspNet.Authentication.Facebook": "1.0.0-rc1-final",
"Microsoft.AspNet.Authentication.Twitter": "1.0.0-rc1-final", "Microsoft.AspNet.Authentication.Twitter": "1.0.0-rc1-final",
"Microsoft.AspNet.Authentication.Google": "1.0.0-rc1-final",
"Microsoft.Extensions.Localization": "1.0.0-rc1-final", "Microsoft.Extensions.Localization": "1.0.0-rc1-final",
"Microsoft.Extensions.Localization.Abstractions": "1.0.0-rc1-final", "Microsoft.Extensions.Localization.Abstractions": "1.0.0-rc1-final",
"Microsoft.Extensions.Globalization.CultureInfoCache": "1.0.0-rc1-final", "Microsoft.Extensions.Globalization.CultureInfoCache": "1.0.0-rc1-final",
@ -82,7 +83,6 @@
"Microsoft.Extensions.PlatformAbstractions": "1.0.0-rc1-final", "Microsoft.Extensions.PlatformAbstractions": "1.0.0-rc1-final",
"Microsoft.Extensions.CodeGenerators.Mvc": "1.0.0-rc1-final", "Microsoft.Extensions.CodeGenerators.Mvc": "1.0.0-rc1-final",
"Microsoft.AspNet.Session": "1.0.0-rc1-final", "Microsoft.AspNet.Session": "1.0.0-rc1-final",
"Microsoft.NETCore.Platforms": "1.0.1-beta-23516",
"Microsoft.AspNet.SignalR.JS": "2.2.0", "Microsoft.AspNet.SignalR.JS": "2.2.0",
"Microsoft.AspNet.WebSockets.Server": "1.0.0-rc1-final", "Microsoft.AspNet.WebSockets.Server": "1.0.0-rc1-final",
"Microsoft.AspNet.Http.Abstractions": "1.0.0-rc1-final", "Microsoft.AspNet.Http.Abstractions": "1.0.0-rc1-final",
@ -90,8 +90,6 @@
"Microsoft.AspNet.Owin": "1.0.0-rc1-final", "Microsoft.AspNet.Owin": "1.0.0-rc1-final",
"Microsoft.AspNet.SignalR.Core": "2.2.0", "Microsoft.AspNet.SignalR.Core": "2.2.0",
"Microsoft.AspNet.Server.WebListener": "1.0.0-rc1-final", "Microsoft.AspNet.Server.WebListener": "1.0.0-rc1-final",
"Microsoft.AspNetCore.Authentication.OpenIdConnect": "0.0.1-alpha",
"Microsoft.AspNetCore.Authentication.Cookies": "0.0.1-alpha",
"Microsoft.AspNet.Authentication.OpenIdConnect": "1.0.0-rc1-final", "Microsoft.AspNet.Authentication.OpenIdConnect": "1.0.0-rc1-final",
"MailKit": "1.3.0-beta7", "MailKit": "1.3.0-beta7",
"Microsoft.Framework.Configuration.Abstractions": "1.0.0-beta8", "Microsoft.Framework.Configuration.Abstractions": "1.0.0-beta8",
@ -101,8 +99,6 @@
"Microsoft.AspNet.Web.Optimization": "1.1.3", "Microsoft.AspNet.Web.Optimization": "1.1.3",
"PayPalCoreSDK": "1.7.1", "PayPalCoreSDK": "1.7.1",
"Microsoft.Extensions.WebEncoders.Core": "1.0.0-rc1-final", "Microsoft.Extensions.WebEncoders.Core": "1.0.0-rc1-final",
"Microsoft.AspNetCore.Authentication.OAuth": "0.0.1-alpha",
"Microsoft.Extensions.Options": "0.0.1-alpha",
"Microsoft.Extensions.WebEncoders": "1.0.0-rc1-final", "Microsoft.Extensions.WebEncoders": "1.0.0-rc1-final",
"Google.Apis.Core": "1.11.1", "Google.Apis.Core": "1.11.1",
"Google.Apis": "1.11.1", "Google.Apis": "1.11.1",
@ -113,7 +109,8 @@
"System.IdentityModel.Tokens": "5.0.0-rc1-211161024", "System.IdentityModel.Tokens": "5.0.0-rc1-211161024",
"System.IdentityModel.Tokens.Jwt": "5.0.0-rc1-211161024", "System.IdentityModel.Tokens.Jwt": "5.0.0-rc1-211161024",
"Microsoft.AspNet.Authorization": "1.0.0-rc1-final", "Microsoft.AspNet.Authorization": "1.0.0-rc1-final",
"AspNet.Security.OpenIdConnect.Server": "1.0.0-beta4" "AspNet.Security.OpenIdConnect.Server": "1.0.0-beta4",
"Microsoft.Extensions.Options": "0.0.1-alpha"
}, },
"commands": { "commands": {
"web": "Microsoft.AspNet.Server.Kestrel --server.urls http://*:5000", "web": "Microsoft.AspNet.Server.Kestrel --server.urls http://*:5000",
@ -149,4 +146,4 @@
"prepublish": "gulp min", "prepublish": "gulp min",
"postpublish": "./postPublish.sh" "postpublish": "./postPublish.sh"
} }
} }

File diff suppressed because it is too large Load Diff

@ -28,9 +28,9 @@ namespace Yavsc.Auth
/// Adds the <see cref="GoogleMiddleware"/> middleware to the specified <see cref="IApplicationBuilder"/>, which enables Google authentication capabilities. /// Adds the <see cref="GoogleMiddleware"/> middleware to the specified <see cref="IApplicationBuilder"/>, which enables Google authentication capabilities.
/// </summary> /// </summary>
/// <param name="app">The <see cref="IApplicationBuilder"/> to add the middleware to.</param> /// <param name="app">The <see cref="IApplicationBuilder"/> to add the middleware to.</param>
/// <param name="options">A <see cref="GoogleOptions"/> that specifies options for the middleware.</param> /// <param name="options">A <see cref="YavscGoogleOptions"/> that specifies options for the middleware.</param>
/// <returns>A reference to this instance after the operation has completed.</returns> /// <returns>A reference to this instance after the operation has completed.</returns>
public static IApplicationBuilder UseGoogleAuthentication(this IApplicationBuilder app, GoogleOptions options) public static IApplicationBuilder UseGoogleAuthentication(this IApplicationBuilder app, YavscGoogleOptions options)
{ {
if (app == null) if (app == null)
{ {

@ -13,7 +13,7 @@ using Newtonsoft.Json.Linq;
namespace Yavsc.Auth namespace Yavsc.Auth
{ {
internal class GoogleHandler : OAuthHandler<GoogleOptions> internal class GoogleHandler : OAuthHandler<YavscGoogleOptions>
{ {
private ILogger _logger; private ILogger _logger;
public GoogleHandler(HttpClient httpClient,ILogger logger) public GoogleHandler(HttpClient httpClient,ILogger logger)

@ -14,7 +14,7 @@ namespace Yavsc.Auth
/// <summary> /// <summary>
/// An ASP.NET Core middleware for authenticating users using Google OAuth 2.0. /// An ASP.NET Core middleware for authenticating users using Google OAuth 2.0.
/// </summary> /// </summary>
public class GoogleMiddleware : OAuthMiddleware<GoogleOptions> public class GoogleMiddleware : OAuthMiddleware<YavscGoogleOptions>
{ {
private RequestDelegate _next; private RequestDelegate _next;
private ILogger _logger; private ILogger _logger;
@ -34,7 +34,7 @@ namespace Yavsc.Auth
ILoggerFactory loggerFactory, ILoggerFactory loggerFactory,
UrlEncoder encoder, UrlEncoder encoder,
IOptions<SharedAuthenticationOptions> sharedOptions, IOptions<SharedAuthenticationOptions> sharedOptions,
GoogleOptions options) YavscGoogleOptions options)
: base(next, dataProtectionProvider, loggerFactory, encoder, sharedOptions, options) : base(next, dataProtectionProvider, loggerFactory, encoder, sharedOptions, options)
{ {
if (next == null) if (next == null)
@ -71,7 +71,7 @@ namespace Yavsc.Auth
} }
protected override AuthenticationHandler<GoogleOptions> CreateHandler() protected override AuthenticationHandler<YavscGoogleOptions> CreateHandler()
{ {
return new GoogleHandler(Backchannel,_logger); return new GoogleHandler(Backchannel,_logger);
} }

@ -3,7 +3,7 @@ using Microsoft.AspNet.Http;
namespace Yavsc.Auth namespace Yavsc.Auth
{ {
public static class GoogleDefaults public static class YavscGoogleDefaults
{ {
public const string AuthenticationScheme = "Google"; public const string AuthenticationScheme = "Google";
@ -17,19 +17,19 @@ namespace Yavsc.Auth
/// <summary> /// <summary>
/// Configuration options for <see cref="GoogleMiddleware"/>. /// Configuration options for <see cref="GoogleMiddleware"/>.
/// </summary> /// </summary>
public class GoogleOptions : OAuthOptions public class YavscGoogleOptions : OAuthOptions
{ {
/// <summary> /// <summary>
/// Initializes a new <see cref="GoogleOptions"/>. /// Initializes a new <see cref="YavscGoogleOptions"/>.
/// </summary> /// </summary>
public GoogleOptions() public YavscGoogleOptions()
{ {
AuthenticationScheme = GoogleDefaults.AuthenticationScheme; AuthenticationScheme = YavscGoogleDefaults.AuthenticationScheme;
DisplayName = AuthenticationScheme; DisplayName = AuthenticationScheme;
CallbackPath = new PathString("/signin-google"); CallbackPath = new PathString("/signin-google");
AuthorizationEndpoint = GoogleDefaults.AuthorizationEndpoint; AuthorizationEndpoint = YavscGoogleDefaults.AuthorizationEndpoint;
TokenEndpoint = GoogleDefaults.TokenEndpoint; TokenEndpoint = YavscGoogleDefaults.TokenEndpoint;
UserInformationEndpoint = GoogleDefaults.UserInformationEndpoint; UserInformationEndpoint = YavscGoogleDefaults.UserInformationEndpoint;
Scope.Add("openid"); Scope.Add("openid");
Scope.Add("profile"); Scope.Add("profile");
Scope.Add("email"); Scope.Add("email");

@ -0,0 +1,42 @@
using System;
using System.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
namespace Yavsc.Auth
{
public class MonoJwtSecurityTokenHandler : JwtSecurityTokenHandler
{
MonoDataProtectionProvider protectionProvider;
public MonoJwtSecurityTokenHandler(MonoDataProtectionProvider prpro)
{
protectionProvider = prpro;
}
public override JwtSecurityToken CreateToken(
string issuer,
string audience, ClaimsIdentity subject,
DateTime? notBefore, DateTime? expires, DateTime? issuedAt,
SigningCredentials signingCredentials
)
{
SecurityTokenDescriptor tokenDescriptor = new SecurityTokenDescriptor
{
Audience = audience,
Claims = subject.Claims,
Expires = expires,
IssuedAt = issuedAt,
Issuer = issuer,
NotBefore = notBefore,
SigningCredentials = signingCredentials
};
var token = base.CreateToken(tokenDescriptor);
return token as JwtSecurityToken;
}
}
}

@ -4,8 +4,21 @@ namespace Yavsc
{ {
public class TokenAuthOptions public class TokenAuthOptions
{ {
/// <summary>
/// Public's identification
/// </summary>
/// <returns></returns>
public string Audience { get; set; } public string Audience { get; set; }
/// <summary>
/// Identity authority
/// </summary>
/// <returns></returns>
public string Issuer { get; set; } public string Issuer { get; set; }
/// <summary>
/// Signin key and signature algotythm
/// </summary>
/// <returns></returns>
public SigningCredentials SigningCredentials { get; set; } public SigningCredentials SigningCredentials { get; set; }
public int ExpiresIn { get; set; }
} }
} }

@ -1,4 +1,5 @@
using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNet.DataProtection; using Microsoft.AspNet.DataProtection;
using Microsoft.AspNet.Identity; using Microsoft.AspNet.Identity;
@ -16,6 +17,7 @@ namespace Yavsc.Auth {
public Task<string> GenerateAsync(string purpose, UserManager<ApplicationUser> manager, ApplicationUser user) public Task<string> GenerateAsync(string purpose, UserManager<ApplicationUser> manager, ApplicationUser user)
{ {
if ( user==null ) throw new InvalidOperationException("no user");
var por = new MonoDataProtector(Constants.ApplicationName,new string[] { purpose } ); var por = new MonoDataProtector(Constants.ApplicationName,new string[] { purpose } );
return Task.FromResult(por.Protect(UserStamp(user))); return Task.FromResult(por.Protect(UserStamp(user)));
@ -29,7 +31,7 @@ namespace Yavsc.Auth {
return Task.FromResult ( user.Id == values[0] && user.Email == values[1] && user.UserName == values[2]); return Task.FromResult ( user.Id == values[0] && user.Email == values[1] && user.UserName == values[2]);
} }
public string UserStamp(ApplicationUser user) { public static string UserStamp(ApplicationUser user) {
return $"{user.Id} {user.Email} {user.UserName}"; return $"{user.Id} {user.Email} {user.UserName}";
} }
} }

@ -55,8 +55,7 @@ namespace Yavsc.Controllers
public IActionResult Login(string returnUrl) public IActionResult Login(string returnUrl)
{ {
return View("SignIn", new LoginViewModel { return View("SignIn", new LoginViewModel {
AfterLoginRedirectUrl = returnUrl, ReturnUrl = returnUrl,
ReturnUrl = "/Account/ExternalLoginCallback",
ExternalProviders = HttpContext.GetExternalProviders() ExternalProviders = HttpContext.GetExternalProviders()
}); });
} }

@ -50,16 +50,15 @@ namespace Yavsc.Controllers
[HttpGet("~/signin")] [HttpGet("~/signin")]
public ActionResult SignIn(string returnUrl = null, string target = null) public ActionResult SignIn(string returnUrl = null)
{ {
_logger.LogWarning($"Singin wanted: returnUrl: {returnUrl} target: {target}"); _logger.LogWarning($"Singin wanted: returnUrl: {returnUrl} ");
// Note: the "returnUrl" parameter corresponds to the endpoint the user agent // Note: the "returnUrl" parameter corresponds to the endpoint the user agent
// will be redirected to after a successful authentication and not // will be redirected to after a successful authentication and not
// the redirect_uri of the requesting client application. // the redirect_uri of the requesting client application.
return View("SignIn", new LoginViewModel return View("SignIn", new LoginViewModel
{ {
ReturnUrl = returnUrl, ReturnUrl = returnUrl,
AfterLoginRedirectUrl = target,
ExternalProviders = HttpContext.GetExternalProviders() ExternalProviders = HttpContext.GetExternalProviders()
}); });
/* Note: When using an external login provider, redirect the query : /* Note: When using an external login provider, redirect the query :
@ -71,7 +70,7 @@ namespace Yavsc.Controllers
[HttpGet("~/authenticate")] [HttpGet("~/authenticate")]
public ActionResult Authenticate(string returnUrl = null) public ActionResult Authenticate(string returnUrl = null)
{ {
return SignIn("/Account/ExternalLoginCallback",returnUrl); return SignIn(returnUrl);
} }
[HttpGet("~/forbidden")] [HttpGet("~/forbidden")]
@ -81,7 +80,7 @@ namespace Yavsc.Controllers
} }
[HttpPost("~/signin")] [HttpPost("~/signin")]
public IActionResult SignIn(string Provider, string ReturnUrl, string AfterLoginRedirectUrl) public IActionResult SignIn(string Provider, string ReturnUrl)
{ {
// Note: the "provider" parameter corresponds to the external // Note: the "provider" parameter corresponds to the external
// authentication provider choosen by the user agent. // authentication provider choosen by the user agent.
@ -97,7 +96,6 @@ namespace Yavsc.Controllers
return HttpBadRequest(); return HttpBadRequest();
} }
// Instruct the middleware corresponding to the requested external identity // Instruct the middleware corresponding to the requested external identity
// provider to redirect the user agent to its own authorization endpoint. // provider to redirect the user agent to its own authorization endpoint.
// Note: the authenticationScheme parameter must match the value configured in Startup.cs // Note: the authenticationScheme parameter must match the value configured in Startup.cs
@ -107,26 +105,13 @@ namespace Yavsc.Controllers
// the redirect_uri of the requesting client application. // the redirect_uri of the requesting client application.
if (string.IsNullOrEmpty(ReturnUrl)) if (string.IsNullOrEmpty(ReturnUrl))
{ {
// If AfterLoginRedirectUrl is non null,
// This is a web interface access,
/// Assert (model.ReturnUrl==null)
/// and the wanted redirection
// after the successfull authentication
if (AfterLoginRedirectUrl != null)
{
ReturnUrl = Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = AfterLoginRedirectUrl });
}
else
{
_logger.LogWarning("ReturnUrl not specified"); _logger.LogWarning("ReturnUrl not specified");
return HttpBadRequest(); return HttpBadRequest();
}
} }
var properties = _signInManager.ConfigureExternalAuthenticationProperties(Provider, ReturnUrl); return new ChallengeResult(Provider, new AuthenticationProperties {
RedirectUri = Url.Action("ExternalLoginCallback","Account", new {returnUrl= ReturnUrl})
return new ChallengeResult(Provider, properties); });
} }
@ -198,7 +183,7 @@ namespace Yavsc.Controllers
{ {
return new ChallengeResult(new AuthenticationProperties return new ChallengeResult(new AuthenticationProperties
{ {
RedirectUri = request.BuildRedirectUrl() RedirectUri = Url.Action("ExternalLoginCallback","Account",new {returnUrl=request.BuildRedirectUrl()})
}); });
} }
// Note: ASOS automatically ensures that an application corresponds to the client_id specified // Note: ASOS automatically ensures that an application corresponds to the client_id specified
@ -253,17 +238,13 @@ namespace Yavsc.Controllers
// ClaimTypes.NameIdentifier is automatically added, even if its // ClaimTypes.NameIdentifier is automatically added, even if its
// destination is not defined or doesn't include "id_token". // destination is not defined or doesn't include "id_token".
// The other claims won't be visible for the client application. // The other claims won't be visible for the client application.
/*
if (claim.Type == ClaimTypes.Name) { if (claim.Type == ClaimTypes.Name) {
claim.SetDestinations(OpenIdConnectConstants.Destinations.AccessToken, claim.WithDestination(OpenIdConnectConstants.TokenTypes.Bearer);
OpenIdConnectConstants.Destinations.IdentityToken); claim.WithDestination( "code");
}
*/
if (claim.Type == ClaimTypes.Name)
{
claim.WithDestination("code");
claim.WithDestination("id_token"); claim.WithDestination("id_token");
} }
identity.AddClaim(claim); identity.AddClaim(claim);
} }

@ -0,0 +1,121 @@
using System;
using System.Linq;
using Microsoft.AspNet.Mvc;
using System.IdentityModel.Tokens;
using System.Security.Claims;
using Microsoft.AspNet.Authorization;
using System.Security.Principal;
using Microsoft.AspNet.Authentication.JwtBearer;
using System.IdentityModel.Tokens.Jwt;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.OptionsModel;
using Yavsc.Auth;
using Microsoft.AspNet.Identity;
using Yavsc.Models;
using System.Threading.Tasks;
namespace Yavsc.Controllers
{
[Produces("application/json"),AllowAnonymous]
public class TokenController : Controller
{
private readonly TokenAuthOptions tokenOptions;
private ILogger logger;
UserManager<ApplicationUser> manager;
SignInManager<ApplicationUser> signInManager;
public class TokenResponse { 
public bool authenticated { get; set; }
public string user_id { get; set; }
public string access_token { get; set; }
public int expires_in { get; set; }
public int entity_id { get; set; }
}
UserTokenProvider tokenProvider;
public TokenController( UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager,
IOptions<TokenAuthOptions> token_options, ILoggerFactory loggerFactory, UserTokenProvider tokenProvider)
{
this.manager = userManager;
this.tokenOptions = token_options.Value;
this.signInManager = signInManager;
this.tokenProvider = tokenProvider;
//this.bearerOptions = options.Value;
//this.signingCredentials = signingCredentials;
logger = loggerFactory.CreateLogger<TokenController>();
}
/// <summary>
/// Check if currently authenticated. Will throw an exception of some sort which shoudl be caught by a general
/// exception handler and returned to the user as a 401, if not authenticated. Will return a fresh token if
/// the user is authenticated, which will reset the expiry.
/// </summary>
/// <returns></returns>
[HttpGet,Authorize]
[Route("~/api/token/get")]
public async Task<dynamic> Get()
{
bool authenticated = false;
string user = null;
int entityId = -1;
string token = null;
DateTime? tokenExpires = default(DateTime?);
var currentUser = User;
if (currentUser != null)
{
authenticated = currentUser.Identity.IsAuthenticated;
if (authenticated)
{
user = User.GetUserId();
logger.LogInformation($"authenticated user:{user}");
foreach (Claim c in currentUser.Claims) if (c.Type == "EntityID") entityId = Convert.ToInt32(c.Value);
tokenExpires = DateTime.UtcNow.AddMinutes(2);
token = await GetToken(user, tokenExpires);
return new TokenResponse { authenticated = authenticated, user_id = user, entity_id = entityId, access_token = token, expires_in = 3400 };
}
}
return new { authenticated = false };
}
public class AuthRequest
{
public string username { get; set; }
public string password { get; set; }
}
/// <summary>
/// Request a new token for a given username/password pair.
/// </summary>
/// <param name="req"></param>
/// <returns></returns>
[HttpPost,Route("~/api/token/post")]
public async Task<IActionResult> Post(AuthRequest req)
{
if (!ModelState.IsValid)
return new BadRequestObjectResult(ModelState);
// Obviously, at this point you need to validate the username and password against whatever system you wish.
var signResult = await signInManager.PasswordSignInAsync(req.username, req.password,false,true);
if (signResult.Succeeded)
{
DateTime? expires = DateTime.UtcNow.AddMinutes(tokenOptions.ExpiresIn);
var token = await GetToken(User.GetUserId(), expires);
return Ok(new TokenResponse { authenticated = true, user_id = User.GetUserId(), access_token = token });
}
return Ok(new TokenResponse { authenticated = false });
}
private async Task<string> GetToken(string userid, DateTime? expires)
{
// Here, you should create or look up an identity for the user which is being authenticated.
// For now, just creating a simple generic identity.
var identuser = await manager.FindByIdAsync(userid);
return await tokenProvider.GenerateAsync("id_token",manager,identuser);
}
}
}

@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc;
using Microsoft.AspNet.Authorization;
namespace Yavsc.Controllers
{
[Route("api/[controller]")]
public class ValuesController : Controller
{
// GET: api/values
[HttpGet]
public IEnumerable<string> Get()
{
//throw new Exception("Horsed");
return new string[] { "value1", "value2" };
}
// GET api/values/5
[HttpGet("{id}")]
[Authorize("Bearer")]
public string Get(int id)
{
return "value";
}
}
}

@ -1,21 +1,29 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks; using System.Threading.Tasks;
using AspNet.Security.OpenIdConnect.Extensions; using AspNet.Security.OpenIdConnect.Extensions;
using AspNet.Security.OpenIdConnect.Server; using AspNet.Security.OpenIdConnect.Server;
using Microsoft.AspNet.Identity;
using Microsoft.Data.Entity; using Microsoft.Data.Entity;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Yavsc.Auth;
using Yavsc.Models; using Yavsc.Models;
namespace Yavsc.Providers { namespace Yavsc.Providers {
public sealed class AuthorizationProvider : OpenIdConnectServerProvider { public sealed class AuthorizationProvider : OpenIdConnectServerProvider {
private ILogger _logger; private ILogger _logger;
UserTokenProvider tokenProvider;
UserManager<ApplicationUser> userManager;
SignInManager<ApplicationUser> signInManager;
public AuthorizationProvider(ILoggerFactory loggerFactory) { public AuthorizationProvider(ILoggerFactory loggerFactory, UserTokenProvider tokenProvider) {
_logger = loggerFactory.CreateLogger<AuthorizationProvider>(); _logger = loggerFactory.CreateLogger<AuthorizationProvider>();
this.tokenProvider = tokenProvider;
} }
public override Task MatchEndpoint(MatchEndpointContext context) { public override Task MatchEndpoint(MatchEndpointContext context) {
// Note: by default, OpenIdConnectServerHandler only handles authorization requests made to the authorization endpoint. // Note: by default, OpenIdConnectServerHandler only handles authorization requests made to the authorization endpoint.
@ -129,5 +137,14 @@ namespace Yavsc.Providers {
_logger.LogWarning($"OIDC success : IsAccessToken: {context.AuthenticationTicket.IsAccessToken()}"); _logger.LogWarning($"OIDC success : IsAccessToken: {context.AuthenticationTicket.IsAccessToken()}");
return Task.FromResult(0); return Task.FromResult(0);
} }
/*
public override async Task SerializeAccessToken(SerializeAccessTokenContext context)
{
var user = await userManager.FindByIdAsync(context.HttpContext.User.GetUserId());
context.AccessToken = await tokenProvider.GenerateAsync("id_token",userManager,user);
context.HandleResponse();
return ;
} */
} }
} }

@ -10,6 +10,7 @@ using System.Web.Optimization;
using AspNet.Security.OpenIdConnect.Extensions; using AspNet.Security.OpenIdConnect.Extensions;
using Microsoft.AspNet.Authentication; using Microsoft.AspNet.Authentication;
using Microsoft.AspNet.Authentication.Cookies; using Microsoft.AspNet.Authentication.Cookies;
using Microsoft.AspNet.Authentication.Google;
using Microsoft.AspNet.Authentication.JwtBearer; using Microsoft.AspNet.Authentication.JwtBearer;
using Microsoft.AspNet.Authentication.OAuth; using Microsoft.AspNet.Authentication.OAuth;
using Microsoft.AspNet.Authorization; using Microsoft.AspNet.Authorization;
@ -35,7 +36,6 @@ using Microsoft.Extensions.PlatformAbstractions;
using Microsoft.Extensions.WebEncoders; using Microsoft.Extensions.WebEncoders;
using Microsoft.Net.Http.Headers; using Microsoft.Net.Http.Headers;
using Yavsc.Auth; using Yavsc.Auth;
using Yavsc.Extensions;
using Yavsc.Formatters; using Yavsc.Formatters;
using Yavsc.Models; using Yavsc.Models;
using Yavsc.Providers; using Yavsc.Providers;
@ -161,7 +161,7 @@ namespace Yavsc
{ {
options.SignInScheme = "ServerCookie"; options.SignInScheme = "ServerCookie";
}); });
/*
services.Configure<TokenAuthOptions>( services.Configure<TokenAuthOptions>(
to => to =>
{ {
@ -169,9 +169,9 @@ namespace Yavsc
to.Issuer = Configuration["Site:Authority"]; to.Issuer = Configuration["Site:Authority"];
to.SigningCredentials = to.SigningCredentials =
new SigningCredentials(key, SecurityAlgorithms.RsaSha256Signature); new SigningCredentials(key, SecurityAlgorithms.RsaSha256Signature);
} }
);*/ );
services.Add(ServiceDescriptor.Singleton(typeof(IOptions<SiteSettings>), typeof(OptionsManager<SiteSettings>))); services.Add(ServiceDescriptor.Singleton(typeof(IOptions<SiteSettings>), typeof(OptionsManager<SiteSettings>)));
services.Add(ServiceDescriptor.Singleton(typeof(IOptions<SmtpSettings>), typeof(OptionsManager<SmtpSettings>))); services.Add(ServiceDescriptor.Singleton(typeof(IOptions<SmtpSettings>), typeof(OptionsManager<SmtpSettings>)));
@ -179,6 +179,9 @@ namespace Yavsc
services.Add(ServiceDescriptor.Singleton(typeof(IOptions<CompanyInfoSettings>), typeof(OptionsManager<CompanyInfoSettings>))); services.Add(ServiceDescriptor.Singleton(typeof(IOptions<CompanyInfoSettings>), typeof(OptionsManager<CompanyInfoSettings>)));
services.Add(ServiceDescriptor.Singleton(typeof(IOptions<OAuth2AppSettings>), typeof(OptionsManager<OAuth2AppSettings>))); services.Add(ServiceDescriptor.Singleton(typeof(IOptions<OAuth2AppSettings>), typeof(OptionsManager<OAuth2AppSettings>)));
services.Add(ServiceDescriptor.Singleton(typeof(IOptions<TokenAuthOptions>), typeof(OptionsManager<TokenAuthOptions>)));
services.AddTransient<Microsoft.Extensions.WebEncoders.UrlEncoder, UrlEncoder>(); services.AddTransient<Microsoft.Extensions.WebEncoders.UrlEncoder, UrlEncoder>();
services.AddDataProtection(); services.AddDataProtection();
services.Add(ServiceDescriptor.Singleton(typeof(IApplicationDiscriminator), services.Add(ServiceDescriptor.Singleton(typeof(IApplicationDiscriminator),
@ -206,6 +209,7 @@ namespace Yavsc
option.User.RequireUniqueEmail = true; option.User.RequireUniqueEmail = true;
option.Cookies.ApplicationCookie.DataProtectionProvider = option.Cookies.ApplicationCookie.DataProtectionProvider =
new MonoDataProtectionProvider(Configuration["Site:Title"]); new MonoDataProtectionProvider(Configuration["Site:Title"]);
option.Cookies.ApplicationCookie.CookieName = "Bearer";
} }
).AddEntityFrameworkStores<ApplicationDbContext>() ).AddEntityFrameworkStores<ApplicationDbContext>()
.AddTokenProvider<EmailTokenProvider<ApplicationUser>>(Constants.EMailFactor) .AddTokenProvider<EmailTokenProvider<ApplicationUser>>(Constants.EMailFactor)
@ -213,7 +217,7 @@ namespace Yavsc
; ;
// .AddTokenProvider<UserTokenProvider>(Constants.SMSFactor) // .AddTokenProvider<UserTokenProvider>(Constants.SMSFactor)
// //
services.AddCors( services.AddCors(
/* /*
options => options =>
@ -242,11 +246,9 @@ namespace Yavsc
}); });
options.AddPolicy("FrontOffice", policy => policy.RequireRole(Constants.FrontOfficeGroupName)); options.AddPolicy("FrontOffice", policy => policy.RequireRole(Constants.FrontOfficeGroupName));
options.AddPolicy("API", policy => options.AddPolicy("Bearer",new AuthorizationPolicyBuilder()
{ .AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
policy.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme); .RequireAuthenticatedUser().Build());
policy.RequireClaim(OpenIdConnectConstants.Claims.Scope, "api-resource-controller");
});
// options.AddPolicy("EmployeeId", policy => policy.RequireClaim("EmployeeId", "123", "456")); // options.AddPolicy("EmployeeId", policy => policy.RequireClaim("EmployeeId", "123", "456"));
// options.AddPolicy("BuildingEntry", policy => policy.Requirements.Add(new OfficeEntryRequirement())); // options.AddPolicy("BuildingEntry", policy => policy.Requirements.Add(new OfficeEntryRequirement()));
// options.AddPolicy("Authenticated", policy => policy.RequireAuthenticatedUser()); // options.AddPolicy("Authenticated", policy => policy.RequireAuthenticatedUser());
@ -259,6 +261,7 @@ namespace Yavsc
services.AddSingleton<IAuthorizationHandler, CommandEditHandler>(); services.AddSingleton<IAuthorizationHandler, CommandEditHandler>();
services.AddSingleton<IAuthorizationHandler, CommandViewHandler>(); services.AddSingleton<IAuthorizationHandler, CommandViewHandler>();
services.AddSingleton<IAuthorizationHandler, PostUserFileHandler>(); services.AddSingleton<IAuthorizationHandler, PostUserFileHandler>();
services.AddMvc(config => services.AddMvc(config =>
{ {
var policy = new AuthorizationPolicyBuilder() var policy = new AuthorizationPolicyBuilder()
@ -302,8 +305,9 @@ namespace Yavsc
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, public void Configure(IApplicationBuilder app, IHostingEnvironment env,
IOptions<SiteSettings> siteSettings, IOptions<RequestLocalizationOptions> localizationOptions, IOptions<SiteSettings> siteSettings,
IOptions<OAuth2AppSettings> oauth2SettingsContainer, IOptions<RequestLocalizationOptions> localizationOptions,
IOptions<OAuth2AppSettings> oauth2SettingsContainer,
ILoggerFactory loggerFactory) ILoggerFactory loggerFactory)
{ {
Startup.UserFilesDirName = siteSettings.Value.UserFiles.DirName; Startup.UserFilesDirName = siteSettings.Value.UserFiles.DirName;
@ -354,16 +358,20 @@ namespace Yavsc
} }
} }
app.UseIISPlatformHandler(
options => options.AuthenticationDescriptions.Clear()
);
var googleOptions = new GoogleOptions var googleOptions = new YavscGoogleOptions
{ {
ClientId = Configuration["Authentication:Google:ClientId"], ClientId = Configuration["Authentication:Google:ClientId"],
ClientSecret = Configuration["Authentication:Google:ClientSecret"], ClientSecret = Configuration["Authentication:Google:ClientSecret"],
AccessType = "offline", /* AccessType = "offline",
SaveTokensAsClaims = true, SaveTokensAsClaims = true,
UserInformationEndpoint = "https://www.googleapis.com/plus/v1/people/me", UserInformationEndpoint = "https://www.googleapis.com/plus/v1/people/me",*/
AutomaticAuthenticate=true,
AutomaticChallenge=true
}; };
var gvents = new OAuthEvents(); var gvents = new OAuthEvents();
googleOptions.Events = new OAuthEvents googleOptions.Events = new OAuthEvents
@ -384,12 +392,6 @@ namespace Yavsc
googleOptions.Scope.Add("https://www.googleapis.com/auth/calendar"); googleOptions.Scope.Add("https://www.googleapis.com/auth/calendar");
app.UseIISPlatformHandler(options =>
{
options.AuthenticationDescriptions.Clear();
options.AutomaticAuthentication = true;
});
app.UseFileServer(new FileServerOptions() app.UseFileServer(new FileServerOptions()
{ {
FileProvider = new PhysicalFileProvider( FileProvider = new PhysicalFileProvider(
@ -404,16 +406,17 @@ namespace Yavsc
EnableDirectoryBrowsing = false EnableDirectoryBrowsing = false
}); });
app.UseStaticFiles().UseWebSockets(); app.UseStaticFiles().UseWebSockets();
app.UseIdentity();
app.UseOpenIdConnectServer(options => app.UseOpenIdConnectServer(options =>
{ {
options.Provider = new AuthorizationProvider(loggerFactory); options.Provider = new AuthorizationProvider(loggerFactory,
new UserTokenProvider());
// Register the certificate used to sign the JWT tokens. // Register the certificate used to sign the JWT tokens.
/* options.SigningCredentials.AddCertificate( // options.SigningCredentials.AddCertificate(
assembly: typeof(Startup).GetTypeInfo().Assembly, // assembly: typeof(Startup).GetTypeInfo().Assembly,
resource: "Mvc.Server.Certificate.pfx", // resource: "Mvc.Server.Certificate.pfx",
password: "Owin.Security.OpenIdConnect.Server"); */ // password: "Owin.Security.OpenIdConnect.Server");
// options.SigningCredentials.AddKey(key); // options.SigningCredentials.AddKey(key);
// Note: see AuthorizationController.cs for more // Note: see AuthorizationController.cs for more
@ -421,6 +424,7 @@ namespace Yavsc
options.ApplicationCanDisplayErrors = true; options.ApplicationCanDisplayErrors = true;
options.AllowInsecureHttp = true; options.AllowInsecureHttp = true;
options.AutomaticChallenge = true; options.AutomaticChallenge = true;
options.AuthorizationEndpointPath = new PathString("/connect/authorize"); options.AuthorizationEndpointPath = new PathString("/connect/authorize");
options.TokenEndpointPath = new PathString("/connect/authorize/accept"); options.TokenEndpointPath = new PathString("/connect/authorize/accept");
options.UseSlidingExpiration = true; options.UseSlidingExpiration = true;
@ -428,54 +432,32 @@ namespace Yavsc
options.AuthenticationScheme = "ServerCookie"; // was = OpenIdConnectDefaults.AuthenticationScheme || "oidc"; options.AuthenticationScheme = "ServerCookie"; // was = OpenIdConnectDefaults.AuthenticationScheme || "oidc";
options.LogoutEndpointPath = new PathString("/connect/logout"); options.LogoutEndpointPath = new PathString("/connect/logout");
/* options.ValidationEndpointPath = new PathString("/connect/introspect"); */ // options.ValidationEndpointPath = new PathString("/connect/introspect");
}); }); /**/
app.UseWhen(context => context.Request.Path.StartsWithSegments(new PathString("/api")), branch =>
{
branch.UseIdentity();
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.UseCookieAuthentication(options =>
app.UseWhen(context => !context.Request.Path.StartsWithSegments(new PathString("/api")), branch =>
{
branch.UseIdentity();
branch.UseCookieAuthentication(options =>
{ {
options.AutomaticAuthenticate = true; options.AutomaticAuthenticate = true;
options.AutomaticChallenge = true; options.AutomaticChallenge = true;
options.AuthenticationScheme = "ServerCookie"; options.AuthenticationScheme = "ServerCookie";
options.CookieName = CookieAuthenticationDefaults.CookiePrefix + "ServerCookie";
options.ExpireTimeSpan = TimeSpan.FromMinutes(5); options.ExpireTimeSpan = TimeSpan.FromMinutes(5);
options.LoginPath = new PathString("/signin"); options.LoginPath = new PathString("/signin");
options.LogoutPath = new PathString("/signout"); options.LogoutPath = new PathString("/signout");
options.ReturnUrlParameter = "target";
}); });
branch.UseMiddleware<GoogleMiddleware>(googleOptions); app.UseMiddleware<Yavsc.Auth.GoogleMiddleware>(googleOptions);
// Facebook // Facebook
branch.UseFacebookAuthentication(options => app.UseFacebookAuthentication(options =>
{ {
options.AppId = Configuration["Authentication:Facebook:AppId"]; options.AppId = Configuration["Authentication:Facebook:AppId"];
options.AppSecret = Configuration["Authentication:Facebook:AppSecret"]; options.AppSecret = Configuration["Authentication:Facebook:AppSecret"];
options.Scope.Add("email"); options.Scope.Add("email");
options.UserInformationEndpoint = "https://graph.facebook.com/v2.5/me?fields=id,name,email,first_name,last_name"; options.UserInformationEndpoint = "https://graph.facebook.com/v2.5/me?fields=id,name,email,first_name,last_name";
}); });
});
app.UseRequestLocalization(localizationOptions.Value, (RequestCulture)new RequestCulture((string)"fr")); app.UseRequestLocalization(localizationOptions.Value, (RequestCulture)new RequestCulture((string)"fr"));
/* Generic OAuth (here GitHub): options.Notifications = new OAuthAuthenticationNotifications /* Generic OAuth (here GitHub): options.Notifications = new OAuthAuthenticationNotifications

@ -23,12 +23,6 @@ namespace Yavsc.ViewModels.Account
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public string ReturnUrl { get; set; } public string ReturnUrl { get; set; }
/// <summary>
/// This is the Url redirection used after a successfull resource grant
/// to a legacy web browser client.
/// </summary>
/// <returns></returns>
public string AfterLoginRedirectUrl { get; set; }
public IEnumerable<AuthenticationDescription> ExternalProviders { get; set; } public IEnumerable<AuthenticationDescription> ExternalProviders { get; set; }
} }

@ -8,10 +8,10 @@ namespace Mvc.Client.Controllers {
public class AuthenticationController : Controller { public class AuthenticationController : Controller {
[HttpGet("~/signin")] [HttpGet("~/signin")]
public ActionResult SignIn(string returnUrl) { public ActionResult SignIn() {
// Instruct the OIDC client middleware to redirect the user agent to the identity provider. // Instruct the OIDC client middleware to redirect the user agent to the identity provider.
// Note: the authenticationType parameter must match the value configured in Startup.cs // Note: the authenticationType parameter must match the value configured in Startup.cs
var properties = new AuthenticationProperties { RedirectUri = "/" }; var properties = new AuthenticationProperties { RedirectUri = "http://localhost:5002/signin-oidc" };
return new ChallengeResult(OpenIdConnectDefaults.AuthenticationScheme, properties); return new ChallengeResult(OpenIdConnectDefaults.AuthenticationScheme, properties);
} }

@ -69,22 +69,32 @@ namespace testOauthClient
LoginPath = new PathString("/signin"), LoginPath = new PathString("/signin"),
LogoutPath = new PathString("/signout") LogoutPath = new PathString("/signout")
}); });
app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions { app.UseOpenIdConnectAuthentication(
RequireHttpsMetadata = false, options => {
options.AuthenticationScheme = OpenIdConnectDefaults.AuthenticationScheme;
options.RequireHttpsMetadata = false;
// Note: these settings must match the application details // Note: these settings must match the application details
// inserted in the database at the server level. // inserted in the database at the server level.
ClientId = "016c5ae4-f4cd-40e3-b250-13701c871ecd", options.ClientId = "016c5ae4-f4cd-40e3-b250-13701c871ecd";
ClientSecret = "blahblah", options.ClientSecret = "blahblah";
PostLogoutRedirectUri = "http://dev.pschneider.fr/", options.PostLogoutRedirectUri = "http://dev.pschneider.fr/";
// Use the authorization code flow. // Use the authorization code flow.
ResponseType = OpenIdConnectResponseTypes.Code, options.ResponseType = OpenIdConnectResponseTypes.Code;
// Note: setting the Authority allows the OIDC client middleware to automatically // Note: setting the Authority allows the OIDC client middleware to automatically
// retrieve the identity provider's configuration and spare you from setting // retrieve the identity provider's configuration and spare you from setting
// the different endpoints URIs or the token validation parameters explicitly. // the different endpoints URIs or the token validation parameters explicitly.
Authority = "http://dev.pschneider.fr/" options.Authority = "http://dev.pschneider.fr/";
// Note: the resource property represents the different endpoints the
// access token should be issued for (values must be space-delimited).
options.Resource = "http://dev.pschneider.fr/";
// options.Scope.Add("api-resource-controller");
}); });

@ -23,7 +23,7 @@
"Microsoft.AspNet.Authentication.Cookies": "1.0.0-rc1-final" "Microsoft.AspNet.Authentication.Cookies": "1.0.0-rc1-final"
}, },
"commands": { "commands": {
"web": "Microsoft.AspNet.Server.Kestrel" "web": "Microsoft.AspNet.Server.Kestrel --server.urls=http://*:5002"
}, },
"frameworks": { "frameworks": {
"dnx451": {} "dnx451": {}

Loading…