From 888caf4264ea92f0b29ef9ae8c7de9f94cc673e2 Mon Sep 17 00:00:00 2001 From: Paul Schneider Date: Mon, 6 Jun 2016 17:13:39 +0200 Subject: [PATCH] oidc++ --- Yavsc/Views/Shared/Authorize.cshtml | 2 +- Yavsc/Views/Shared/_GetAToken.cshtml | 7 + Yavsc/Views/Shared/_LoginPartial.cshtml | 8 - Yavsc/project.json.new | 152 ++++++++++++++++++ Yavsc/src/Controllers/OAuthController.cs | 43 ++--- Yavsc/src/Controllers/TokenController.cs | 23 ++- Yavsc/src/Startup.cs | 2 +- .../src/ViewModels/Auth/AuthorisationView.cs | 1 - testOauthClient/Startup.cs | 4 +- 9 files changed, 189 insertions(+), 53 deletions(-) create mode 100644 Yavsc/Views/Shared/_GetAToken.cshtml create mode 100755 Yavsc/project.json.new diff --git a/Yavsc/Views/Shared/Authorize.cshtml b/Yavsc/Views/Shared/Authorize.cshtml index 3598a273..76efe101 100644 --- a/Yavsc/Views/Shared/Authorize.cshtml +++ b/Yavsc/Views/Shared/Authorize.cshtml @@ -15,8 +15,8 @@ @foreach (var parameter in Model.Message.Parameters) { } - + diff --git a/Yavsc/Views/Shared/_GetAToken.cshtml b/Yavsc/Views/Shared/_GetAToken.cshtml new file mode 100644 index 00000000..8a217944 --- /dev/null +++ b/Yavsc/Views/Shared/_GetAToken.cshtml @@ -0,0 +1,7 @@ + + \ No newline at end of file diff --git a/Yavsc/Views/Shared/_LoginPartial.cshtml b/Yavsc/Views/Shared/_LoginPartial.cshtml index 02ad9d5f..6d2e13b5 100755 --- a/Yavsc/Views/Shared/_LoginPartial.cshtml +++ b/Yavsc/Views/Shared/_LoginPartial.cshtml @@ -13,14 +13,6 @@ - - - } else { diff --git a/Yavsc/project.json.new b/Yavsc/project.json.new new file mode 100755 index 00000000..b6089572 --- /dev/null +++ b/Yavsc/project.json.new @@ -0,0 +1,152 @@ +{ + "version": "1.0.0-*", + "authors": [ + "pazof" + ], + "tags": [ + "" + ], + "projectUrl": "http://yavsc.pschneider.fr", + "licenseUrl": "", + "userSecretsId": "aspnet5-YavscWeb-a0dadd21-2ced-43d3-96f9-7e504345102f", + "compilationOptions": { + "emitEntryPoint": true + }, + "compile": [ + "*.cs" + ], + "resource": [ + "Resources/**/*.resx" + ], + "configurations": { + "Debug": { + "compilationOptions": { + "emitEntryPoint": true, + "define": [ + "DEBUG", + "TRACE" + ], + "optimize": false, + "debugType": "full" + } + }, + "Release": { + "compilationOptions": { + "define": [ + "RELEASE", + "TRACE" + ], + "optimize": true + } + } + }, + "webroot": "wwwroot", + "tooling": { + "defaultNamespace": "Yavsc" + }, + "dependencies": { + "EntityFramework.Core": "7.0.0-rc1-*", + "EntityFramework.Relational": "7.0.0-rc1-*", + "EntityFramework.Commands": "7.0.0-rc1-*", + "EntityFramework.Sqlite": "7.0.0-rc1-*", + "EntityFramework.MicrosoftSqlServer": "7.0.0-rc1-*", + "EntityFramework7.Npgsql": "3.1.0-*", + "EntityFramework7.Npgsql.Design": "3.1.0-*", + "Microsoft.AspNet.Authentication.Cookies": "1.0.0-rc1-*", + "Microsoft.AspNet.Diagnostics.Entity": "7.0.0-rc1-*", + "Microsoft.AspNet.Identity.EntityFramework": "3.0.0-rc1-*", + "Microsoft.AspNet.IISPlatformHandler": "1.0.0-rc1-*", + "Microsoft.AspNet.Mvc": "6.0.0-rc1-*", + "Microsoft.AspNet.Mvc.TagHelpers": "6.0.0-rc1-*", + "Microsoft.AspNet.Server.Kestrel": "1.0.0-rc1-final", + "Microsoft.AspNet.StaticFiles": "1.0.0-rc1-*", + "Microsoft.AspNet.Tooling.Razor": "1.0.0-rc1-*", + "Microsoft.Extensions.Configuration.FileProviderExtensions": "1.0.0-rc1-*", + "Microsoft.Extensions.Configuration.Json": "1.0.0-rc1-*", + "Microsoft.Extensions.Configuration.Abstractions": "1.0.0-rc1-final", + "Microsoft.Extensions.Configuration.UserSecrets": "1.0.0-rc1-*", + "Microsoft.Extensions.Logging": "1.0.0-rc1-final", + "Microsoft.Extensions.Logging.Console": "1.0.0-rc1-final", + "Microsoft.Extensions.Logging.Debug": "1.0.0-rc1-final", + "Microsoft.Framework.DependencyInjection": "1.0.0-beta8", + "Microsoft.Extensions.DependencyInjection.Abstractions": "1.0.0-rc1-final", + "Microsoft.AspNet.Authentication.Facebook": "1.0.0-rc1-final", + "Microsoft.AspNet.Authentication.Twitter": "1.0.0-rc1-final", + "Microsoft.Extensions.Localization": "1.0.0-rc1-final", + "Microsoft.Extensions.Localization.Abstractions": "1.0.0-rc1-final", + "Microsoft.Extensions.Globalization.CultureInfoCache": "1.0.0-rc1-final", + "Microsoft.AspNet.Localization": "1.0.0-rc1-final", + "Microsoft.Framework.ConfigurationModel.Json": "1.0.0-beta4", + "MarkdownDeep-av.NET": "1.5.2", + "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.AspNet.Session": "1.0.0-rc1-final", + "Microsoft.NETCore.Platforms": "1.0.1-beta-23516", + "Microsoft.AspNet.SignalR.JS": "2.2.0", + "Microsoft.AspNet.WebSockets.Server": "1.0.0-rc1-final", + "Microsoft.AspNet.Http.Abstractions": "1.0.0-rc1-final", + "Microsoft.AspNet.SignalR.Owin": "1.2.2", + "Microsoft.AspNet.Owin": "1.0.0-rc1-final", + "Microsoft.AspNet.SignalR.Core": "2.2.0", + "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", + "MailKit": "1.3.0-beta7", + "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", + "PayPalCoreSDK": "1.7.1", + "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", + "Google.Apis.Core": "1.11.1", + "Google.Apis": "1.11.1", + "PayPalButtonManagerSDK": "2.10.109", + "Microsoft.AspNet.DataProtection": "1.0.0-rc1-final", + "Microsoft.AspNet.DataProtection.SystemWeb": "1.0.0-rc1-final", + "Microsoft.AspNet.Authentication.JwtBearer": "1.0.0-rc1-final", + "System.IdentityModel.Tokens": "5.0.0-rc1-211161024", + "System.IdentityModel.Tokens.Jwt": "5.0.0-rc1-211161024", + "Microsoft.AspNet.Authorization": "1.0.0-rc1-final", + "AspNet.Security.OpenIdConnect.Server": "1.0.0-beta4" + }, + "commands": { + "web": "Microsoft.AspNet.Server.Kestrel --server.urls http://*:5000", + "lua": "Microsoft.AspNet.Hosting --server Microsoft.AspNet.Server.Kestrel --server.urls http://*:85", + "kestrel": "Microsoft.AspNet.Hosting --server Microsoft.AspNet.Server.Kestrel --server.urls http://*:5000", + "booking": "Microsoft.AspNet.Hosting --server Microsoft.AspNet.Server.Kestrel --server.urls http://*:87", + "yavsc": "Microsoft.AspNet.Hosting --server Microsoft.AspNet.Server.Kestrel --server.urls http://*:86", + "yavscpre": "Microsoft.AspNet.Hosting --server Microsoft.AspNet.Server.Kestrel --server.urls http://*:84", + "ef": "EntityFramework.Commands", + "gen": "Microsoft.Extensions.CodeGeneration" + }, + "frameworks": { + "dnx451": {} + }, + "exclude": [ + "wwwroot", + "node_modules", + "bower_components", + "contrib" + ], + "publishExclude": [ + "**.user", + "**.vspscc", + "contrib/**/*.*" + ], + "scripts": { + "prebuild": "echo before building", + "postbuild": "echo after building", + "prepack": "gulp min", + "postpack": "echo after packing", + "prerestore": "echo before restoring packages", + "postrestore": "echo after restoring packages", + "prepublish": "gulp min", + "postpublish": "./postPublish.sh" + } +} diff --git a/Yavsc/src/Controllers/OAuthController.cs b/Yavsc/src/Controllers/OAuthController.cs index 0de0fdae..df115106 100644 --- a/Yavsc/src/Controllers/OAuthController.cs +++ b/Yavsc/src/Controllers/OAuthController.cs @@ -182,9 +182,7 @@ namespace Yavsc.Controllers if (!User.Identities.Any(identity => identity.IsAuthenticated)) { return new ChallengeResult(new AuthenticationProperties { - RedirectUri = Url.Action(nameof(Authorize), new { - unique_id = request.GetUniqueIdentifier() - })}); + RedirectUri = Url.Action(nameof(Authorize), request.BuildRedirectUrl())}); } // Note: ASOS automatically ensures that an application corresponds to the client_id specified // in the authorization request by calling IOpenIdConnectServerProvider.ValidateAuthorizationRequest. @@ -204,18 +202,13 @@ namespace Yavsc.Controllers } // Note: in a real world application, you'd probably prefer creating a specific view model. - return View("Authorize", new AuthorisationView { Message = request, Application = application}); + return View("Authorize", new AuthorisationView { Message = request, + Application = application}); } - [HttpPost("~/connect/authorize/accept"), ValidateAntiForgeryToken] + [HttpPost("~/connect/authorize/accept"),Authorize] public async Task Accept(CancellationToken cancellationToken) { - var response = HttpContext.GetOpenIdConnectResponse(); - if (response != null) - { - return View("OidcError", response); - } - var request = HttpContext.GetOpenIdConnectRequest(); if (request == null) { @@ -230,18 +223,24 @@ namespace Yavsc.Controllers // will be used to create an id_token, a token or a code. var identity = new ClaimsIdentity(OpenIdConnectServerDefaults.AuthenticationScheme); + identity.AddClaim(new Claim(ClaimTypes.NameIdentifier,"name",User.GetUserId())); + if (User.IsInRole(Constants.AdminGroupName)) + identity.AddClaim(new Claim(ClaimTypes.Actor,"role",Constants.AdminGroupName)); + // Copy the claims retrieved from the external identity provider // (e.g Google, Facebook, a WS-Fed provider or another OIDC server). - foreach (var claim in HttpContext.User.Claims) + foreach (var claim in User.Claims) { // Allow ClaimTypes.Name to be added in the id_token. // ClaimTypes.NameIdentifier is automatically added, even if its // destination is not defined or doesn't include "id_token". // The other claims won't be visible for the client application. - if (claim.Type == ClaimTypes.Name) { + if (claim.Type == ClaimTypes.Role + || claim.Type == ClaimTypes.Email + || claim.Type == ClaimTypes.NameIdentifier ) { + claim.WithDestination( "code" ); claim.WithDestination( "id_token" ); - claim.WithDestination( "access_token" ); } identity.AddClaim(claim); @@ -250,14 +249,12 @@ namespace Yavsc.Controllers var application = await GetApplicationAsync(request.ClientId, cancellationToken); if (application == null) { - _logger.LogError($"OidcError: {request.ClientId} {response.ClientId} "); return View("OidcError", new OpenIdConnectMessage { Error = OpenIdConnectConstants.Errors.InvalidClient, ErrorDescription = "Details concerning the calling client application cannot be found in the database" }); } - // Create a new ClaimsIdentity containing the claims associated with the application. // Note: setting identity.Actor is not mandatory but can be useful to access // the whole delegation chain from the resource server (see ResourceController.cs). @@ -281,6 +278,7 @@ namespace Yavsc.Controllers properties.SetResources(new[] { _siteSettings.Audience }); + // This call will instruct AspNet.Security.OpenIdConnect.Server to serialize // the specified identity to build appropriate tokens (id_token and token). @@ -289,7 +287,7 @@ namespace Yavsc.Controllers // identities always contain the name identifier returned by the external provider. // Note: the authenticationScheme parameter must match the value configured in Startup.cs. await HttpContext.Authentication.SignInAsync( - OpenIdConnectServerDefaults.AuthenticationScheme, + "oidc-server", new ClaimsPrincipal(identity), properties); return new EmptyResult(); @@ -298,12 +296,6 @@ namespace Yavsc.Controllers [HttpPost("~/connect/authorize/deny"), ValidateAntiForgeryToken] public IActionResult Deny(CancellationToken cancellationToken) { - var response = HttpContext.GetOpenIdConnectResponse(); - if (response != null) - { - return View("OidcError", response); - } - var request = HttpContext.GetOpenIdConnectRequest(); if (request == null) { @@ -326,7 +318,6 @@ namespace Yavsc.Controllers State = request.State }); - return new EmptyResult(); } @@ -383,10 +374,6 @@ namespace Yavsc.Controllers where application.ApplicationID == identifier select application).SingleOrDefaultAsync(cancellationToken); } - private async Task GetCurrentUserAsync() - { - return await _userManager.FindByIdAsync(HttpContext.User.GetUserId()); - } private IActionResult RedirectToLocal(string returnUrl) { diff --git a/Yavsc/src/Controllers/TokenController.cs b/Yavsc/src/Controllers/TokenController.cs index 7793c338..1685ac6e 100644 --- a/Yavsc/src/Controllers/TokenController.cs +++ b/Yavsc/src/Controllers/TokenController.cs @@ -24,11 +24,9 @@ namespace Yavsc.Controllers UserManager manager; SignInManager 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; } + public string grant_type { get; set; } } UserTokenProvider tokenProvider; @@ -51,7 +49,7 @@ namespace Yavsc.Controllers /// the user is authenticated, which will reset the expiry. /// /// - [HttpGet,Authorize] + [HttpGet,HttpPost,Authorize] [Route("~/api/token/get")] public async Task Get() { @@ -72,12 +70,12 @@ namespace Yavsc.Controllers 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 }; + token = await GetToken("id_token", user, tokenExpires); + return new TokenResponse { access_token = token, expires_in = 3400, grant_type="id_token" }; } } - return new { authenticated = false }; + return new { authenticated = false, grant_type="id_token" }; } public class AuthRequest @@ -102,20 +100,19 @@ namespace Yavsc.Controllers 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 }); + var token = await GetToken("id_token",User.GetUserId(), expires); + return Ok(new TokenResponse {access_token = token, expires_in = 3400, grant_type="id_token" }); } - return Ok(new TokenResponse { authenticated = false }); + return new BadRequestObjectResult(new { authenticated = false } ) ; } - private async Task GetToken(string userid, DateTime? expires) + private async Task 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("id_token",manager,identuser); - + return await tokenProvider.GenerateAsync(purpose,manager,identuser); } } } diff --git a/Yavsc/src/Startup.cs b/Yavsc/src/Startup.cs index ff597b09..a37d994d 100755 --- a/Yavsc/src/Startup.cs +++ b/Yavsc/src/Startup.cs @@ -432,7 +432,7 @@ namespace Yavsc options.TokenEndpointPath = new PathString("/connect/authorize/accept"); options.UseSlidingExpiration = true; options.AllowInsecureHttp = true; - options.AuthenticationScheme = "ServerCookie"; // was = OpenIdConnectDefaults.AuthenticationScheme || "oidc"; + options.AuthenticationScheme = "oidc-server"; // was = OpenIdConnectDefaults.AuthenticationScheme || "oidc"; options.LogoutEndpointPath = new PathString("/connect/logout"); // options.ValidationEndpointPath = new PathString("/connect/introspect"); diff --git a/Yavsc/src/ViewModels/Auth/AuthorisationView.cs b/Yavsc/src/ViewModels/Auth/AuthorisationView.cs index 5c6e9482..3259423b 100644 --- a/Yavsc/src/ViewModels/Auth/AuthorisationView.cs +++ b/Yavsc/src/ViewModels/Auth/AuthorisationView.cs @@ -7,6 +7,5 @@ namespace Yavsc public class AuthorisationView {  public OpenIdConnectMessage Message { get; set; } public Application Application { get; set; } - } } \ No newline at end of file diff --git a/testOauthClient/Startup.cs b/testOauthClient/Startup.cs index 714a72e5..dc1ac311 100755 --- a/testOauthClient/Startup.cs +++ b/testOauthClient/Startup.cs @@ -94,7 +94,9 @@ namespace testOauthClient // 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"); + options.Scope.Clear(); + options.Scope.Add("openid"); + // .Add("api-resource-controller"); });