Skip to content
5 changes: 5 additions & 0 deletions Auth/LearningHub.Nhs.Auth/Configuration/WebSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,5 +56,10 @@ public class WebSettings
/// Gets or sets the SupportFeedbackForm.
/// </summary>
public string SupportFeedbackForm { get; set; }

/// <summary>
/// Gets or sets a value indicating whether IsPasswordUpdate.
/// </summary>
public bool IsPasswordUpdate { get; set; }
}
}
23 changes: 15 additions & 8 deletions Auth/LearningHub.Nhs.Auth/Controllers/AccountController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,11 @@
using LearningHub.Nhs.Auth.Models.Account;
using LearningHub.Nhs.Caching;
using LearningHub.Nhs.Models.Common;
using LearningHub.Nhs.Models.Entities.Reporting;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using NHSUKViewComponents.Web.ViewModels;

/// <summary>
/// Account Controller operations.
Expand Down Expand Up @@ -72,7 +68,7 @@ public AccountController(
this.authConfig = authConfig?.Value;
this.webSettings = webSettings;
this.logger = logger;
}
}

/// <summary>
/// Shows the Login page.
Expand Down Expand Up @@ -214,9 +210,9 @@ await this.UserService.AddLogonToUserHistory(
this.ModelState.AddModelError(string.Empty, loginResult.ErrorMessage);
}

showFormWithError:
showFormWithError:

// something went wrong, show form with error
// something went wrong, show form with error
var vm = await this.BuildLoginViewModelAsync(model);
if ((vm.ClientId == "learninghubwebclient") || (vm.ClientId == "learninghubadmin"))
{
Expand Down Expand Up @@ -268,6 +264,9 @@ public async Task<IActionResult> Logout(LogoutInputModel model)
// delete local authentication cookie
await this.HttpContext.SignOutAsync();

// Delete the authentication cookie to ensure it is invalidated
this.HttpContext.Response.Cookies.Delete(".AspNetCore.Identity.Application");

// raise the logout event
await this.Events.RaiseAsync(new UserLogoutSuccessEvent(this.User.GetSubjectId(), this.User.GetDisplayName()));

Expand Down Expand Up @@ -296,7 +295,15 @@ public async Task<IActionResult> Logout(LogoutInputModel model)
return this.SignOut(new AuthenticationProperties { RedirectUri = url }, vm.ExternalAuthenticationScheme);
}

return this.View("LoggedOut", vm);
if (this.webSettings.IsPasswordUpdate)
{
var redirectUri = $"{this.webSettings.LearningHubWebClient}Home/ChangePasswordAcknowledgement";
return this.Redirect(redirectUri);
}
else
{
return this.View("LoggedOut", vm);
}
}

/// <summary>
Expand Down
21 changes: 21 additions & 0 deletions Auth/LearningHub.Nhs.Auth/Controllers/HomeController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,27 @@ public async Task<IActionResult> Error()
return this.View("Error");
}

/// <summary>
/// IsPasswordUpdateMethod.
/// </summary>
/// <param name="isLogout">The Logout.</param>
/// <returns>The <see cref="ActionResult"/>.</returns>
[HttpGet]
public IActionResult SetIsPasswordUpdate(bool isLogout)
{
if (isLogout)
{
this.webSettings.IsPasswordUpdate = false;
}
else
{
this.webSettings.IsPasswordUpdate = true;
}

var redirectUri = $"{this.webSettings.LearningHubWebClient}Home/UserLogout";
return this.Redirect(redirectUri);
}

/// <summary>
/// Shows the HealthCheck response.
/// </summary>
Expand Down
104 changes: 104 additions & 0 deletions Auth/LearningHub.Nhs.Auth/Helpers/InMemoryTicketStore.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
namespace LearningHub.Nhs.Auth.Helpers
{
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;

/// <summary>
/// Defines the <see cref="InMemoryTicketStore" />.
/// </summary>
public class InMemoryTicketStore : ITicketStore
{
private readonly ConcurrentDictionary<string, AuthenticationTicket> cache;

/// <summary>
/// Initializes a new instance of the <see cref="InMemoryTicketStore"/> class.
/// The InMemoryTicketStore.
/// </summary>
/// <param name="cache">the cache.</param>
public InMemoryTicketStore(ConcurrentDictionary<string, AuthenticationTicket> cache)
{
this.cache = cache;
}

/// <summary>
/// The StoreAsync.
/// </summary>
/// <param name="ticket">The ticket.</param>
/// <returns>The key.</returns>
public async Task<string> StoreAsync(AuthenticationTicket ticket)
{
var ticketUserId = ticket.Principal.Claims.Where(c => c.Type == "sub")
.FirstOrDefault()
.Value;
var matchingAuthTicket = this.cache.Values.FirstOrDefault(
t => t.Principal.Claims.FirstOrDefault(
c => c.Type == "sub"
&& c.Value == ticketUserId) != null);
if (matchingAuthTicket != null)
{
var cacheKey = this.cache.Where(
entry => entry.Value == matchingAuthTicket)
.Select(entry => entry.Key)
.FirstOrDefault();
this.cache.TryRemove(
cacheKey,
out _);
}

var key = Guid
.NewGuid()
.ToString();
await this.RenewAsync(
key,
ticket);
return key;
}

/// <summary>
/// The RenewAsync.
/// </summary>
/// <param name="key">The key.</param>
/// <param name="ticket">The ticket.</param>
/// <returns>The Task.</returns>
public Task RenewAsync(
string key,
AuthenticationTicket ticket)
{
this.cache.AddOrUpdate(
key,
ticket,
(_, _) => ticket);
return Task.CompletedTask;
}

/// <summary>
/// The RetrieveAsync.
/// </summary>
/// <param name="key">The Key.</param>
/// <returns>The Task.</returns>
public Task<AuthenticationTicket> RetrieveAsync(string key)
{
this.cache.TryGetValue(
key,
out var ticket);
return Task.FromResult(ticket);
}

/// <summary>
/// The RemoveAsync.
/// </summary>
/// <param name="key">The key.</param>
/// <returns>The Task.</returns>
public Task RemoveAsync(string key)
{
this.cache.TryRemove(
key,
out _);
return Task.CompletedTask;
}
}
}
10 changes: 9 additions & 1 deletion Auth/LearningHub.Nhs.Auth/ServiceCollectionExtension.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
namespace LearningHub.Nhs.Auth
{
using System;
using System.Collections.Concurrent;
using System.Security.Cryptography.X509Certificates;
using Azure.Identity;
using IdentityServer4;
using LearningHub.Nhs.Auth.Configuration;
using LearningHub.Nhs.Auth.Helpers;
using LearningHub.Nhs.Auth.Middleware;
using LearningHub.Nhs.Caching;
using LearningHub.Nhs.Models.Enums;
Expand Down Expand Up @@ -70,7 +72,13 @@ public static void ConfigureServices(this IServiceCollection services, IConfigur
{
options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
}).AddCookie().AddOpenIdConnect(
})
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
{
options.AccessDeniedPath = "/Home/AccessDenied";
options.SessionStore = new InMemoryTicketStore(new ConcurrentDictionary<string, AuthenticationTicket>());
})
.AddOpenIdConnect(
"oidc_oa",
options =>
{
Expand Down
5 changes: 2 additions & 3 deletions Auth/LearningHub.Nhs.Auth/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,8 @@
"ElfhHub": "",
"Rcr": "",
"SupportForm": "https://support.learninghub.nhs.uk/support/tickets/new",
"SupportFeedbackForm": "https://forms.office.com/e/C8tteweEhG"


"SupportFeedbackForm": "https://forms.office.com/e/C8tteweEhG",
"IsPasswordUpdate": "false"
},
"AllowOpenAthensDebug": false,
"OaLhClients": {
Expand Down
12 changes: 12 additions & 0 deletions LearningHub.Nhs.UserApi/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

var logger = NLogBuilder.ConfigureNLog("nlog.config").GetCurrentClassLogger();

var csp = "object-src 'none'; frame-ancestors 'none'; sandbox allow-forms allow-same-origin allow-scripts allow-popups; base-uri 'self';";
try
{
logger.Debug("Log Started");
Expand All @@ -36,6 +37,17 @@
c.SwaggerEndpoint($"/swagger/{app.Configuration["Swagger:Title"]}/swagger.json", app.Configuration["Swagger:Version"]);
});

app.Use(async (context, next) =>
{
context.Response.Headers.Add("content-security-policy", csp);
context.Response.Headers.Add("Referrer-Policy", "no-referrer");
context.Response.Headers.Add("Strict-Transport-Security", "max-age=31536000; includeSubDomains");
context.Response.Headers.Add("X-Content-Type-Options", "nosniff");
context.Response.Headers.Add("X-Frame-Options", "SAMEORIGIN");
context.Response.Headers.Add("X-XSS-protection", "0");
await next();
});

app.UseMiddleware<ExceptionMiddleware>();

app.UseEndpoints(endpoints => endpoints.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}"));
Expand Down
Loading