OAuth Api call success

This commit is contained in:
2016-06-13 13:33:32 +02:00
parent 8ce7767672
commit ca4625a7cf
22 changed files with 1510 additions and 1779 deletions

View File

@ -7,6 +7,7 @@ using Microsoft.AspNet.Mvc;
using Yavsc.ViewModels.Account;
using System.Security.Claims;
using Microsoft.Extensions.Logging;
using Yavsc.Models.Auth;
namespace Yavsc.WebApi.Controllers
{
@ -121,18 +122,14 @@ namespace Yavsc.WebApi.Controllers
[HttpGet("~/api/me"),Produces("application/json")]
public async Task<IActionResult> Me ()
{
if (User==null) return new BadRequestObjectResult(
new {
error = "no user"
});
if (User==null)
return new BadRequestObjectResult(
new { error = "user not found" });
var uid = User.GetUserId();
if (uid == null)
return new BadRequestObjectResult(
new {
error = "not identified"
});
return Ok(await UserManager.FindByIdAsync(uid));
new { error = "user not identified" });
return Ok(new Me(await UserManager.FindByIdAsync(uid)));
}
}

View File

@ -1,147 +0,0 @@
//
// YavscAuthenticationHandler.cs
//
// Author:
// Paul Schneider <paul@pschneider.fr>
//
// Copyright (c) 2016 GNU GPL
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using System;
using System.Collections.Generic;
using System.Security.Claims;
using System.Threading.Tasks;
namespace Yavsc.Authentication
{
using System.Net.Http;
using System.Net.Http.Headers;
using Microsoft.AspNet.Authentication;
using Microsoft.AspNet.Authentication.OAuth;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Authentication;
using Microsoft.AspNet.Http.Features.Authentication;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json.Linq;
public static class YavscAuthenticationExtensions
{
}
class YavscAuthenticationHandler : OAuthHandler<YavscAuthenticationOptions>
{
private ILogger _logger;
private HttpClient _backchannel;
public YavscAuthenticationHandler(HttpClient backchannel, ILogger logger) : base (backchannel)
{
_backchannel = backchannel;
_logger = logger;
}
protected new async Task<AuthenticationTicket> AuthenticateAsync(AuthenticateContext context)
{
AuthenticationProperties properties = null;
try
{
// ASP.Net Identity requires the NameIdentitifer field to be set or it won't
// accept the external login (AuthenticationManagerExtensions.GetExternalLoginInfo)
string code = null;
string state = null;
IReadableStringCollection query = Request.Query;
IList<string> values = query["code"];
if (values != null && values.Count == 1)
{
code = values[0];
}
values = query["state"];
if (values != null && values.Count == 1)
{
state = values[0];
}
properties = Options.StateDataFormat.Unprotect(state);
if (properties == null)
{
return null;
}
// OAuth2 10.12 CSRF
if (!ValidateCorrelationId(properties))
{
return new AuthenticationTicket(null, properties, this.Options.AuthenticationScheme);
}
string requestPrefix = Request.Scheme + "://" + Request.Host;
string redirectUri = requestPrefix + Request.PathBase + Options.CallbackPath;
// Build up the body for the token request
var body = new List<KeyValuePair<string, string>>();
body.Add(new KeyValuePair<string, string>("grant_type", "authorization_code"));
body.Add(new KeyValuePair<string, string>("code", code));
body.Add(new KeyValuePair<string, string>("redirect_uri", redirectUri));
body.Add(new KeyValuePair<string, string>("client_id", Options.ClientId));
body.Add(new KeyValuePair<string, string>("client_secret", Options.ClientSecret));
// Request the token
HttpResponseMessage tokenResponse =
await _backchannel.PostAsync(Options.TokenEndpoint, new FormUrlEncodedContent(body));
tokenResponse.EnsureSuccessStatusCode();
string text = await tokenResponse.Content.ReadAsStringAsync();
// Deserializes the token response
JObject response = JObject.Parse(text);
string accessToken = response.Value<string>("access_token");
if (string.IsNullOrWhiteSpace(accessToken))
{
_logger.LogWarning("Access token was not found");
return new AuthenticationTicket(null, properties, this.Options.AuthenticationScheme);
}
// Get the user
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, Options.UserInformationEndpoint);
request.Headers.Authorization = new AuthenticationHeaderValue(this.Options.AuthenticationScheme, accessToken);
HttpResponseMessage graphResponse = await _backchannel.SendAsync(request);
graphResponse.EnsureSuccessStatusCode();
text = await graphResponse.Content.ReadAsStringAsync();
JObject user = JObject.Parse(text);
// Read user data
var id = new ClaimsIdentity(
Options.SignInAsAuthenticationType,
ClaimsIdentity.DefaultNameClaimType,
ClaimsIdentity.DefaultRoleClaimType);
context.Authenticated(new ClaimsPrincipal(id)
, new Dictionary<string,string>(), new Dictionary<string,object>{
{ "John" , (object) "Doe" }
});
return new AuthenticationTicket(context.Principal, properties, Options.SignInAsAuthenticationType);
}
catch (Exception ex)
{
_logger.LogError("Authentication failed", ex);
return new AuthenticationTicket(null, properties, this.Options.AuthenticationScheme);
}
}
}
}

View File

@ -1,102 +0,0 @@
//
// YavscAuthenticationMiddleware.cs
//
// Author:
// Paul Schneider <paul@pschneider.fr>
//
// Copyright (c) 2016 GNU GPL
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using System;
using Microsoft.AspNet.Authentication;
using Microsoft.AspNet.Authentication.OAuth;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.DataProtection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.OptionsModel;
using Microsoft.Extensions.WebEncoders;
namespace Yavsc.Authentication
{
public class YavscAuthenticationMiddleware : OAuthMiddleware<YavscAuthenticationOptions>
{
RequestDelegate _next;
ILogger _logger;
public YavscAuthenticationMiddleware(
RequestDelegate next,
IDataProtectionProvider dataProtectionProvider,
ILoggerFactory loggerFactory,
UrlEncoder encoder,
IOptions<SharedAuthenticationOptions> sharedOptions,
YavscAuthenticationOptions options)
: base(next, dataProtectionProvider, loggerFactory, encoder, sharedOptions, options)
{
if (next == null)
{
throw new ArgumentNullException(nameof(next));
}
_next = next;
if (dataProtectionProvider == null)
{
throw new ArgumentNullException(nameof(dataProtectionProvider));
}
if (loggerFactory == null)
{
throw new ArgumentNullException(nameof(loggerFactory));
}
_logger = loggerFactory.CreateLogger<YavscAuthenticationMiddleware>();
if (encoder == null)
{
throw new ArgumentNullException(nameof(encoder));
}
if (sharedOptions == null)
{
throw new ArgumentNullException(nameof(sharedOptions));
}
if (options == null)
{
throw new ArgumentNullException(nameof(options));
}
if(string.IsNullOrEmpty(options.SignInAsAuthenticationType))
{
options.SignInAsAuthenticationType = sharedOptions.Value.SignInScheme;
}
if(options.StateDataFormat == null)
{
var dataProtector = dataProtectionProvider.CreateProtector(typeof(YavscAuthenticationMiddleware).FullName,
options.AuthenticationScheme);
options.StateDataFormat = new PropertiesDataFormat(dataProtector);
}
}
// Called for each request, to create a handler for each request.
protected override AuthenticationHandler<YavscAuthenticationOptions> CreateHandler()
{
return new YavscAuthenticationHandler(this.Backchannel,_logger);
}
}
}

View File

@ -1,62 +0,0 @@
//
// YavscAuthenticationMiddlewareOptions.cs
//
// Author:
// Paul Schneider <paul@pschneider.fr>
//
// Copyright (c) 2016 GNU GPL
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using System;
using Microsoft.AspNet.Authentication.OAuth;
using Microsoft.AspNet.Http;
namespace Yavsc.Authentication
{
public class YavscAuthenticationOptions: OAuthOptions
{
public YavscAuthenticationOptions()
{}
public YavscAuthenticationOptions(string authType, string clientId, string clientSecret)
{
if (authType == null)
throw new NotSupportedException();
Description.AuthenticationScheme = authType;
ClientId = clientId;
ClientSecret = clientSecret;
SignInAsAuthenticationType = authType;
Scope.Clear();
}
public PathString TokenPath { get; set; }
public PathString AuthorizePath { get; set; }
public PathString ReturnUrl { get; set; }
public PathString LoginPath { get; set; }
public PathString LogoutPath { get; set; }
internal string AuthenticationServerUri = "https://accounts.google.com/o/oauth2/auth";
internal string TokenServerUri = "https://accounts.google.com/o/oauth2/token";
private string signInAsAuthenticationType = null;
public string SignInAsAuthenticationType { get
{ return signInAsAuthenticationType ; }
set { signInAsAuthenticationType = value;
ReturnUrl = new PathString("/signin-"+signInAsAuthenticationType.ToLower());
} }
}
}

View File

@ -4,14 +4,26 @@ namespace Yavsc
public static class Constants
{
public const string AccessDeniedPath = "~/signin", AuthorizePath = "~/authorize",
TokenPath = "~/token", LocalLoginPath = "~/signin", LoginPath = "~/signin",
ExternalLoginPath = "~/extsign", LogoutPath = "~/signout", MePath = "~/api/Me";
public const string
public const string ApplicationName = "Yavsc",
CompanyClaimType = "https://schemas.pschneider.fr/identity/claims/Company",
UserNameRegExp = @"^[a-zA-Z][a-zA-Z0-9 ]*$",
AuthorizePath = "~/authorize",
TokenPath = "~/token", LoginPath = "~/signin",
LogoutPath = "~/signout", UserInfoPath = "~/api/me",
ApplicationAuthenticationSheme = "ServerCookie",
ExternalAuthenticationSheme= "ExternalCookie";
ExternalAuthenticationSheme= "ExternalCookie",
CompanyInfoUrl = " https://societeinfo.com/app/rest/api/v1/company/json?registration_number={0}&key={1}",
DefaultFactor = "Default",
MobileAppFactor = "Mobile Application",
EMailFactor = "Email",
SMSFactor = "SMS",
AdminGroupName = "Administrator",
BlogModeratorGroupName = "Moderator",
FrontOfficeGroupName = "FrontOffice",
UserBillsFilesDir= "Bills",
UserFilesDir = "UserFiles",
GCMNotificationUrl = "https://gcm-http.googleapis.com/gcm/send",
KeyProtectorPurpose = "OAuth.AspNet.AuthServer";
public static readonly Scope[] SiteScopes = { 
new Scope { Id = "profile", Description = "Your profile informations" },  
new Scope { Id = "book" , Description ="Your booking interface"},  
@ -23,26 +35,9 @@ namespace Yavsc
new Scope { Id = "frontoffice" , Description ="Your front office interface" }
};
public const string
CompanyInfoUrl = " https://societeinfo.com/app/rest/api/v1/company/json?registration_number={0}&key={1}",
DefaultFactor = "Default",
MobileAppFactor = "Google.clood",
EMailFactor = "Email",
SMSFactor = "Phone",
AdminGroupName = "Administrator",
BlogModeratorGroupName = "Moderator",
FrontOfficeGroupName = "FrontOffice",
UserBillsFilesDir= "Bills",
UserFilesDir = "UserFiles",
GCMNotificationUrl = "https://gcm-http.googleapis.com/gcm/send",
KeyProtectorPurpose = "OAuth.AspNet.AuthServer";
private static readonly string[] GoogleScopes = { "openid", "profile", "email" };
public static readonly string[] GoogleCalendarScopes =
{ "openid", "profile", "email", "https://www.googleapis.com/auth/calendar" };
public const string ApplicationName = "Yavsc",
Issuer = "https://dev.pschneider.fr",
CompanyClaimType = "https://schemas.pschneider.fr/identity/claims/Company",
UserNameRegExp = @"^[a-zA-Z][a-zA-Z0-9 ]*$";
}
}

View File

@ -18,7 +18,7 @@ using Yavsc.Helpers;
namespace Yavsc.Controllers
{
[ServiceFilter(typeof(LanguageActionFilter)),AllowAnonymous]
[ServiceFilter(typeof(LanguageActionFilter)), AllowAnonymous]
public class AccountController : Controller
{
private readonly UserManager<ApplicationUser> _userManager;
@ -49,7 +49,7 @@ namespace Yavsc.Controllers
_smtpSettings = smtpSettings.Value;
_twilioSettings = twilioSettings.Value;
_logger = loggerFactory.CreateLogger<AccountController>();
}
[HttpGet(Constants.LoginPath)]
@ -69,90 +69,84 @@ namespace Yavsc.Controllers
return new ChallengeResult(OpenIdConnectDefaults.AuthenticationScheme, properties);
*/
}
[HttpPost(Constants.LoginPath)]
public async Task<IActionResult> SignIn(SignInViewModel model)
{
if (Request.Method == "POST")
{
if (model.Provider=="LOCAL")
if (model.Provider == "LOCAL")
{
return await Login(model);
if (ModelState.IsValid)
{
// This doesn't count login failures towards account lockout
// To enable password failures to trigger account lockout, set lockoutOnFailure: true
var result = await _signInManager.PasswordSignInAsync(model.UserName, model.Password, model.RememberMe, lockoutOnFailure: false);
if (result.Succeeded)
{
return Redirect(model.ReturnUrl);
}
if (result.RequiresTwoFactor)
{
return RedirectToAction(nameof(SendCode), new { ReturnUrl = model.ReturnUrl, RememberMe = model.RememberMe });
}
if (result.IsLockedOut)
{
_logger.LogWarning(2, "User account locked out.");
return View("Lockout");
}
else
{
ModelState.AddModelError(string.Empty, "Invalid login attempt.");
return View(model);
}
}
// If we got this far, something failed, redisplay form
ModelState.AddModelError(string.Empty, "Unexpected behavior: something failed ... you could try again, or contact me ...");
}
else
{
// Note: the "provider" parameter corresponds to the external
// authentication provider choosen by the user agent.
if (string.IsNullOrEmpty(model.Provider))
{
_logger.LogWarning("Provider not specified");
return HttpBadRequest();
}
if (!_signInManager.GetExternalAuthenticationSchemes().Any(x => x.AuthenticationScheme == model.Provider))
{
_logger.LogWarning($"Provider not found : {model.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(model.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 = model.ReturnUrl });
var properties = _signInManager.ConfigureExternalAuthenticationProperties(model.Provider, redirectUrl);
// var properties = new AuthenticationProperties{RedirectUri=ReturnUrl};
return new ChallengeResult(model.Provider, properties);
}
}
model.ExternalProviders = HttpContext.GetExternalProviders();
return View(model);
}
[HttpPost(Constants.ExternalLoginPath)]
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);
}
public async Task<IActionResult> Login(SignInViewModel model)
{
if (ModelState.IsValid)
{
// This doesn't count login failures towards account lockout
// To enable password failures to trigger account lockout, set lockoutOnFailure: true
var result = await _signInManager.PasswordSignInAsync(model.UserName, model.Password, model.RememberMe, lockoutOnFailure: false);
if (result.Succeeded)
{
return Redirect(model.ReturnUrl);
}
if (result.RequiresTwoFactor)
{
return RedirectToAction(nameof(SendCode), new { ReturnUrl = model.ReturnUrl, RememberMe = model.RememberMe });
}
if (result.IsLockedOut)
{
_logger.LogWarning(2, "User account locked out.");
return View("Lockout");
}
else
{
ModelState.AddModelError(string.Empty, "Invalid login attempt.");
return View(model);
}
}
// If we got this far, something failed, redisplay form
ModelState.AddModelError(string.Empty, "Unexpected behavior: something failed ... you could try again, or contact me ...");
return View(model);
}
//
// GET: /Account/Register
[HttpGet]
@ -198,7 +192,7 @@ namespace Yavsc.Controllers
{
await _signInManager.SignOutAsync();
_logger.LogInformation(4, "User logged out.");
if (returnUrl==null) return RedirectToAction(nameof(HomeController.Index), "Home");
if (returnUrl == null) return RedirectToAction(nameof(HomeController.Index), "Home");
return Redirect(returnUrl);
}
@ -545,7 +539,7 @@ namespace Yavsc.Controllers
return await _userManager.FindByIdAsync(HttpContext.User.GetUserId());
}
#endregion
}

View File

@ -29,6 +29,10 @@ namespace Yavsc.Controllers
{
return View();
}
public IActionResult AboutMarkdown()
{
return View();
}
public IActionResult Contact()
{

View File

@ -167,7 +167,6 @@ namespace Yavsc.Controllers
{
primaryIdentity.AddClaim(new Claim("urn:oauth:scope", scope));
}
_logger.LogWarning("Logging user {principal} against {OAuthDefaults.AuthenticationType}");
await authentication.SignInAsync(OAuthDefaults.AuthenticationType, principal);
}
if (!string.IsNullOrEmpty(Request.Form["submit.Deny"]))

View File

@ -3,6 +3,7 @@ using System.Security.Claims;
using Microsoft.AspNet.Authentication;
using Microsoft.AspNet.Authentication.Cookies;
using Microsoft.AspNet.Authentication.Facebook;
using Microsoft.AspNet.Authentication.JwtBearer;
using Microsoft.AspNet.Authentication.OAuth;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Http;
@ -12,7 +13,9 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.OptionsModel;
using Microsoft.Extensions.WebEncoders;
using OAuth.AspNet.AuthServer;
using OAuth.AspNet.Tokens;
using Yavsc.Auth;
using Yavsc.Extensions;
using Yavsc.Models;
namespace Yavsc
@ -26,6 +29,8 @@ namespace Yavsc
public static FacebookOptions FacebookAppOptions { get; private set; }
public static OAuthAuthorizationServerOptions OAuthServerAppOptions { get; private set; }
public static OAuthOptions OAuthClientOptions { get; set; }
public static YavscGoogleOptions YavscGoogleAppOptions { get; private set; }
public static MonoDataProtectionProvider ProtectionProvider { get; private set; }
@ -38,23 +43,7 @@ namespace Yavsc
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);
}
); */
services.AddAuthentication(options =>
{
options.SignInScheme = Constants.ExternalAuthenticationSheme;
@ -70,7 +59,7 @@ namespace Yavsc
IdentityAppOptions = option;
option.User.AllowedUserNameCharacters += " ";
option.User.RequireUniqueEmail = true;
// option.Cookies.ApplicationCookieAuthenticationScheme = Constants.ApplicationAuthenticationSheme;
// option.Cookies.ApplicationCookieAuthenticationScheme = Constants.ApplicationAuthenticationSheme;
option.Cookies.ApplicationCookie.LoginPath = "/signin";
// option.Cookies.ApplicationCookie.AuthenticationScheme = Constants.ApplicationAuthenticationSheme;
/*
@ -94,93 +83,119 @@ namespace Yavsc
;
}
private void ConfigureOAuthApp(IApplicationBuilder app)
private void ConfigureOAuthApp(IApplicationBuilder app, SiteSettings settings)
{
// External authentication shared cookie:
app.UseCookieAuthentication(options =>
{
ExternalCookieAppOptions = options;
options.AuthenticationScheme = Constants.ExternalAuthenticationSheme;
options.AutomaticAuthenticate = true;
options.ExpireTimeSpan = TimeSpan.FromMinutes(5);
options.LoginPath = new PathString(Constants.LoginPath.Substring(1));
options.AccessDeniedPath = new PathString(Constants.AccessDeniedPath.Substring(1));
});
app.UseIdentity();
app.UseOAuthAuthorizationServer(
options =>
{
OAuthServerAppOptions = options;
options.AuthorizeEndpointPath = new PathString(Constants.AuthorizePath.Substring(1));
options.TokenEndpointPath = new PathString(Constants.TokenPath.Substring(1));
options.ApplicationCanDisplayErrors = true;
options.AllowInsecureHttp = true;
options.AuthenticationScheme = OAuthDefaults.AuthenticationType;
options =>
{
OAuthServerAppOptions = options;
options.AuthorizeEndpointPath = new PathString(Constants.AuthorizePath.Substring(1));
options.TokenEndpointPath = new PathString(Constants.TokenPath.Substring(1));
options.ApplicationCanDisplayErrors = true;
options.AllowInsecureHttp = true;
options.AuthenticationScheme = OAuthDefaults.AuthenticationType;
options.TokenDataProtector = ProtectionProvider.CreateProtector("Bearer protection");
options.Provider = new OAuthAuthorizationServerProvider
{
OnValidateClientRedirectUri = ValidateClientRedirectUri,
OnValidateClientAuthentication = ValidateClientAuthentication,
OnGrantResourceOwnerCredentials = GrantResourceOwnerCredentials,
OnGrantClientCredentials = GrantClientCredetails
};
options.Provider = new OAuthAuthorizationServerProvider
{
OnValidateClientRedirectUri = ValidateClientRedirectUri,
OnValidateClientAuthentication = ValidateClientAuthentication,
OnGrantResourceOwnerCredentials = GrantResourceOwnerCredentials,
OnGrantClientCredentials = GrantClientCredetails
};
options.AuthorizationCodeProvider = new AuthenticationTokenProvider
{
OnCreate = CreateAuthenticationCode,
OnReceive = ReceiveAuthenticationCode,
};
options.AuthorizationCodeProvider = new AuthenticationTokenProvider
{
OnCreate = CreateAuthenticationCode,
OnReceive = ReceiveAuthenticationCode,
};
options.RefreshTokenProvider = new AuthenticationTokenProvider
{
OnCreate = CreateRefreshToken,
OnReceive = ReceiveRefreshToken,
};
options.RefreshTokenProvider = new AuthenticationTokenProvider
{
OnCreate = CreateRefreshToken,
OnReceive = ReceiveRefreshToken,
};
options.AutomaticAuthenticate = true;
options.AutomaticChallenge = true;
}
);
options.AutomaticAuthenticate = true;
options.AutomaticChallenge = true;
}
);
app.UseWhen(context => context.Request.Path.StartsWithSegments("/api"),
branch =>
{
branch.UseJwtBearerAuthentication(
options =>
{
options.AuthenticationScheme = JwtBearerDefaults.AuthenticationScheme;
options.AutomaticAuthenticate = true;
options.SecurityTokenValidators.Clear();
options.SecurityTokenValidators.Add(new TicketDataFormatTokenValidator(
ProtectionProvider
));
}
);
});
app.UseWhen(context => !context.Request.Path.StartsWithSegments("/api"),
branch =>
{
// External authentication shared cookie:
branch.UseCookieAuthentication(options =>
{
ExternalCookieAppOptions = options;
options.AuthenticationScheme = Constants.ExternalAuthenticationSheme;
options.AutomaticAuthenticate = true;
options.ExpireTimeSpan = TimeSpan.FromMinutes(5);
options.LoginPath = new PathString(Constants.LoginPath.Substring(1));
// TODO implement an access denied page
options.AccessDeniedPath = new PathString(Constants.LoginPath.Substring(1));
});
var gvents = new OAuthEvents();
YavscGoogleAppOptions = 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);
}
}
}
};
YavscGoogleAppOptions.Scope.Add("https://www.googleapis.com/auth/calendar");
branch.UseMiddleware<Yavsc.Auth.GoogleMiddleware>(YavscGoogleAppOptions);
// Facebook
branch.UseFacebookAuthentication(options =>
{
FacebookAppOptions = 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.UseIdentity();
var gvents = new OAuthEvents();
YavscGoogleAppOptions = 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);
}
}
}
};
YavscGoogleAppOptions.Scope.Add("https://www.googleapis.com/auth/calendar");
app.UseMiddleware<Yavsc.Auth.GoogleMiddleware>(YavscGoogleAppOptions);
// Facebook
app.UseFacebookAuthentication(options =>
{
FacebookAppOptions = 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";
});
}
}

View File

@ -1,9 +1,12 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Security.Principal;
using System.Threading.Tasks;
using Microsoft.AspNet.Identity;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using OAuth.AspNet.AuthServer;
using Yavsc.Helpers;
@ -73,17 +76,37 @@ namespace Yavsc
return Task.FromResult(0);
}
private Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
private async Task<Task> GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
ApplicationUser user = null;
logger.LogWarning($"GrantResourceOwnerCredentials task ... {context.UserName}");
// var user = ValidateUser(context.UserName, context.Password)
ClaimsPrincipal principal = new ClaimsPrincipal(new ClaimsIdentity(new GenericIdentity(context.UserName, OAuthDefaults.AuthenticationType), context.Scope.Select(x => new Claim("urn:oauth:scope", x))));
using (var usermanager = context.HttpContext.ApplicationServices.GetRequiredService<UserManager<ApplicationUser>>())
{
user = await usermanager.FindByNameAsync(context.UserName);
if (await usermanager.CheckPasswordAsync(user,context.Password))
{
var claims = new List<Claim>(
context.Scope.Select(x => new Claim("urn:oauth:scope", x))
);
claims.Add(new Claim(ClaimTypes.NameIdentifier,user.Id));
claims.Add(new Claim(ClaimTypes.Email,user.Email));
claims.AddRange((await usermanager.GetRolesAsync(user)).Select(
r => new Claim(ClaimTypes.Role,r)
) );
ClaimsPrincipal principal = new ClaimsPrincipal(
new ClaimsIdentity(
new GenericIdentity(context.UserName, OAuthDefaults.AuthenticationType),
claims)
);
// TODO set a NameIdentifier, roles and scopes claims
context.HttpContext.User = principal;
context.Validated(principal);
}
}
return Task.FromResult(0);
}
@ -117,6 +140,9 @@ namespace Yavsc
{
var uid = context.Ticket.Principal.GetUserId();
logger.LogInformation($"CreateRefreshToken for {uid}");
foreach (var c in context.Ticket.Principal.Claims)
logger.LogInformation($"| User claim: {c.Type} {c.Value}");
context.SetToken(context.SerializeTicket());
}
@ -124,6 +150,8 @@ namespace Yavsc
{
var uid = context.Ticket.Principal.GetUserId();
logger.LogInformation($"ReceiveRefreshToken for {uid}");
foreach (var c in context.Ticket.Principal.Claims)
logger.LogInformation($"| User claim: {c.Type} {c.Value}");
context.DeserializeTicket(context.Token);
}
}

View File

@ -29,6 +29,9 @@ namespace Yavsc
public partial class Startup
{
public static string ConnectionString { get; private set; }
public static string Authority { get; private set; }
public static string Audience { get; private set; }
private static ILogger logger;
public Startup(IHostingEnvironment env, IApplicationEnvironment appEnv)
{
@ -268,14 +271,16 @@ namespace Yavsc
}
}
app.UseIISPlatformHandler(options =>
{
options.AuthenticationDescriptions.Clear();
options.AutomaticAuthentication = false;
});
ConfigureOAuthApp(app);
Authority = siteSettings.Value.Authority;
Audience = siteSettings.Value.Audience;
ConfigureOAuthApp(app,siteSettings.Value);
ConfigureFileServerApp(app,siteSettings.Value,env);

View File

@ -0,0 +1,21 @@
namespace Yavsc.Models.Auth
{
public class Me {
public Me(ApplicationUser user)
{
Id = user.Id;
GivenName = user.UserName;
Emails = new string [] { user.Email } ;
}
public string Id { get; set; }
public string GivenName { get; set; }
public string[] Emails { get; set; }
/// <summary>
/// Known as profile, could point to an avatar
/// </summary>
/// <returns></returns>
public string Url { get; set; }
}
}

View File

@ -108,7 +108,7 @@ public class BlogViewHandler : AuthorizationHandler<ViewRequirement, Blog>
protected override void Handle(AuthorizationContext context, PrivateChatEntryRequirement requirement)
{
if (!context.User.HasClaim(c => c.Type == "TemporaryBadgeExpiry" &&
c.Issuer == Constants.Issuer))
c.Issuer == Startup.Authority))
{
return;
}
@ -116,7 +116,7 @@ public class BlogViewHandler : AuthorizationHandler<ViewRequirement, Blog>
var temporaryBadgeExpiry =
Convert.ToDateTime(context.User.FindFirst(
c => c.Type == "TemporaryBadgeExpiry" &&
c.Issuer == Constants.Issuer).Value);
c.Issuer == Startup.Authority).Value);
if (temporaryBadgeExpiry > DateTime.Now)
{
@ -130,7 +130,7 @@ public class BlogViewHandler : AuthorizationHandler<ViewRequirement, Blog>
protected override void Handle(AuthorizationContext context, PrivateChatEntryRequirement requirement)
{
if (!context.User.HasClaim(c => c.Type == "BadgeNumber" &&
c.Issuer == Constants.Issuer))
c.Issuer == Startup.Authority))
{
return;
}

View File

@ -4,10 +4,16 @@
<h1>@ViewData["Title"]</h1>
<markdown>
Quelques extension à un Markdown de base :
Quelques extensions à un Markdown de base :
* les video et audio
* le tag bug = Bug#542
* le tag annonce = A#542
* les video et audio: `![video: Ep. 28 - La France en commun - Une invitation à écrire un nouveau projet d'émancipation humaine](/images/testVideo.mp4)`
![video: Ep. 28 - La France en commun - Une invitation à écrire un nouveau projet d'émancipation humaine](/images/testVideo.mp4)
à faire:
* le tag "Numéro de ticket": `Ticket#6854`
* le tag "Titre d'article": `#1_great_title`
* le tag "User" = `@@(007, it'sme)` ou `@@james`
</markdown>

View File

@ -56,23 +56,23 @@
<hr/>
<h2 class="lead text-left">@SR["Use another service to log in"]:</h2>
@if (Model.ExternalProviders.Count() == 0)
{
<div>
<p>
There are no external authentication services configured. See <a href="http://go.microsoft.com/fwlink/?LinkID=532715">this article</a>
for details on setting up this ASP.NET application to support logging in via external services.
</p>
</div>
}
else
{
@foreach (var description in Model.ExternalProviders) {
<form action="@Constants.ExternalLoginPath" method="post">
{
<div>
<p>
There are no external authentication services configured. See <a href="http://go.microsoft.com/fwlink/?LinkID=532715">this article</a>
for details on setting up this ASP.NET application to support logging in via external services.
</p>
</div>
}
else
{
@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" name="Submit.Login">@SR["Connect using"] @description.DisplayName</button>
@Html.AntiForgeryToken()
</form>
}
}
}
</div>

View File

@ -74,7 +74,6 @@
"Microsoft.AspNet.SignalR.JS": "2.2.0",
"Microsoft.AspNet.Tooling.Razor": "1.0.0-rc1-*",
"Microsoft.AspNet.WebSockets.Server": "1.0.0-rc1-final",
"Microsoft.AspNet.Authentication.OpenIdConnect": "1.0.0-rc1-final",
"Microsoft.Extensions.Configuration.Abstractions": "1.0.0-rc1-final",
"Microsoft.Extensions.Configuration.FileProviderExtensions": "1.0.0-rc1-*",
"Microsoft.Extensions.Configuration.Json": "1.0.0-rc1-*",
@ -105,7 +104,8 @@
"Microsoft.AspNet.Authentication.JwtBearer": "1.0.0-rc1-final",
"PayPalCoreSDK": "1.7.1",
"PayPalButtonManagerSDK": "2.10.109",
"Microsoft.AspNet.Owin": "1.0.0-rc1-final"
"Microsoft.AspNet.Owin": "1.0.0-rc1-final",
"Microsoft.AspNet.Authentication.OAuth": "1.0.0-rc1-final"
},
"commands": {
"web": "Microsoft.AspNet.Server.Kestrel --server.urls http://*:5000",

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -1,6 +1,9 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc;
@ -8,11 +11,35 @@ namespace testOauthClient.Controllers
{
public class HomeController : Controller
{
[HttpGet]
public IActionResult Index()
{
return View();
}
[HttpPost]
public async Task<IActionResult> GetUserInfo(CancellationToken cancellationToken)
{
using (var client = new HttpClient()) {
var request = new HttpRequestMessage(HttpMethod.Get, "http://dev.pschneider.fr/api/me");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", AccessToken);
var response = await client.SendAsync(request, cancellationToken);
response.EnsureSuccessStatusCode();
return View("Index", model: await response.Content.ReadAsStringAsync());
}
}
protected string AccessToken {
get {
var claim = HttpContext.User?.FindFirst("access_token");
if (claim == null) {
throw new InvalidOperationException();
}
return claim.Value;
}
}
public IActionResult About()
{
ViewData["Message"] = "Your application description page.";

View File

@ -78,7 +78,7 @@ namespace testOauthClient
options.ClientId="21d8bd1b-4aed-4fcb-9ed9-00b43f6a8169";
options.ClientSecret="blih";
options.Scope.Add("profile");
options.SaveTokensAsClaims = true;
// options.SaveTokensAsClaims = true;
options.UserInformationEndpoint = "http://dev.pschneider.fr/api/me";
}
);

View File

@ -16,7 +16,7 @@
<h3>Message received from the resource controller: @Model</h3>
}
<form action="~/" method="post">
<form action="~/Home/GetUserInfo" method="post">
<button class="btn btn-lg btn-warning" type="submit">Query the resource controller</button>
</form>

View File

@ -7,7 +7,6 @@
"defaultNamespace": "testOauthClient"
},
"dependencies": {
"Microsoft.AspNet.Authentication.OpenIdConnect": "1.0.0-rc1-final",
"Microsoft.AspNet.Authentication.Cookies": "1.0.0-rc1-final",
"Microsoft.AspNet.Authentication.OAuth": "1.0.0-rc1-final",
"Microsoft.AspNet.Diagnostics": "1.0.0-rc1-final",