This commit is contained in:
2016-06-09 01:29:23 +02:00
parent 849aa6f407
commit 4b8c5cc984
19 changed files with 1713 additions and 7604 deletions

View File

@ -1,7 +1,9 @@
using System;
using System.IdentityModel.Tokens;
namespace Yavsc
{
[Obsolete("Use OAuth2AppSettings instead")]
public class TokenAuthOptions
{
/// <summary>

View File

@ -2,9 +2,17 @@ namespace Yavsc
{
using Yavsc.Models.Auth;
public static class Constants
public static class Constants
{
public const string RememberMeCookieName = "Berme";
public const string AccessDeniedPath = "~/forbidden";
public const string AuthorizePath = "~/authorize";
public const string TokenPath = "~/token";
public const string LoginPath = "~/signin";
public const string LogoutPath = "~/signout";
public const string MePath = "~/api/Me";
public const string ExternalAuthenticationSheme = "External";
public const string ApplicationAuthenticationSheme = "Server";
public static readonly Scope[] SiteScopes = { 
new Scope { Id = "profile", Description = "Your profile informations" },  
new Scope { Id = "book" , Description ="Your booking interface"},  
@ -40,8 +48,6 @@ namespace Yavsc
public const string UserNameRegExp = @"^[a-zA-Z][a-zA-Z0-9 ]*$";
public const string AuthenticationEndPath = "/signin";
public const string TokenEndPath = "/token";
public const string KeyProtectorPurpose = "OAuth.AspNet.AuthServer";

View File

@ -50,18 +50,62 @@ namespace Yavsc.Controllers
_twilioSettings = twilioSettings.Value;
_logger = loggerFactory.CreateLogger<AccountController>();
}
[HttpGet("~/login")]
public IActionResult Login(string returnUrl)
[HttpGet(Constants.LoginPath)]
public ActionResult Login(string returnUrl = null)
{
return View("SignIn", new LoginViewModel {
// 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 against the third
// party identity provider.
return View("Login", new LoginViewModel
{
ReturnUrl = returnUrl,
ExternalProviders = HttpContext.GetExternalProviders()
});
});
/* Note: When using an external login provider, redirect the query :
var properties = _signInManager.ConfigureExternalAuthenticationProperties(OpenIdConnectDefaults.AuthenticationScheme, returnUrl);
return new ChallengeResult(OpenIdConnectDefaults.AuthenticationScheme, properties);
*/
}
[HttpPost("~/login")]
public async Task<IActionResult> SignIn(LoginViewModel model)
public IActionResult ExternalLogin(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("Provider not specified");
return HttpBadRequest();
}
if (!_signInManager.GetExternalAuthenticationSchemes().Any(x => x.AuthenticationScheme == Provider))
{
_logger.LogWarning($"Provider not found : {Provider}");
return HttpBadRequest();
}
// 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
// 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("ReturnUrl not specified");
return HttpBadRequest();
}
// Note: this still is not the redirect uri given to the third party provider, at building the challenge.
var redirectUrl = Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = ReturnUrl });
var properties = _signInManager.ConfigureExternalAuthenticationProperties(Provider, redirectUrl);
// var properties = new AuthenticationProperties{RedirectUri=ReturnUrl};
return new ChallengeResult(Provider, properties);
}
[HttpPost(Constants.LoginPath)]
public async Task<IActionResult> Login(LoginViewModel model)
{
if (ModelState.IsValid)
{
@ -92,6 +136,7 @@ namespace Yavsc.Controllers
ModelState.AddModelError(string.Empty, "Unexpected behavior: something failed ... you could try again, or contact me ...");
return View(model);
}
//
// GET: /Account/Register
[HttpGet]
@ -131,7 +176,7 @@ namespace Yavsc.Controllers
//
// POST: /Account/LogOff
[HttpPost]
[HttpPost(Constants.LogoutPath)]
[ValidateAntiForgeryToken]
public async Task<IActionResult> LogOff(string returnUrl = null)
{
@ -140,18 +185,6 @@ namespace Yavsc.Controllers
if (returnUrl==null) return RedirectToAction(nameof(HomeController.Index), "Home");
return Redirect(returnUrl);
}
//
// POST: /Account/ExternalLogin
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult ExternalLogin(string provider, string returnUrl = null)
{
// 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);
}
//
// GET: /Account/ExternalLoginCallback

View File

@ -1,34 +1,40 @@
using System.Linq;
using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNet.Authorization;
using Microsoft.AspNet.DataProtection.KeyManagement;
using Microsoft.AspNet.Http.Authentication;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Mvc;
using Microsoft.Data.Entity;
using Microsoft.AspNet.WebUtilities;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.OptionsModel;
using Yavsc.Extensions;
using OAuth.AspNet.AuthServer;
using Yavsc.Models;
using Yavsc.ViewModels.Account;
namespace Yavsc.Controllers
{
public class TokenResponse
{
public string access_token { get; set; }
public int expires_in { get; set; }
public string grant_type { get; set; }
public int entity_id { get; set; }
}
[AllowAnonymous]
public class OAuthController : Controller
{
ApplicationDbContext _context;
UserManager<ApplicationUser> _userManager;
SiteSettings _siteSettings;
ILogger _logger;
private readonly SignInManager<ApplicationUser> _signInManager;
private TokenAuthOptions _tokenOptions;
public OAuthController(ApplicationDbContext context, SignInManager<ApplicationUser> signInManager, IKeyManager keyManager,
IOptions<TokenAuthOptions> tokenOptions,
UserManager<ApplicationUser> userManager,
IOptions<SiteSettings> siteSettings,
ILoggerFactory loggerFactory
@ -37,90 +43,61 @@ namespace Yavsc.Controllers
_siteSettings = siteSettings.Value;
_context = context;
_signInManager = signInManager;
_tokenOptions = tokenOptions.Value;
_userManager = userManager;
_logger = loggerFactory.CreateLogger<OAuthController>();
}
[HttpGet("~/signin")]
public ActionResult SignIn(string returnUrl = null)
{
// 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 against the third
// party identity provider.
return View("SignIn", new LoginViewModel
{
ReturnUrl = returnUrl,
ExternalProviders = HttpContext.GetExternalProviders()
});
/* Note: When using an external login provider, redirect the query :
var properties = _signInManager.ConfigureExternalAuthenticationProperties(OpenIdConnectDefaults.AuthenticationScheme, returnUrl);
return new ChallengeResult(OpenIdConnectDefaults.AuthenticationScheme, properties);
*/
}
[HttpGet("~/authenticate")]
public ActionResult Authenticate(string returnUrl = null)
{
return SignIn(returnUrl);
}
[HttpGet("~/forbidden")]
[HttpGet(Constants.AccessDeniedPath)]
public ActionResult Forbidden(string returnUrl = null)
{
return View("Forbidden",returnUrl);
return View("Forbidden", returnUrl);
}
[HttpPost("~/signin")]
public IActionResult SignIn(string Provider, string ReturnUrl)
/*
private async Task<string> GetToken(string purpose, string userid, DateTime? expires)
{
// Note: the "provider" parameter corresponds to the external
// authentication provider choosen by the user agent.
if (string.IsNullOrEmpty(Provider))
{
_logger.LogWarning("Provider not specified");
return HttpBadRequest();
}
// 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 _userManager.FindByIdAsync(userid);
if (!_signInManager.GetExternalAuthenticationSchemes().Any(x => x.AuthenticationScheme == Provider))
{
_logger.LogWarning($"Provider not found : {Provider}");
return HttpBadRequest();
}
// 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
// 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("ReturnUrl not specified");
return HttpBadRequest();
}
var redirectUrl = Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = ReturnUrl });
var properties = _signInManager.ConfigureExternalAuthenticationProperties(Provider, redirectUrl);
// var properties = new AuthenticationProperties{RedirectUri=ReturnUrl};
return new ChallengeResult(Provider,properties);
return await _tokenProvider.GenerateAsync(purpose, _userManager, identuser);
}
[HttpGet("~/signout"), HttpPost("~/signout")]
public async Task SignOut()
/// <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, HttpPost, Authorize]
[Route("~/oauth/token")]
public async Task<dynamic> Get()
{
// Instruct the cookies middleware to delete the local cookie created
// when the user agent is redirected from the external identity provider
// after a successful authentication flow (e.g Google or Facebook).
await HttpContext.Authentication.SignOutAsync("ServerCookie");
}
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("id_token", user, tokenExpires);
return new TokenResponse { access_token = token, expires_in = 3400, entity_id = entityId };
}
}
return new { authenticated = false };
} */
[HttpGet("~/api/getclaims"), Produces("application/json")]
@ -140,14 +117,66 @@ namespace Yavsc.Controllers
return Ok(claims);
}
protected virtual Task<Application> GetApplicationAsync(string identifier, CancellationToken cancellationToken)
[HttpGet(Constants.AuthorizePath)]
public async Task<ActionResult> Authorize()
{
// Retrieve the application details corresponding to the requested client_id.
return (from application in _context.Applications
where application.ApplicationID == identifier
select application).SingleOrDefaultAsync(cancellationToken);
if (Response.StatusCode != 200)
{
return View("AuthorizeError");
}
AuthenticationManager authentication = Request.HttpContext.Authentication;
ClaimsPrincipal principal = await authentication.AuthenticateAsync(Constants.ApplicationAuthenticationSheme);
if (principal == null)
{
await authentication.ChallengeAsync(Constants.ApplicationAuthenticationSheme);
if (Response.StatusCode == 200)
return new HttpUnauthorizedResult();
return new HttpStatusCodeResult(Response.StatusCode);
}
string[] scopes = { };
if (Request.QueryString.HasValue)
{
var queryStringComponents = QueryHelpers.ParseQuery(Request.QueryString.Value);
if (queryStringComponents.ContainsKey("scope"))
scopes = queryStringComponents["scope"];
}
if (Request.Method == "POST")
{
if (!string.IsNullOrEmpty(Request.Form["submit.Grant"]))
{
principal = new ClaimsPrincipal(principal.Identities);
ClaimsIdentity primaryIdentity = (ClaimsIdentity)principal.Identity;
foreach (var scope in scopes)
{
primaryIdentity.AddClaim(new Claim("urn:oauth:scope", scope));
}
await authentication.SignInAsync(OAuthDefaults.AuthenticationType, principal);
}
if (!string.IsNullOrEmpty(Request.Form["submit.Login"]))
{
await authentication.SignOutAsync(Constants.ApplicationAuthenticationSheme);
await authentication.ChallengeAsync(Constants.ApplicationAuthenticationSheme);
return new HttpUnauthorizedResult();
}
}
return View(new AuthorisationView { Scopes = scopes } );
}
}
}

View File

@ -1,114 +0,0 @@
using System;
using Microsoft.AspNet.Mvc;
using System.Security.Claims;
using Microsoft.AspNet.Authorization;
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 string access_token { get; set; }
public int expires_in { get; set; }
public string grant_type { 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,HttpPost,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("id_token", user, tokenExpires);
return new TokenResponse { access_token = token, expires_in = 3400, entity_id = entityId };
}
}
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("id_token",User.GetUserId(), expires);
return Ok(new TokenResponse {access_token = token, expires_in = 3400, grant_type="id_token" });
}
return new BadRequestObjectResult(new { authenticated = false } ) ;
}
private async Task<string> GetToken(string purpose, 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(purpose,manager,identuser);
}
}
}

View File

@ -0,0 +1,41 @@
using System;
using System.IO;
using System.Web;
using Microsoft.AspNet.DataProtection.Infrastructure;
using Microsoft.Extensions.DependencyInjection;
namespace Yavsc
{
public partial class Startup
{
public void ConfigureProtectionServices(IServiceCollection services)
{
services.AddDataProtection();
services.Add(ServiceDescriptor.Singleton(typeof(IApplicationDiscriminator),
typeof(SystemWebApplicationDiscriminator)));
services.ConfigureDataProtection(configure =>
{
configure.SetApplicationName(Configuration["Site:Title"]);
configure.SetDefaultKeyLifetime(TimeSpan.FromDays(45));
configure.PersistKeysToFileSystem(
new DirectoryInfo(Configuration["DataProtection:Keys:Dir"]));
});
}
private sealed class SystemWebApplicationDiscriminator : IApplicationDiscriminator
{
private readonly Lazy<string> _lazyDiscriminator = new Lazy<string>(GetAppDiscriminatorCore);
public string Discriminator => _lazyDiscriminator.Value;
private static string GetAppDiscriminatorCore()
{
return HttpRuntime.AppDomainAppId;
}
}
}
}

View File

@ -0,0 +1,174 @@
using System;
using System.Security.Claims;
using Microsoft.AspNet.Authentication;
using Microsoft.AspNet.Authentication.OAuth;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.OptionsModel;
using Microsoft.Extensions.WebEncoders;
using OAuth.AspNet.AuthServer;
using Yavsc.Auth;
using Yavsc.Models;
namespace Yavsc
{
public partial class Startup
{
private void ConfigureOAuthServices(IServiceCollection services)
{
services.Configure<SharedAuthenticationOptions>(options => options.SignInScheme = Constants.ExternalAuthenticationSheme);
services.AddAuthentication(options =>
{
options.SignInScheme = Constants.ExternalAuthenticationSheme;
});
services.Add(ServiceDescriptor.Singleton(typeof(IOptions<OAuth2AppSettings>), typeof(OptionsManager<OAuth2AppSettings>)));
// used by the YavscGoogleOAuth middelware (TODO drop it)
services.AddTransient<Microsoft.Extensions.WebEncoders.UrlEncoder, UrlEncoder>();
/* Obsolete:
var keyParamsFileInfo =
new FileInfo(Configuration["DataProtection:RSAParamFile"]);
var keyParams = (keyParamsFileInfo.Exists) ?
RSAKeyUtils.GetKeyParameters(keyParamsFileInfo.Name) :
RSAKeyUtils.GenerateKeyAndSave(keyParamsFileInfo.Name);
key = new RsaSecurityKey(keyParams);
services.Configure<TokenAuthOptions>(
to =>
{
to.Audience = Configuration["Site:Audience"];
to.Issuer = Configuration["Site:Authority"];
to.SigningCredentials =
new SigningCredentials(key, SecurityAlgorithms.RsaSha256Signature);
}
); */
}
private void ConfigureOAuthApp(IApplicationBuilder app)
{
app.UseIdentity();
// External authentication shared cookie:
app.UseCookieAuthentication(options =>
{
options.AuthenticationScheme = Constants.ExternalAuthenticationSheme;
options.AutomaticAuthenticate = false;
options.ExpireTimeSpan = TimeSpan.FromMinutes(5);
options.LoginPath = new PathString(Constants.LoginPath.Substring(1));
});
var gvents = new OAuthEvents();
var googleOptions = new YavscGoogleOptions
{
ClientId = Configuration["Authentication:Google:ClientId"],
ClientSecret = Configuration["Authentication:Google:ClientSecret"],
AccessType = "offline",
SaveTokensAsClaims = true,
UserInformationEndpoint = "https://www.googleapis.com/plus/v1/people/me",
Events = new OAuthEvents
{
OnCreatingTicket = async context =>
{
using (var serviceScope = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>()
.CreateScope())
{
var gcontext = context as GoogleOAuthCreatingTicketContext;
context.Identity.AddClaim(new Claim(YavscClaimTypes.GoogleUserId, gcontext.GoogleUserId));
var service =
serviceScope.ServiceProvider.GetService<ApplicationDbContext>();
await service.StoreTokenAsync(gcontext.GoogleUserId, context.TokenResponse);
}
}
}
};
googleOptions.Scope.Add("https://www.googleapis.com/auth/calendar");
app.UseMiddleware<Yavsc.Auth.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
{
OnGetUserInformationAsync = async context =>
{
// Get the GitHub user
HttpRequestMessage userRequest = new HttpRequestMessage(HttpMethod.Get, context.Options.UserInformationEndpoint);
userRequest.Headers.Authorization = new AuthenticationHeaderValue("Bearer", context.AccessToken);
userRequest.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage userResponse = await context.Backchannel.SendAsync(userRequest, context.HttpContext.RequestAborted);
userResponse.EnsureSuccessStatusCode();
var text = await userResponse.Content.ReadAsStringAsync();
JObject user = JObject.Parse(text);
var identity = new ClaimsIdentity(
context.Options.AuthenticationType,
ClaimsIdentity.DefaultNameClaimType,
ClaimsIdentity.DefaultRoleClaimType);
JToken value;
var id = user.TryGetValue("id", out value) ? value.ToString() : null;
if (!string.IsNullOrEmpty(id))
{
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, id, ClaimValueTypes.String, context.Options.AuthenticationType));
}
var userName = user.TryGetValue("login", out value) ? value.ToString() : null;
if (!string.IsNullOrEmpty(userName))
{
identity.AddClaim(new Claim(ClaimsIdentity.DefaultNameClaimType, userName, ClaimValueTypes.String, context.Options.AuthenticationType));
}
var name = user.TryGetValue("name", out value) ? value.ToString() : null;
if (!string.IsNullOrEmpty(name))
{
identity.AddClaim(new Claim("urn:github:name", name, ClaimValueTypes.String, context.Options.AuthenticationType));
}
var link = user.TryGetValue("url", out value) ? value.ToString() : null;
if (!string.IsNullOrEmpty(link))
{
identity.AddClaim(new Claim("urn:github:url", link, ClaimValueTypes.String, context.Options.AuthenticationType));
}
context.Identity = identity;
}
}; */
app.UseOAuthAuthorizationServer(
options =>
{
options.AuthorizeEndpointPath = new PathString(Constants.AuthorizePath.Substring(1));
options.TokenEndpointPath = new PathString(Constants.TokenPath.Substring(1));
options.ApplicationCanDisplayErrors = true;
#if DEBUG
options.AllowInsecureHttp = true;
#endif
options.Provider = new OAuthAuthorizationServerProvider
{
OnValidateClientRedirectUri = ValidateClientRedirectUri,
OnValidateClientAuthentication = ValidateClientAuthentication,
OnGrantResourceOwnerCredentials = GrantResourceOwnerCredentials,
OnGrantClientCredentials = GrantClientCredetails
};
options.AuthorizationCodeProvider = new AuthenticationTokenProvider
{
OnCreate = CreateAuthenticationCode,
OnReceive = ReceiveAuthenticationCode,
};
options.RefreshTokenProvider = new AuthenticationTokenProvider
{
OnCreate = CreateRefreshToken,
OnReceive = ReceiveRefreshToken,
};
options.AutomaticAuthenticate = false;
}
);
}
}
}

View File

@ -1,17 +1,11 @@
using System;
using System.Globalization;
using System.IdentityModel.Tokens;
using System.IO;
using System.Reflection;
using System.Security.Claims;
using System.Web;
using System.Web.Optimization;
using Microsoft.AspNet.Authentication;
using Microsoft.AspNet.Authentication.OAuth;
using Microsoft.AspNet.Authorization;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.DataProtection.Infrastructure;
using Microsoft.AspNet.Diagnostics;
using Microsoft.AspNet.Hosting;
using Microsoft.AspNet.Http;
@ -27,13 +21,11 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.OptionsModel;
using Microsoft.Extensions.PlatformAbstractions;
using Microsoft.Extensions.WebEncoders;
using Microsoft.Net.Http.Headers;
using Yavsc.Auth;
using Yavsc.Formatters;
using Yavsc.Models;
using Yavsc.Services;
using OAuth.AspNet.AuthServer;
namespace Yavsc
{
@ -41,8 +33,6 @@ namespace Yavsc
public partial class Startup
{
public static string ConnectionString { get; private set; }
private RsaSecurityKey key;
public Startup(IHostingEnvironment env, IApplicationEnvironment appEnv)
{
// Set up configuration sources.
@ -97,7 +87,6 @@ namespace Yavsc
new CultureInfo("fr")
};
// You must explicitly state which cultures your application supports.
// These are the cultures the app supports for formatting numbers, dates, etc.
options.SupportedCultures = supportedCultures;
@ -118,54 +107,17 @@ namespace Yavsc
// return new ProviderCultureResult("en");
//}));
});
var keyParamsFileInfo =
new FileInfo(Configuration["DataProtection:RSAParamFile"]);
var keyParams = (keyParamsFileInfo.Exists) ?
RSAKeyUtils.GetKeyParameters(keyParamsFileInfo.Name) :
RSAKeyUtils.GenerateKeyAndSave(keyParamsFileInfo.Name);
key = new RsaSecurityKey(keyParams);
services.Configure<SharedAuthenticationOptions>(options =>
{
options.SignInScheme = "ServerCookie";
});
services.Configure<TokenAuthOptions>(
to =>
{
to.Audience = Configuration["Site:Audience"];
to.Issuer = Configuration["Site:Authority"];
to.SigningCredentials =
new SigningCredentials(key, SecurityAlgorithms.RsaSha256Signature);
}
);
ConfigureOAuthServices(services);
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<GoogleAuthSettings>), typeof(OptionsManager<GoogleAuthSettings>)));
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<TokenAuthOptions>), typeof(OptionsManager<TokenAuthOptions>)));
// DataProtection
ConfigureProtectionServices(services);
services.AddTransient<Microsoft.Extensions.WebEncoders.UrlEncoder, UrlEncoder>();
services.AddDataProtection();
services.Add(ServiceDescriptor.Singleton(typeof(IApplicationDiscriminator),
typeof(SystemWebApplicationDiscriminator)));
services.ConfigureDataProtection(configure =>
{
configure.SetApplicationName(Configuration["Site:Title"]);
configure.SetDefaultKeyLifetime(TimeSpan.FromDays(45));
configure.PersistKeysToFileSystem(
new DirectoryInfo(Configuration["DataProtection:Keys:Dir"]));
});
services.AddAuthentication(
op => op.SignInScheme = "ServerCookie"
);
// Add framework services.
services.AddEntityFramework()
.AddNpgsql()
@ -179,6 +131,8 @@ namespace Yavsc
option.User.RequireUniqueEmail = true;
option.Cookies.ApplicationCookie.DataProtectionProvider =
new MonoDataProtectionProvider(Configuration["Site:Title"]);
option.Cookies.ApplicationCookie.LoginPath = new PathString(Constants.LoginPath.Substring(1));
option.Cookies.ApplicationCookie.AccessDeniedPath = new PathString(Constants.AccessDeniedPath.Substring(1));
}
).AddEntityFrameworkStores<ApplicationDbContext>()
.AddTokenProvider<EmailTokenProvider<ApplicationUser>>(Constants.EMailFactor)
@ -331,181 +285,21 @@ namespace Yavsc
}
}
var googleOptions = new YavscGoogleOptions
{
ClientId = Configuration["Authentication:Google:ClientId"],
ClientSecret = Configuration["Authentication:Google:ClientSecret"],
AccessType = "offline",
SaveTokensAsClaims = true,
UserInformationEndpoint = "https://www.googleapis.com/plus/v1/people/me"
};
var gvents = new OAuthEvents();
googleOptions.Events = new OAuthEvents
{
OnCreatingTicket = async context =>
{
using (var serviceScope = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>()
.CreateScope())
{
var gcontext = context as GoogleOAuthCreatingTicketContext;
context.Identity.AddClaim(new Claim(YavscClaimTypes.GoogleUserId, gcontext.GoogleUserId));
var service =
serviceScope.ServiceProvider.GetService<ApplicationDbContext>();
await service.StoreTokenAsync(gcontext.GoogleUserId, context.TokenResponse);
}
}
};
googleOptions.Scope.Add("https://www.googleapis.com/auth/calendar");
app.UseIISPlatformHandler(options =>
{
options.AuthenticationDescriptions.Clear();
options.AutomaticAuthentication = true;
});
ConfigureOAuthApp(app);
ConfigureFileServerApp(app,siteSettings.Value,env);
app.UseWebSockets();
app.UseIdentity();
/* app.UseOpenIdConnectServer(options =>
{
options.Provider = new AuthorizationProvider(loggerFactory,
new UserTokenProvider());
// 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.AutomaticAuthenticate=true;
options.AuthorizationEndpointPath = new PathString("/connect/authorize");
options.TokenEndpointPath = new PathString("/connect/authorize/accept");
options.UseSlidingExpiration = true;
options.AllowInsecureHttp = true;
options.AuthenticationScheme = "oidc-server"; // was = OpenIdConnectDefaults.AuthenticationScheme || "oidc";
options.LogoutEndpointPath = new PathString("/connect/logout");
// options.ValidationEndpointPath = new PathString("/connect/introspect");
}); */
app.UseCookieAuthentication(options =>
{
options.AutomaticAuthenticate = true;
options.AutomaticChallenge = true;
options.AuthenticationScheme = "ServerCookie";
options.ExpireTimeSpan = TimeSpan.FromMinutes(5);
options.LoginPath = new PathString("/signin");
options.LogoutPath = new PathString("/signout");
});
app.UseMiddleware<Yavsc.Auth.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";
});
app.UseOAuthAuthorizationServer(
options =>
{
options.AuthorizeEndpointPath = new PathString("/signin");
options.TokenEndpointPath = new PathString("/token");
options.ApplicationCanDisplayErrors = true;
#if DEBUG
options.AllowInsecureHttp = true;
#endif
options.Provider = new OAuthAuthorizationServerProvider
{
OnValidateClientRedirectUri = ValidateClientRedirectUri,
OnValidateClientAuthentication = ValidateClientAuthentication,
OnGrantResourceOwnerCredentials = GrantResourceOwnerCredentials,
OnGrantClientCredentials = GrantClientCredetails
};
options.AuthorizationCodeProvider = new AuthenticationTokenProvider
{
OnCreate = CreateAuthenticationCode,
OnReceive = ReceiveAuthenticationCode,
};
options.RefreshTokenProvider = new AuthenticationTokenProvider
{
OnCreate = CreateRefreshToken,
OnReceive = ReceiveRefreshToken,
};
options.AutomaticAuthenticate = false;
}
);
app.UseRequestLocalization(localizationOptions.Value, (RequestCulture)new RequestCulture((string)"fr"));
/* Generic OAuth (here GitHub): options.Notifications = new OAuthAuthenticationNotifications
{
OnGetUserInformationAsync = async context =>
{
// Get the GitHub user
HttpRequestMessage userRequest = new HttpRequestMessage(HttpMethod.Get, context.Options.UserInformationEndpoint);
userRequest.Headers.Authorization = new AuthenticationHeaderValue("Bearer", context.AccessToken);
userRequest.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage userResponse = await context.Backchannel.SendAsync(userRequest, context.HttpContext.RequestAborted);
userResponse.EnsureSuccessStatusCode();
var text = await userResponse.Content.ReadAsStringAsync();
JObject user = JObject.Parse(text);
var identity = new ClaimsIdentity(
context.Options.AuthenticationType,
ClaimsIdentity.DefaultNameClaimType,
ClaimsIdentity.DefaultRoleClaimType);
JToken value;
var id = user.TryGetValue("id", out value) ? value.ToString() : null;
if (!string.IsNullOrEmpty(id))
{
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, id, ClaimValueTypes.String, context.Options.AuthenticationType));
}
var userName = user.TryGetValue("login", out value) ? value.ToString() : null;
if (!string.IsNullOrEmpty(userName))
{
identity.AddClaim(new Claim(ClaimsIdentity.DefaultNameClaimType, userName, ClaimValueTypes.String, context.Options.AuthenticationType));
}
var name = user.TryGetValue("name", out value) ? value.ToString() : null;
if (!string.IsNullOrEmpty(name))
{
identity.AddClaim(new Claim("urn:github:name", name, ClaimValueTypes.String, context.Options.AuthenticationType));
}
var link = user.TryGetValue("url", out value) ? value.ToString() : null;
if (!string.IsNullOrEmpty(link))
{
identity.AddClaim(new Claim("urn:github:url", link, ClaimValueTypes.String, context.Options.AuthenticationType));
}
context.Identity = identity;
}
}; */
app.UseMvc(routes =>
{
routes.MapRoute(
@ -515,17 +309,7 @@ namespace Yavsc
app.UseSignalR();
}
private sealed class SystemWebApplicationDiscriminator : IApplicationDiscriminator
{
private readonly Lazy<string> _lazyDiscriminator = new Lazy<string>(GetAppDiscriminatorCore);
public string Discriminator => _lazyDiscriminator.Value;
private static string GetAppDiscriminatorCore()
{
return HttpRuntime.AppDomainAppId;
}
}
// Entry point for the application.
public static void Main(string[] args) => Microsoft.AspNet.Hosting.WebApplication.Run<Startup>(args);
}

View File

@ -1,11 +1,12 @@
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using System.Collections.Generic;
namespace Yavsc
{
public class AuthorisationView { 
public OpenIdConnectMessage Message { get; set; }
public Application Application { get; set; }
public IEnumerable<string> Scopes { get; set; }
public string RedirectUrl { get; set; }
public string Message { get; set; }
}
}

View File

@ -1,20 +1,18 @@
@using System.Collections.Generic
@using Microsoft.AspNet.Http
@using Microsoft.AspNet.Http.Authentication
@model LoginViewModel
@inject SignInManager<ApplicationUser> SignInManager
@using Microsoft.AspNet.Http.Authentication
@using Yavsc.ViewModels.Account
@model LoginViewModel
@{
ViewData["Title"] = SR["Log in"];
}
<h2>@ViewData["Title"]</h2>
<div class="row">
<div class="col-md-8">
<section>
<form asp-controller="Account" asp-action="Login" asp-route-returnurl="@ViewData["ReturnUrl"]" method="post" class="form-horizontal" role="form">
<h4>@SR["Use a local account to log in."]</h4>
<hr />
<div class="jumbotron">
<h1>@ViewData["Title"]</h1>
<hr/>
<h2 class="lead text-left">@SR["Use a local account to log in"]</h2>
<form action="@Constants.LoginPath" method="post" class="form-horizontal" role="form">
<div asp-validation-summary="ValidationSummary.All" class="text-danger"></div>
<div class="form-group">
<label for="UserName" class="col-md-2 control-label">@SR["UserName"]</label>
@ -40,25 +38,24 @@
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<button type="submit" class="btn btn-default">@SR["Login"]</button>
<button type="submit" class="btn btn-lg btn-success">@SR["Login"]</button>
</div>
</div>
<p>
<a asp-action="Register">@SR["Register as a new user?"]</a>
<a asp-action="Register" asp-controller="Account">@SR["Register as a new user"]?</a>
</p>
<p>
<a asp-action="ForgotPassword">@SR["Forgot your password?"]</a>
<a asp-action="ForgotPassword" asp-controller="Account">@SR["Forgot your password"]?</a>
</p>
<input type="hidden" name="Provider" value="LOCAL" />
<input type="hidden" name="ReturnUrl" value="@Model.ReturnUrl" />
@Html.AntiForgeryToken()
</form>
</section>
</div>
<div class="col-md-4">
<section>
<h4>@SR["Use another service to log in."]</h4>
<hr />
@{
var loginProviders = SignInManager.GetExternalAuthenticationSchemes().ToList();
if (loginProviders.Count == 0)
<hr/>
<h2 class="lead text-left">@SR["Use another service to log in"]:</h2>
@if (Model.ExternalProviders?.Count() == 0)
{
<div>
<p>
@ -69,22 +66,13 @@
}
else
{
<form asp-controller="Account" asp-action="ExternalLogin" asp-route-returnurl="@ViewData["ReturnUrl"]" method="post" class="form-horizontal" role="form">
<div>
<p>
@foreach (var provider in loginProviders)
{
<button type="submit" class="btn btn-default" name="provider" value="@provider.AuthenticationScheme" title="Log in using your @provider.DisplayName account">@provider.AuthenticationScheme</button>
}
</p>
</div>
</form>
@foreach (var description in Model.ExternalProviders) {
<form action="@Constants.LoginPath" method="post">
<input type="hidden" name="Provider" value="@description.AuthenticationScheme" />
<input type="hidden" name="ReturnUrl" value="@Model.ReturnUrl" />
<button class="btn btn-lg btn-success" type="submit">@SR["Connect using"] @description.DisplayName</button>
@Html.AntiForgeryToken()
</form>
}
}
</section>
</div>
}
</div>
@section Scripts {
@{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
}

View File

@ -0,0 +1,32 @@
@using Microsoft.AspNet.Http.Authentication
@using Microsoft.AspNet.WebUtilities
@using System.Security.Claims
@{
AuthenticationManager authentication = Context.Authentication;
ClaimsPrincipal principal = authentication.AuthenticateAsync(Constants.ApplicationAuthenticationSheme).Result;
string[] scopes = QueryHelpers.ParseQuery(Context.Request.QueryString.Value)["scope"];
}
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Authorize</title>
</head>
<body>
<h1>Authorization Server</h1>
<h2>OAuth2 Authorize</h2>
<form method="POST">
<p>Hello, @principal.Identity.Name</p>
<p>A third party application want to do the following on your behalf:</p>
<ul>
@foreach (var scope in scopes)
{
<li>@scope</li>
}
</ul>
<p>
<input type="submit" name="submit.Grant" value="Grant" />
<input type="submit" name="submit.Login" value="Sign in as different user" />
</p>
</form>
</body>
</html>

View File

@ -0,0 +1,20 @@
@using Microsoft.AspNet.Http
@using System
@using System.Security.Claims
@{
var error = Context.Items["oauth.Error"];
var errorDescription = Context.Items["oauth.ErrorDescription"];
var errorUri = Context.Items["oauth.ErrorUri"];
}
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Authorize Error</title>
</head>
<body>
<h1>Katana.Sandbox.WebServer</h1>
<h2>OAuth2 Authorize Error</h2>
<p>Error: @error</p>
<p>@errorDescription</p>
</body>
</html>

View File

@ -1,22 +1,22 @@
@using AspNet.Security.OpenIdConnect.Extensions
@using Microsoft.IdentityModel.Protocols.OpenIdConnect

@model AuthorisationView
<div class="jumbotron">
<h1>Authorization</h1>
<h1>Authorization @Application?.DisplayName</h1>
<p>@Model.Message</p>
<p class="lead text-left">Do you wanna grant <strong>@Model.Application.DisplayName</strong> an access to your resources? (scopes requested: @Model.Message.Scope)</p>
<form enctype="application/x-www-form-urlencoded" method="post">
@Html.AntiForgeryToken()
@foreach (var parameter in Model.Message.Parameters) {
<input type="hidden" name="@parameter.Key" value="@parameter.Value" />
@foreach (var scope in Model.Scopes) {
<li>@scope</li>
}
<input formaction="/connect/authorize/accept" class="btn btn-lg btn-success" name="Authorize" type="submit" value="Yeah, sure" />
<input formaction="/api/token/get" class="btn btn-lg btn-success" name="Authorize" type="submit" value="hum" />
<input formaction="/connect/authorize/deny" class="btn btn-lg btn-danger" name="Deny" type="submit" value="Hell, no" />
</form>
@Html.Hidden("ReturnUrl")
<input formaction="/oauth/accept" class="btn btn-lg btn-success" name="Authorize" type="submit" value="Yeah, sure" />
<input formaction="/oauth/deny" class="btn btn-lg btn-danger" name="Deny" type="submit" value="Hell, no" />
<input formaction="/oauth/deny" class="btn btn-lg btn-success" name="Submit.Login" type="submit" value="Login using another account" />
</form>
</div>

View File

@ -11,7 +11,7 @@
<hr/>
<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="@Constants.LoginPath" method="post" class="form-horizontal" role="form">
<div asp-validation-summary="ValidationSummary.All" class="text-danger"></div>
<div class="form-group">
@ -67,7 +67,7 @@
else
{
@foreach (var description in Model.ExternalProviders) {
<form action="/signin" method="post">
<form action="@Constants.LoginPath" method="post">
<input type="hidden" name="Provider" value="@description.AuthenticationScheme" />
<input type="hidden" name="ReturnUrl" value="@Model.ReturnUrl" />
<button class="btn btn-lg btn-success" type="submit">@SR["Connect using"] @description.DisplayName</button>

View File

@ -48,7 +48,7 @@ gulp.task("min:css", function () {
gulp.task("min", ["min:js", "min:css"]);
gulp.task('watch', shell.task(['MONO_OPTIONS=--debug ASPNET_ENV=Development dnx-watch web --configuration=Debug']))
gulp.task('watch', shell.task(['ASPNET_ENV=Development dnx-watch web --configuration=Debug']))
gulp.task('build', shell.task(['dnu build --configuration=Debug']))
gulp.task('publish', shell.task(['dnu publish -o ../build']))

View File

@ -88,18 +88,15 @@
"Microsoft.Extensions.Globalization.CultureInfoCache": "1.0.0-rc1-final",
"Microsoft.Extensions.Localization": "1.0.0-rc1-final",
"Microsoft.Extensions.Localization.Abstractions": "1.0.0-rc1-final",
"Microsoft.Framework.ConfigurationModel.Json": "1.0.0-beta4",
"Microsoft.Framework.DependencyInjection": "1.0.0-beta8",
"Microsoft.Extensions.CodeGeneration": "1.0.0-rc1-final",
"Microsoft.Extensions.PlatformAbstractions": "1.0.0-rc1-final",
"Microsoft.Extensions.CodeGenerators.Mvc": "1.0.0-rc1-final",
"Microsoft.Framework.ConfigurationModel.Json": "1.0.0-beta4",
"Microsoft.Framework.Configuration.Abstractions": "1.0.0-beta8",
"Microsoft.Framework.Configuration.Binder": "1.0.0-beta8",
"Microsoft.Framework.Configuration.Json": "1.0.0-beta8",
"Microsoft.AspNet.Session": "1.0.0-rc1-final",
"Microsoft.NETCore.Platforms": "1.0.1-beta-23516",
"Microsoft.Framework.Configuration.Abstractions": "1.0.0-beta8",
"Microsoft.Framework.Configuration.Json": "1.0.0-beta8",
"Microsoft.Framework.DependencyInjection.Abstractions": "1.0.0-beta8",
"Microsoft.Framework.Configuration.Binder": "1.0.0-beta8",
"Microsoft.AspNet.Web.Optimization": "1.1.3",
"Microsoft.Extensions.WebEncoders.Core": "1.0.0-rc1-final",
"Microsoft.AspNetCore.Authentication.OAuth": "0.0.1-alpha",

File diff suppressed because it is too large Load Diff

Submodule external/oauth-aspnet updated: 3ce8e24903...1fdf3cb799

View File

@ -67,7 +67,7 @@ namespace testOauthClient
app.UseOAuthAuthentication(
options => { 
options.AuthenticationScheme="yavsc";
options.AuthorizationEndpoint="http://dev.pschneider.fr/signin";
options.AuthorizationEndpoint="http://dev.pschneider.fr/authorize";
options.TokenEndpoint="http://dev.pschneider.fr/token";
options.AutomaticAuthenticate=true;
options.AutomaticChallenge=true;