From d7b5f8b7775a7c623d4bcfa7015476f835aabfa2 Mon Sep 17 00:00:00 2001 From: ivarlovlie Date: Thu, 6 Aug 2020 23:28:30 +0200 Subject: server: start of ids4 impl --- src/server/Controllers/AccountController.cs | 63 +++++++++++------------------ src/server/Dough.csproj | 6 +++ src/server/IdentityServer/Config.cs | 48 ++++++++++++++++++++++ src/server/Models/Constants.cs | 21 +++++++++- src/server/Models/Database/User.cs | 1 + src/server/Services/EmailService.cs | 7 ++++ src/server/Startup.cs | 45 +++++++++++---------- 7 files changed, 129 insertions(+), 62 deletions(-) create mode 100644 src/server/IdentityServer/Config.cs create mode 100644 src/server/Services/EmailService.cs (limited to 'src/server') diff --git a/src/server/Controllers/AccountController.cs b/src/server/Controllers/AccountController.cs index af1e9ac..fe7b7a2 100644 --- a/src/server/Controllers/AccountController.cs +++ b/src/server/Controllers/AccountController.cs @@ -1,69 +1,54 @@ using System; -using System.Collections.Generic; -using System.Security.Claims; using System.Threading.Tasks; -using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Dough.Models; using Dough.Models.Database; -using Dough.Models.Payloads; -using Dough.Models.Results; using Dough.Utilities; +using IdentityServer4.Services; namespace Dough.Controllers { + [AllowAnonymous] public class AccountController : BaseController { private readonly MainDbContext _context; + private readonly IIdentityServerInteractionService _identityServerInteractionService; - public AccountController(MainDbContext context) + public AccountController(MainDbContext context, + IIdentityServerInteractionService identityServerInteractionService) { _context = context; + _identityServerInteractionService = identityServerInteractionService; } + + // This is the default route for identityserver4 logins (https://identityserver4.readthedocs.io/en/latest/topics/signin.html#login-workflow) [HttpPost("login")] - public async Task Login(LoginPayload payload) + public async Task Login(string returnUrl) { - var user = _context.Users.SingleByNameOrDefault(payload.Username); - if (user == default) - return BadRequest(new ErrorResult("Ugyldig brukernavn eller passord", - "Verifiser at passord og brukernavn er riktig og prøv igjen")); - - if (!user.VerifyPassword(payload.Password)) - return BadRequest(new ErrorResult("Ugyldig brukernavn eller passord", - "Verifiser at passord og brukernavn er riktig")); - - var claims = new List - { - new Claim(ClaimTypes.Name, user.Username), - new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()), - new Claim(ClaimTypes.AuthenticationInstant, DateTime.UtcNow.ToString("O")) - }; + if (returnUrl.IsMissing() || !_identityServerInteractionService.IsValidReturnUrl(returnUrl)) + return BadRequest("route parameter returnUrl is invalid"); - var claimsIdentity = new ClaimsIdentity(claims, Constants.AuthenticationScheme); - var claimsPrincipal = new ClaimsPrincipal(claimsIdentity); - var authenticationProperties = new AuthenticationProperties + Console.WriteLine("returnUrl: " + returnUrl); + var reqBody = await HttpContext.Request.ReadFormAsync(); + foreach (var formEl in reqBody) { - IsPersistent = false, - IssuedUtc = DateTime.UtcNow, - AllowRefresh = true, - ExpiresUtc = DateTime.UtcNow.AddDays(7), - }; - - await HttpContext.SignInAsync(Constants.AuthenticationScheme, - claimsPrincipal, - authenticationProperties); + Console.WriteLine(formEl.Key); + foreach (var value in formEl.Value) + Console.WriteLine(" - " + value); + } return Ok(); } - [HttpGet("logout")] - public async Task Logout(string continueTo = default) + + [HttpGet("forgot")] + public async Task ForgotPassword(string username) { - await HttpContext.SignOutAsync(Constants.AuthenticationScheme); - if (continueTo.IsPresent() && continueTo.IsValidUrl()) return Redirect(continueTo); + var user = _context.Users.SingleByNameOrDefault(username); + if (user == default) return Ok(); return Ok(); } @@ -74,4 +59,4 @@ namespace Dough.Controllers return Ok(LoggedInUser); } } -} +} \ No newline at end of file diff --git a/src/server/Dough.csproj b/src/server/Dough.csproj index 65911ed..e54ff49 100644 --- a/src/server/Dough.csproj +++ b/src/server/Dough.csproj @@ -6,9 +6,15 @@ + + + + + + \ No newline at end of file diff --git a/src/server/IdentityServer/Config.cs b/src/server/IdentityServer/Config.cs new file mode 100644 index 0000000..41363f1 --- /dev/null +++ b/src/server/IdentityServer/Config.cs @@ -0,0 +1,48 @@ +using System.Collections.Generic; +using Dough.Models; +using IdentityServer4; +using IdentityServer4.Models; + +namespace Dough.IdentityServer +{ + public static class Config + { + private const string MainApiScopeName = "main_api"; + private const string BrowserClientId = "browser"; + + + public static IEnumerable Clients => new List + { + new Client + { + ClientId = BrowserClientId, + AllowedGrantTypes = GrantTypes.Code, + RequireClientSecret = false, + + RedirectUris = Constants.BrowserAppLoginRedirectUrls, + PostLogoutRedirectUris = Constants.BrowserAppLogoutRedirectUrls, + AllowedCorsOrigins = Constants.BrowserAppUrls, + + AllowedScopes = + { + IdentityServerConstants.StandardScopes.OpenId, + IdentityServerConstants.StandardScopes.Profile, + MainApiScopeName + } + } + }; + + public static IEnumerable ApiScopes => + new List + { + new ApiScope(MainApiScopeName) + }; + + public static IEnumerable IdentityResources => + new List + { + new IdentityResources.OpenId(), + new IdentityResources.Profile(), + }; + } +} diff --git a/src/server/Models/Constants.cs b/src/server/Models/Constants.cs index 759030a..3afaaad 100644 --- a/src/server/Models/Constants.cs +++ b/src/server/Models/Constants.cs @@ -1,7 +1,24 @@ +using System.Collections.Generic; + namespace Dough.Models { - public class Constants + public static class Constants { - public const string AuthenticationScheme = "Cookies"; + public static readonly string[] BrowserAppUrls = + { + "http://localhost:8080", + "http://localhost:3000", + }; + public static readonly string[] BrowserAppLoginRedirectUrls = + { + "http://localhost:8080/signin-oidc", + "http://localhost:3000/signin-oidc", + }; + public static readonly string[] BrowserAppLogoutRedirectUrls = + { + "http://localhost:8080/signout-callback-oidc", + "http://localhost:3000/signout-callback-oidc", + }; + } } \ No newline at end of file diff --git a/src/server/Models/Database/User.cs b/src/server/Models/Database/User.cs index 479c15c..7d8bf1c 100644 --- a/src/server/Models/Database/User.cs +++ b/src/server/Models/Database/User.cs @@ -1,4 +1,5 @@ using System; +using Microsoft.AspNetCore.Identity; namespace Dough.Models.Database { diff --git a/src/server/Services/EmailService.cs b/src/server/Services/EmailService.cs new file mode 100644 index 0000000..0d70f0f --- /dev/null +++ b/src/server/Services/EmailService.cs @@ -0,0 +1,7 @@ +namespace Dough.Services +{ + public class EmailService + { + + } +} \ No newline at end of file diff --git a/src/server/Startup.cs b/src/server/Startup.cs index 4f0d467..f55a761 100644 --- a/src/server/Startup.cs +++ b/src/server/Startup.cs @@ -1,4 +1,4 @@ -using Microsoft.AspNetCore.Authentication.Cookies; +using Dough.IdentityServer; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.EntityFrameworkCore; @@ -6,10 +6,9 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Dough.Models; -using Dough.Utilities; using Dough.Models.Database; -using Microsoft.AspNetCore.CookiePolicy; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; namespace Dough { @@ -21,7 +20,8 @@ namespace Dough } public IConfiguration Configuration { get; } - private const string MainCorsPolicy = "MainCorsPolicy"; + + private const string DefaultCorsPolicy = "DefaultCorsPolicy"; private string GetConnectionStringFromEnvironment() { @@ -38,33 +38,35 @@ namespace Dough services.AddCors(options => { - options.AddPolicy(MainCorsPolicy, builder => + options.AddPolicy(DefaultCorsPolicy, builder => { builder + .WithOrigins(Constants.BrowserAppUrls) .AllowAnyHeader() .AllowAnyMethod() - .AllowCredentials() - .WithOrigins("http://localhost:8080"); + .AllowCredentials(); }); }); + services.AddHealthChecks() + .AddDbContextCheck(); + services.AddDbContext(options => { options.UseMySql(GetConnectionStringFromEnvironment()); }); + + services.Configure(options => + { + options.SuppressModelStateInvalidFilter = true; + options.SuppressInferBindingSourcesForParameters = true; + }); + var builder = services.AddIdentityServer() + .AddInMemoryIdentityResources(Config.IdentityResources) + .AddInMemoryApiScopes(Config.ApiScopes) + .AddInMemoryClients(Config.Clients); + services.AddControllers(); - - services.AddAuthentication(Constants.AuthenticationScheme) - .AddCookie(Constants.AuthenticationScheme, options => - { - options.Cookie.Name = "dough_session"; - options.Cookie.HttpOnly = true; - options.Cookie.SameSite = SameSiteMode.Strict; - options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest; - options.LoginPath = "/api/account/login"; - options.SlidingExpiration = true; - options.LogoutPath = "/api/account/logout"; - }); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) @@ -72,12 +74,13 @@ namespace Dough if (env.IsDevelopment()) app.UseDeveloperExceptionPage(); - app.UseCors(MainCorsPolicy); app.UseRouting(); + app.UseCors(DefaultCorsPolicy); + app.UseHealthChecks("/health"); app.UseStatusCodePages(); app.UseAuthentication(); app.UseAuthorization(); - app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); + app.UseEndpoints(endpoints => { endpoints.MapControllers().RequireAuthorization(); }); } } } -- cgit v1.3