From cdb8acb841412b48f2201b437b6b216b8f467594 Mon Sep 17 00:00:00 2001 From: James Gunn Date: Tue, 23 Apr 2024 17:39:35 +0100 Subject: [PATCH] Support trn_token in sign in journeys (#1292) --- TeachingRecordSystem/Directory.Packages.props | 3 +- .../Controllers/OAuth2Controller.cs | 13 +- .../IdDbContext.cs | 30 ++ .../Security/FormFlowJourneySignInHandler.cs | 2 +- ...chToTeachingRecordAuthenticationHandler.cs | 20 +- .../OneLoginAuthenticationSchemeProvider.cs | 2 +- .../Pages/DebugIdentity.cshtml | 8 +- .../Pages/DebugIdentity.cshtml.cs | 58 ++- .../Pages/OidcTest/Start.cshtml | 2 + .../Pages/OidcTest/Start.cshtml.cs | 11 +- .../Pages/Test.cshtml.cs | 6 +- .../Program.cs | 2 + .../SignInJourneyHelper.cs | 206 +++++++---- .../SignInJourneyState.cs | 5 +- .../TestAppConfiguration.cs | 12 + .../HostFixture.cs | 2 + .../Security/FakeOneLoginHandler.cs | 2 +- .../PageExtensions.cs | 7 +- .../SignInTests.cs | 35 ++ ...ystem.AuthorizeAccess.EndToEndTests.csproj | 1 + .../TestBase.cs | 2 + .../HostFixture.cs | 2 + .../PageTests/CheckAnswersTests.cs | 18 +- .../PageTests/ConnectTests.cs | 12 +- .../PageTests/FoundTests.cs | 8 +- .../PageTests/NationalInsuranceNumberTests.cs | 22 +- .../PageTests/NotFoundTests.cs | 12 +- .../PageTests/NotVerifiedTests.cs | 6 +- .../PageTests/TrnTests.cs | 26 +- .../SignInJourneyHelperTests.cs | 332 ++++++++++++++++-- ...gRecordSystem.AuthorizeAccess.Tests.csproj | 1 + 31 files changed, 674 insertions(+), 194 deletions(-) create mode 100644 TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/IdDbContext.cs diff --git a/TeachingRecordSystem/Directory.Packages.props b/TeachingRecordSystem/Directory.Packages.props index 637abe92a..57d983d27 100644 --- a/TeachingRecordSystem/Directory.Packages.props +++ b/TeachingRecordSystem/Directory.Packages.props @@ -42,6 +42,7 @@ + @@ -91,4 +92,4 @@ - + \ No newline at end of file diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Controllers/OAuth2Controller.cs b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Controllers/OAuth2Controller.cs index 9e531f71e..38949600a 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Controllers/OAuth2Controller.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Controllers/OAuth2Controller.cs @@ -48,14 +48,19 @@ public async Task Authorize() var parameters = Request.HasFormContentType ? Request.Form.ToList() : Request.Query.ToList(); var serviceUrl = new Uri(request.RedirectUri!).GetLeftPart(UriPartial.Authority); + var trnToken = parameters.GroupBy(kvp => kvp.Key).FirstOrDefault(kvp => kvp.Key == "trn_token")?.Select(kvp => kvp.Value).FirstOrDefault(); var authenticationProperties = new AuthenticationProperties() { - RedirectUri = Request.PathBase + Request.Path + QueryString.Create(parameters) + RedirectUri = Request.PathBase + Request.Path + QueryString.Create(parameters), + Items = + { + { MatchToTeachingRecordAuthenticationHandler.AuthenticationPropertiesItemKeys.OneLoginAuthenticationScheme, client.OneLoginAuthenticationSchemeName }, + { MatchToTeachingRecordAuthenticationHandler.AuthenticationPropertiesItemKeys.ServiceName, client.Name }, + { MatchToTeachingRecordAuthenticationHandler.AuthenticationPropertiesItemKeys.ServiceUrl, serviceUrl }, + { MatchToTeachingRecordAuthenticationHandler.AuthenticationPropertiesItemKeys.TrnToken, trnToken }, + } }; - authenticationProperties.Items.Add(MatchToTeachingRecordAuthenticationHandler.AuthenticationPropertiesItemKeys.OneLoginAuthenticationScheme, client.OneLoginAuthenticationSchemeName); - authenticationProperties.Items.Add(MatchToTeachingRecordAuthenticationHandler.AuthenticationPropertiesItemKeys.ServiceName, client.Name); - authenticationProperties.Items.Add(MatchToTeachingRecordAuthenticationHandler.AuthenticationPropertiesItemKeys.ServiceUrl, serviceUrl); return Challenge(authenticationProperties, childAuthenticationScheme); } diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/IdDbContext.cs b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/IdDbContext.cs new file mode 100644 index 000000000..e02b97618 --- /dev/null +++ b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/IdDbContext.cs @@ -0,0 +1,30 @@ +using System.ComponentModel.DataAnnotations.Schema; + +namespace TeachingRecordSystem.AuthorizeAccess; + +public class IdDbContext(DbContextOptions options) : DbContext(options) +{ + public DbSet TrnTokens => Set(); + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity().HasKey(t => t.TrnToken); + } +} + +[Table("trn_tokens")] +public class IdTrnToken +{ + [Column("trn_token")] + public required string TrnToken { get; set; } + [Column("trn")] + public required string Trn { get; set; } + [Column("email")] + public required string Email { get; set; } + [Column("created_utc")] + public required DateTime CreatedUtc { get; set; } + [Column("expires_utc")] + public required DateTime ExpiresUtc { get; set; } + [Column("user_id")] + public Guid? UserId { get; set; } +} diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Infrastructure/Security/FormFlowJourneySignInHandler.cs b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Infrastructure/Security/FormFlowJourneySignInHandler.cs index 36b83d58c..0ccb2cdce 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Infrastructure/Security/FormFlowJourneySignInHandler.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Infrastructure/Security/FormFlowJourneySignInHandler.cs @@ -64,7 +64,7 @@ public async Task SignInAsync(ClaimsPrincipal user, AuthenticationProperties? pr var ticket = new AuthenticationTicket(user, properties, _scheme.Name); - var result = await helper.OnSignedInWithOneLogin(journeyInstance, ticket); + var result = await helper.OnOneLoginCallback(journeyInstance, ticket); // Override the redirect done by RemoteAuthenticationHandler _context.Response.OnStarting(() => result.ExecuteAsync(_context)); diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Infrastructure/Security/MatchToTeachingRecordAuthenticationHandler.cs b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Infrastructure/Security/MatchToTeachingRecordAuthenticationHandler.cs index e6c85e659..2abd27197 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Infrastructure/Security/MatchToTeachingRecordAuthenticationHandler.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Infrastructure/Security/MatchToTeachingRecordAuthenticationHandler.cs @@ -35,23 +35,28 @@ public async Task ChallengeAsync(AuthenticationProperties? properties) EnsureInitialized(); if (properties is null || - !properties.Items.TryGetValue(AuthenticationPropertiesItemKeys.OneLoginAuthenticationScheme, out var oneLoginAuthenticationScheme) || - oneLoginAuthenticationScheme is null || - !properties.Items.TryGetValue(AuthenticationPropertiesItemKeys.ServiceName, out var serviceName) || - serviceName is null || - !properties.Items.TryGetValue(AuthenticationPropertiesItemKeys.ServiceUrl, out var serviceUrl) || - serviceUrl is null) + !TryGetNonNullItem(AuthenticationPropertiesItemKeys.OneLoginAuthenticationScheme, out var oneLoginAuthenticationScheme) || + !TryGetNonNullItem(AuthenticationPropertiesItemKeys.ServiceName, out var serviceName) || + !TryGetNonNullItem(AuthenticationPropertiesItemKeys.ServiceUrl, out var serviceUrl)) { throw new InvalidOperationException($"{nameof(AuthenticationProperties)} is missing one or more items."); } + properties.Items.TryGetValue(AuthenticationPropertiesItemKeys.TrnToken, out var trnToken); + var journeyInstance = await helper.UserInstanceStateProvider.GetOrCreateSignInJourneyInstanceAsync( _context, - createState: () => new SignInJourneyState(properties.RedirectUri ?? "/", serviceName, serviceUrl, oneLoginAuthenticationScheme), + createState: () => new SignInJourneyState(properties.RedirectUri ?? "/", serviceName, serviceUrl, oneLoginAuthenticationScheme, trnToken), updateState: state => state.Reset()); var result = helper.SignInWithOneLogin(journeyInstance); await result.ExecuteAsync(_context); + + bool TryGetNonNullItem(string key, [NotNullWhen(true)] out string? value) + { + value = default; + return properties?.Items.TryGetValue(key, out value) == true && value is not null; + } } public Task ForbidAsync(AuthenticationProperties? properties) @@ -80,5 +85,6 @@ public static class AuthenticationPropertiesItemKeys public const string OneLoginAuthenticationScheme = "OneLoginAuthenticationScheme"; public const string ServiceName = "ServiceName"; public const string ServiceUrl = "ServiceUrl"; + public const string TrnToken = "TrnToken"; } } diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Infrastructure/Security/OneLoginAuthenticationSchemeProvider.cs b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Infrastructure/Security/OneLoginAuthenticationSchemeProvider.cs index 77a28fa7f..e152d7d6e 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Infrastructure/Security/OneLoginAuthenticationSchemeProvider.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Infrastructure/Security/OneLoginAuthenticationSchemeProvider.cs @@ -187,7 +187,7 @@ void IConfigureNamedOptions.Configure(string? name, OneLoginOpt var signInJourneyHelper = context.HttpContext.RequestServices.GetRequiredService(); var journeyInstance = (await signInJourneyHelper.UserInstanceStateProvider.GetSignInJourneyInstanceAsync(context.HttpContext, journeyInstanceId))!; - var result = await signInJourneyHelper.OnUserVerificationWithOneLoginFailed(journeyInstance); + var result = await signInJourneyHelper.OnVerificationFailed(journeyInstance); await result.ExecuteAsync(context.HttpContext); } }; diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/DebugIdentity.cshtml b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/DebugIdentity.cshtml index ed0f4dde7..3a833bff6 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/DebugIdentity.cshtml +++ b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/DebugIdentity.cshtml @@ -1,7 +1,7 @@ @page "/debug" @model TeachingRecordSystem.AuthorizeAccess.Pages.DebugIdentityModel @{ - ViewBag.Title = "Identity information"; + ViewBag.Title = "Debug One Login journey"; }
@@ -9,6 +9,10 @@

@ViewBag.Title

+ + +

User information

+ @@ -43,7 +47,7 @@ @Model.Person.DateOfBirth?.ToString("dd/MM/yyyy") - National insurance number + National Insurance number @Model.Person.NationalInsuranceNumber diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/DebugIdentity.cshtml.cs b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/DebugIdentity.cshtml.cs index 71e7ca6f6..1e458cd1e 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/DebugIdentity.cshtml.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/DebugIdentity.cshtml.cs @@ -17,13 +17,15 @@ namespace TeachingRecordSystem.AuthorizeAccess.Pages; public class DebugIdentityModel( TrsDbContext dbContext, SignInJourneyHelper helper, - IClock clock, IOptions optionsAccessor) : PageModel { private OneLoginUser? _oneLoginUser; public JourneyInstance? JourneyInstance { get; set; } + [Display(Name = "TRN token")] + public string? TrnToken { get; set; } + [Display(Name = "Subject")] public string? Subject { get; set; } @@ -106,49 +108,36 @@ public async Task OnPost() return this.PageWithErrors(); } - if (DetachPerson && _oneLoginUser!.PersonId is not null) + if (_oneLoginUser!.PersonId is not null && !DetachPerson) + { + await JourneyInstance!.UpdateStateAsync(state => helper.Complete(state, _oneLoginUser.Person!.Trn!)); + return GetNextPage(); + } + + if (_oneLoginUser!.PersonId is not null && DetachPerson) { _oneLoginUser.PersonId = null; } - if (_oneLoginUser!.PersonId is null) + if (IdentityVerified) { - if (IdentityVerified) - { - _oneLoginUser!.VerifiedOn = clock.UtcNow; - _oneLoginUser.VerificationRoute = OneLoginUserVerificationRoute.OneLogin; - _oneLoginUser.VerifiedNames = verifiedNames; - _oneLoginUser.VerifiedDatesOfBirth = verifiedDatesOfBirth; - } - else - { - _oneLoginUser!.VerifiedOn = null; - _oneLoginUser.VerificationRoute = null; - _oneLoginUser.VerifiedNames = null; - _oneLoginUser.VerifiedDatesOfBirth = null; - } + await helper.OnUserVerifiedCore(JourneyInstance!, verifiedNames!, verifiedDatesOfBirth!, coreIdentityClaimVc: null); + } + else + { + _oneLoginUser!.VerifiedOn = null; + _oneLoginUser.VerificationRoute = null; + _oneLoginUser.VerifiedNames = null; + _oneLoginUser.VerifiedDatesOfBirth = null; + + await JourneyInstance!.UpdateStateAsync(state => state.ClearVerified()); } await dbContext.SaveChangesAsync(); - await JourneyInstance!.UpdateStateAsync(state => - { - if (_oneLoginUser!.PersonId is not null) - { - helper.Complete(state, _oneLoginUser.Person!.Trn!); - } - else if (IdentityVerified) - { - state.SetVerified(verifiedNames!, verifiedDatesOfBirth!); - } - else - { - state.ClearVerified(); - } - }); + return GetNextPage(); - var nextPage = helper.GetNextPage(JourneyInstance); - return nextPage.ToActionResult(); + IActionResult GetNextPage() => helper.GetNextPage(JourneyInstance!).ToActionResult(); } public override async Task OnPageHandlerExecutionAsync(PageHandlerExecutingContext context, PageHandlerExecutionDelegate next) @@ -165,6 +154,7 @@ public override async Task OnPageHandlerExecutionAsync(PageHandlerExecutingConte return; } + TrnToken = JourneyInstance.State.TrnToken; Subject = User.FindFirstValue("sub"); Email = User.FindFirstValue("email"); diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/OidcTest/Start.cshtml b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/OidcTest/Start.cshtml index 186a1fd44..c5da8d9e9 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/OidcTest/Start.cshtml +++ b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/OidcTest/Start.cshtml @@ -4,5 +4,7 @@ } + + Start diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/OidcTest/Start.cshtml.cs b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/OidcTest/Start.cshtml.cs index d7541707f..fac584700 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/OidcTest/Start.cshtml.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/OidcTest/Start.cshtml.cs @@ -1,3 +1,4 @@ +using System.ComponentModel.DataAnnotations; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; @@ -6,6 +7,10 @@ namespace TeachingRecordSystem.AuthorizeAccess.Pages.OidcTest; public class StartModel : PageModel { + [BindProperty] + [Display(Name = "TRN token")] + public string? TrnToken { get; set; } + public void OnGet() { } @@ -13,7 +18,11 @@ public void OnGet() public IActionResult OnPost() => Challenge( new AuthenticationProperties() { - RedirectUri = Url.Page("SignedIn") + RedirectUri = Url.Page("SignedIn"), + Parameters = + { + { "TrnToken", TrnToken } + } }, TestAppConfiguration.AuthenticationSchemeName); } diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/Test.cshtml.cs b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/Test.cshtml.cs index 7f02bf5ca..ec56b3258 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/Test.cshtml.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/Test.cshtml.cs @@ -13,6 +13,9 @@ public class TestModel : PageModel [FromQuery(Name = "scheme")] public string? AuthenticationScheme { get; set; } + [FromQuery(Name = "trn_token")] + public string? TrnToken { get; set; } + public IActionResult OnGet() { if (User.Identity?.IsAuthenticated != true) @@ -24,7 +27,8 @@ public IActionResult OnGet() { { MatchToTeachingRecordAuthenticationHandler.AuthenticationPropertiesItemKeys.OneLoginAuthenticationScheme, AuthenticationScheme }, { MatchToTeachingRecordAuthenticationHandler.AuthenticationPropertiesItemKeys.ServiceName, "Test service" }, - { MatchToTeachingRecordAuthenticationHandler.AuthenticationPropertiesItemKeys.ServiceUrl, Request.GetEncodedUrl() } + { MatchToTeachingRecordAuthenticationHandler.AuthenticationPropertiesItemKeys.ServiceUrl, Request.GetEncodedUrl() }, + { MatchToTeachingRecordAuthenticationHandler.AuthenticationPropertiesItemKeys.TrnToken, TrnToken }, }, RedirectUri = Request.GetEncodedUrl() }, diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Program.cs b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Program.cs index 9eaaafb96..9d3ec98cb 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Program.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Program.cs @@ -145,6 +145,8 @@ static SecurityKey LoadKey(string configurationValue) RetryPauseTime = TimeSpan.FromSeconds(1) }; builder.Services.AddDefaultServiceClient(ServiceLifetime.Transient, _ => crmServiceClient.Clone()); + + builder.Services.AddDbContext(options => options.UseNpgsql(builder.Configuration.GetRequiredConnectionString("Id"))); } builder.Services diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/SignInJourneyHelper.cs b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/SignInJourneyHelper.cs index 020ee7e9d..e1b296bd6 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/SignInJourneyHelper.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/SignInJourneyHelper.cs @@ -7,6 +7,7 @@ using Microsoft.IdentityModel.Protocols.OpenIdConnect; using TeachingRecordSystem.AuthorizeAccess.Infrastructure.Security; using TeachingRecordSystem.Core.DataStore.Postgres; +using TeachingRecordSystem.Core.Dqt.Models; using TeachingRecordSystem.Core.Services.PersonSearch; using TeachingRecordSystem.FormFlow; using TeachingRecordSystem.FormFlow.State; @@ -16,6 +17,7 @@ namespace TeachingRecordSystem.AuthorizeAccess; public class SignInJourneyHelper( TrsDbContext dbContext, IPersonSearchService personSearchService, + IdDbContext idDbContext, AuthorizeAccessLinkGenerator linkGenerator, IOptions optionsAccessor, IUserInstanceStateProvider userInstanceStateProvider, @@ -24,6 +26,10 @@ public class SignInJourneyHelper( public const string AuthenticationOnlyVtr = @"[""Cl.Cm""]"; public const string AuthenticationAndIdentityVerificationVtr = @"[""Cl.Cm.P2""]"; + // ID's database has a user_id column to indicate that a TRN token has been used already. + // This sentinel value indicates the token has been used by us, rather than a teacher ID user. + private static readonly Guid _teacherAuthIdUserIdSentinel = Guid.Empty; + public AuthorizeAccessLinkGenerator LinkGenerator { get; } = linkGenerator; public IUserInstanceStateProvider UserInstanceStateProvider { get; } = userInstanceStateProvider; @@ -36,25 +42,22 @@ public IResult SignInWithOneLogin(JourneyInstance journeyIns public IResult VerifyIdentityWithOneLogin(JourneyInstance journeyInstance) => OneLoginChallenge(journeyInstance, AuthenticationAndIdentityVerificationVtr); - public async Task OnSignedInWithOneLogin(JourneyInstance journeyInstance, AuthenticationTicket ticket) + public async Task OnOneLoginCallback(JourneyInstance journeyInstance, AuthenticationTicket ticket) { if (!ticket.Properties.TryGetVectorOfTrust(out var vtr)) { throw new InvalidOperationException("No vtr."); } - var sub = ticket.Principal.FindFirstValue("sub") ?? throw new InvalidOperationException("No sub claim."); - var email = ticket.Principal.FindFirstValue("email") ?? throw new InvalidOperationException("No email claim."); - if (vtr == AuthenticationOnlyVtr) { - await OnUserAuthenticated(); + await OnUserAuthenticated(journeyInstance, ticket); } else { Debug.Assert(vtr == AuthenticationAndIdentityVerificationVtr); Debug.Assert(journeyInstance.State.OneLoginAuthenticationTicket is not null); - await OnUserVerified(); + await OnUserVerified(journeyInstance, ticket); } if (ShowDebugPages) @@ -63,84 +66,157 @@ public async Task OnSignedInWithOneLogin(JourneyInstance journeyInstance, AuthenticationTicket ticket) + { + var sub = ticket.Principal.FindFirstValue("sub") ?? throw new InvalidOperationException("No sub claim."); + var email = ticket.Principal.FindFirstValue("email") ?? throw new InvalidOperationException("No email claim."); + + var oneLoginUser = await dbContext.OneLoginUsers + .Include(u => u.Person) + .SingleOrDefaultAsync(u => u.Subject == sub); + + if (oneLoginUser is null) { - var oneLoginUser = await dbContext.OneLoginUsers - .Include(u => u.Person) - .SingleOrDefaultAsync(u => u.Subject == sub); + oneLoginUser = new() + { + Subject = sub, + Email = email, + FirstOneLoginSignIn = clock.UtcNow, + LastOneLoginSignIn = clock.UtcNow + }; + dbContext.OneLoginUsers.Add(oneLoginUser); + } + else + { + oneLoginUser.LastOneLoginSignIn = clock.UtcNow; + + // Email may have changed since the last sign in - ensure we update it. + // TODO Should we emit an event if it has changed? + oneLoginUser.Email = email; - if (oneLoginUser is null) + if (oneLoginUser.PersonId is not null) { - oneLoginUser = new() - { - Subject = sub, - Email = email, - FirstOneLoginSignIn = clock.UtcNow, - LastOneLoginSignIn = clock.UtcNow - }; - dbContext.OneLoginUsers.Add(oneLoginUser); + oneLoginUser.LastSignIn = clock.UtcNow; } - else - { - oneLoginUser.LastOneLoginSignIn = clock.UtcNow; + } - // Email may have changed since the last sign in - ensure we update it. - // TODO Should we emit an event if it has changed? - oneLoginUser.Email = email; + await dbContext.SaveChangesAsync(); - if (oneLoginUser.PersonId is not null) - { - oneLoginUser.LastSignIn = clock.UtcNow; - } - } + await journeyInstance.UpdateStateAsync(state => + { + state.Reset(); + state.OneLoginAuthenticationTicket = ticket; - await dbContext.SaveChangesAsync(); + if (oneLoginUser.VerificationRoute is not null) + { + Debug.Assert(oneLoginUser.VerifiedNames is not null); + Debug.Assert(oneLoginUser.VerifiedDatesOfBirth is not null); + state.SetVerified(oneLoginUser.VerifiedNames!, oneLoginUser.VerifiedDatesOfBirth!); + } - await journeyInstance.UpdateStateAsync(state => + if (oneLoginUser.Person?.Trn is string trn && !ShowDebugPages) { - state.Reset(); - state.OneLoginAuthenticationTicket = ticket; - - if (oneLoginUser.VerificationRoute is not null) - { - Debug.Assert(oneLoginUser.VerifiedNames is not null); - Debug.Assert(oneLoginUser.VerifiedDatesOfBirth is not null); - state.SetVerified(oneLoginUser.VerifiedNames!, oneLoginUser.VerifiedDatesOfBirth!); - } - - if (oneLoginUser.Person?.Trn is string trn && !ShowDebugPages) - { - Complete(state, trn); - } - }); + Complete(state, trn); + } + }); + } + + public Task OnUserVerified(JourneyInstance journeyInstance, AuthenticationTicket ticket) + { + var verifiedNames = ticket.Principal.GetCoreIdentityNames().Select(n => n.NameParts.Select(part => part.Value).ToArray()).ToArray(); + var verifiedDatesOfBirth = ticket.Principal.GetCoreIdentityBirthDates().Select(d => d.Value).ToArray(); + var coreIdentityClaimVc = ticket.Principal.FindFirstValue("vc") ?? throw new InvalidOperationException("No vc claim present."); + + return OnUserVerifiedCore( + journeyInstance, + verifiedNames, + verifiedDatesOfBirth, + coreIdentityClaimVc, + state => state.OneLoginAuthenticationTicket = ticket); + } + + public async Task OnUserVerifiedCore( + JourneyInstance journeyInstance, + string[][] verifiedNames, + DateOnly[] verifiedDatesOfBirth, + string? coreIdentityClaimVc, + Action? updateState = null) + { + var sub = journeyInstance.State.OneLoginAuthenticationTicket!.Principal.FindFirstValue("sub") ?? throw new InvalidOperationException("No sub claim."); + + var oneLoginUser = await dbContext.OneLoginUsers.SingleAsync(u => u.Subject == sub); + oneLoginUser.VerifiedOn = clock.UtcNow; + oneLoginUser.VerificationRoute = OneLoginUserVerificationRoute.OneLogin; + oneLoginUser.VerifiedNames = verifiedNames; + oneLoginUser.VerifiedDatesOfBirth = verifiedDatesOfBirth; + oneLoginUser.LastCoreIdentityVc = coreIdentityClaimVc; + + string? trn = null; + + if (await TryApplyTrnToken() is (Guid PersonId, string Trn) trnTokenPerson) + { + oneLoginUser.PersonId = trnTokenPerson.PersonId; + oneLoginUser.FirstSignIn = clock.UtcNow; + oneLoginUser.LastSignIn = clock.UtcNow; + trn = trnTokenPerson.Trn; } - async Task OnUserVerified() + await dbContext.SaveChangesAsync(); + + await journeyInstance.UpdateStateAsync(state => { - var verifiedNames = ticket.Principal.GetCoreIdentityNames().Select(n => n.NameParts.Select(part => part.Value).ToArray()).ToArray(); - var verifiedDatesOfBirth = ticket.Principal.GetCoreIdentityBirthDates().Select(d => d.Value).ToArray(); - var coreIdentityClaimVc = ticket.Principal.FindFirstValue("vc") ?? throw new InvalidOperationException("No vc claim present."); - - var oneLoginUser = await dbContext.OneLoginUsers.SingleAsync(u => u.Subject == sub); - oneLoginUser.VerifiedOn = clock.UtcNow; - oneLoginUser.VerificationRoute = OneLoginUserVerificationRoute.OneLogin; - oneLoginUser.VerifiedNames = verifiedNames; - oneLoginUser.VerifiedDatesOfBirth = verifiedDatesOfBirth; - oneLoginUser.LastCoreIdentityVc = coreIdentityClaimVc; - await dbContext.SaveChangesAsync(); + updateState?.Invoke(state); + state.AttemptedIdentityVerification = true; + + state.SetVerified(verifiedNames, verifiedDatesOfBirth); + + if (trn is not null) + { + Complete(state, trn); + } + }); + + async Task<(Guid PersonId, string Trn)?> TryApplyTrnToken() + { + if (journeyInstance.State.TrnToken is not string trnToken) + { + return null; + } + + var trnTokenModel = await idDbContext.TrnTokens.SingleOrDefaultAsync( + t => t.TrnToken == trnToken && t.ExpiresUtc > clock.UtcNow && t.UserId == null); - await journeyInstance.UpdateStateAsync(state => + if (trnTokenModel is null) { - state.OneLoginAuthenticationTicket = ticket; - state.AttemptedIdentityVerification = true; + return null; + } + + var trnTokenPerson = await dbContext.Persons.SingleOrDefaultAsync( + p => p.Trn == trnTokenModel.Trn && p.DqtState == (int)ContactState.Active); + + if (trnTokenPerson is null) + { + return null; + } + + // If the record's last name and DOB do not match the verified details then don't automatically link + if (!trnTokenPerson.LastName.Equals(verifiedNames.First().Last(), StringComparison.OrdinalIgnoreCase) || + trnTokenPerson.DateOfBirth != verifiedDatesOfBirth.First()) + { + return null; + } + + // Invalidate the token + trnTokenModel.UserId = _teacherAuthIdUserIdSentinel; + await idDbContext.SaveChangesAsync(); - state.SetVerified(verifiedNames, verifiedDatesOfBirth); - }); + return (trnTokenPerson.PersonId, trnTokenModel.Trn); } } - public async Task OnUserVerificationWithOneLoginFailed(JourneyInstance journeyInstance) + public async Task OnVerificationFailed(JourneyInstance journeyInstance) { await journeyInstance.UpdateStateAsync(state => { diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/SignInJourneyState.cs b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/SignInJourneyState.cs index 3dea9bda2..78d2160f1 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/SignInJourneyState.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/SignInJourneyState.cs @@ -10,7 +10,8 @@ public class SignInJourneyState( string redirectUri, string serviceName, string serviceUrl, - string oneLoginAuthenticationScheme) + string oneLoginAuthenticationScheme, + string? trnToken = null) { public const string JourneyName = "SignInJourney"; @@ -25,6 +26,8 @@ public class SignInJourneyState( public string OneLoginAuthenticationScheme { get; } = oneLoginAuthenticationScheme; + public string? TrnToken { get; } = trnToken; + [JsonConverter(typeof(AuthenticationTicketJsonConverter))] public AuthenticationTicket? AuthenticationTicket { get; set; } diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/TestAppConfiguration.cs b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/TestAppConfiguration.cs index d640e3bca..a942bace3 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/TestAppConfiguration.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/TestAppConfiguration.cs @@ -36,13 +36,25 @@ public static WebApplicationBuilder AddTestApp(this WebApplicationBuilder builde options.MapInboundClaims = false; options.GetClaimsFromUserInfoEndpoint = true; options.SaveTokens = true; + options.Scope.Clear(); options.Scope.Add("openid"); options.Scope.Add("email"); options.Scope.Add("profile"); options.Scope.Add(CustomScopes.TeachingRecord); + options.ClaimActions.Add(new MapJsonClaimAction(ClaimTypes.OneLoginVerifiedNames)); options.ClaimActions.Add(new MapJsonClaimAction(ClaimTypes.OneLoginIdVerifiedBirthDates)); + + options.Events.OnRedirectToIdentityProvider = ctx => + { + if (ctx.Properties.Parameters.TryGetValue("TrnToken", out var trnTokenObj) && trnTokenObj is string trnToken) + { + ctx.ProtocolMessage.SetParameter("trn_token", trnToken); + } + + return Task.CompletedTask; + }; }); } else diff --git a/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.EndToEndTests/HostFixture.cs b/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.EndToEndTests/HostFixture.cs index 4e68f55ec..554dccc08 100644 --- a/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.EndToEndTests/HostFixture.cs +++ b/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.EndToEndTests/HostFixture.cs @@ -65,6 +65,8 @@ private Host CreateHost() => { DbHelper.ConfigureDbServices(services, context.Configuration.GetRequiredConnectionString("DefaultConnection")); + services.AddDbContext(options => options.UseInMemoryDatabase("TeacherAuthId"), contextLifetime: ServiceLifetime.Transient); + services.Configure(options => { options.AddScheme(FakeOneLoginAuthenticationScheme, b => b.HandlerType = typeof(FakeOneLoginHandler)); diff --git a/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.EndToEndTests/Infrastructure/Security/FakeOneLoginHandler.cs b/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.EndToEndTests/Infrastructure/Security/FakeOneLoginHandler.cs index 1a30bc54d..fc43417e2 100644 --- a/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.EndToEndTests/Infrastructure/Security/FakeOneLoginHandler.cs +++ b/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.EndToEndTests/Infrastructure/Security/FakeOneLoginHandler.cs @@ -43,7 +43,7 @@ public async Task ChallengeAsync(AuthenticationProperties? properties) var journeyInstance = (await signInJourneyHelper.UserInstanceStateProvider.GetSignInJourneyInstanceAsync(_context, journeyInstanceId))!; - var result = await signInJourneyHelper.OnUserVerificationWithOneLoginFailed(journeyInstance); + var result = await signInJourneyHelper.OnVerificationFailed(journeyInstance); await result.ExecuteAsync(_context); return; diff --git a/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.EndToEndTests/PageExtensions.cs b/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.EndToEndTests/PageExtensions.cs index 4871d4d54..9c37abcd7 100644 --- a/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.EndToEndTests/PageExtensions.cs +++ b/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.EndToEndTests/PageExtensions.cs @@ -11,9 +11,12 @@ public static Task WaitForUrlPathAsync(this IPage page, string path) => return asUri.LocalPath == path; }); - public static async Task GoToTestStartPage(this IPage page) + public static async Task GoToTestStartPage(this IPage page, string? trnToken = null) { - await page.GotoAsync($"/test?scheme={Uri.EscapeDataString(HostFixture.FakeOneLoginAuthenticationScheme)}"); + await page.GotoAsync( + $"/test" + + $"?scheme={Uri.EscapeDataString(HostFixture.FakeOneLoginAuthenticationScheme)}" + + $"&trn_token={Uri.EscapeDataString(trnToken ?? "")}"); } public static async Task AssertSignedIn(this IPage page, string trn) diff --git a/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.EndToEndTests/SignInTests.cs b/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.EndToEndTests/SignInTests.cs index 2826f20e2..f8b7d60c1 100644 --- a/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.EndToEndTests/SignInTests.cs +++ b/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.EndToEndTests/SignInTests.cs @@ -179,6 +179,41 @@ public async Task SignIn_UnknownVerifiedUserWithNeitherNinoNorTrn_DoesNotMatch() await page.WaitForUrlPathAsync("/check-answers"); } + [Fact] + public async Task SignIn_UnknownVerifiedUserWithTrnTokenAndMatchingDetails_MatchesWithTrn() + { + var person = await TestData.CreatePerson(x => x.WithTrn().WithNationalInsuranceNumber(false)); + + var subject = TestData.CreateOneLoginUserSubject(); + var email = Faker.Internet.Email(); + var coreIdentityVc = TestData.CreateOneLoginCoreIdentityVc(person.FirstName, person.LastName, person.DateOfBirth); + SetCurrentOneLoginUser(OneLoginUserInfo.Create(subject, email, coreIdentityVc)); + + var trnToken = Guid.NewGuid().ToString(); + + using (var idDbContext = HostFixture.Services.GetRequiredService()) + { + idDbContext.TrnTokens.Add(new IdTrnToken() + { + TrnToken = trnToken, + Trn = person.Trn!, + CreatedUtc = Clock.UtcNow, + ExpiresUtc = Clock.UtcNow.AddDays(1), + Email = email, + UserId = null + }); + + await idDbContext.SaveChangesAsync(); + } + + await using var context = await HostFixture.CreateBrowserContext(); + var page = await context.NewPageAsync(); + + await page.GoToTestStartPage(trnToken: trnToken); + + await page.AssertSignedIn(person.Trn!); + } + [Fact] public async Task SignIn_KnownUser() { diff --git a/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.EndToEndTests/TeachingRecordSystem.AuthorizeAccess.EndToEndTests.csproj b/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.EndToEndTests/TeachingRecordSystem.AuthorizeAccess.EndToEndTests.csproj index 5dfbd4799..1787e25ce 100644 --- a/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.EndToEndTests/TeachingRecordSystem.AuthorizeAccess.EndToEndTests.csproj +++ b/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.EndToEndTests/TeachingRecordSystem.AuthorizeAccess.EndToEndTests.csproj @@ -10,6 +10,7 @@ + diff --git a/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.EndToEndTests/TestBase.cs b/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.EndToEndTests/TestBase.cs index 200ecf079..a924c67f3 100644 --- a/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.EndToEndTests/TestBase.cs +++ b/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.EndToEndTests/TestBase.cs @@ -7,6 +7,8 @@ public abstract class TestBase(HostFixture hostFixture) { public HostFixture HostFixture { get; } = hostFixture; + public IClock Clock => HostFixture.Services.GetRequiredService(); + public TestData TestData => HostFixture.Services.GetRequiredService(); public virtual async Task WithDbContext(Func> action) diff --git a/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/HostFixture.cs b/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/HostFixture.cs index 8b143a4eb..9fcdbcede 100644 --- a/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/HostFixture.cs +++ b/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/HostFixture.cs @@ -35,6 +35,8 @@ protected override void ConfigureWebHost(IWebHostBuilder builder) { DbHelper.ConfigureDbServices(services, context.Configuration.GetRequiredConnectionString("DefaultConnection")); + services.AddDbContext(options => options.UseInMemoryDatabase("TeacherAuthId"), contextLifetime: ServiceLifetime.Transient); + services .Configure(options => { diff --git a/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/PageTests/CheckAnswersTests.cs b/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/PageTests/CheckAnswersTests.cs index d36f2696d..f25746770 100644 --- a/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/PageTests/CheckAnswersTests.cs +++ b/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/PageTests/CheckAnswersTests.cs @@ -28,7 +28,7 @@ public async Task Get_NotVerifiedWithOneLogin_ReturnsBadRequest() var journeyInstance = await CreateJourneyInstance(state); var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationOnlyVtr, createCoreIdentityVc: false); - await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + await GetSignInJourneyHelper().OnOneLoginCallback(journeyInstance, ticket); var request = new HttpRequestMessage(HttpMethod.Get, $"/check-answers?{journeyInstance.GetUniqueIdQueryParameter()}"); @@ -49,7 +49,7 @@ public async Task Get_NationalInsuranceNumberNotSpecified_RedirectsToNationalIns var oneLoginUser = await TestData.CreateOneLoginUser(verified: true); var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationOnlyVtr, oneLoginUser); - await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + await GetSignInJourneyHelper().OnOneLoginCallback(journeyInstance, ticket); Debug.Assert(state.NationalInsuranceNumber is null); @@ -73,7 +73,7 @@ public async Task Get_TrnNotSpecified_RedirectsToTrnPage() var oneLoginUser = await TestData.CreateOneLoginUser(verified: true); var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationOnlyVtr, oneLoginUser); - await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + await GetSignInJourneyHelper().OnOneLoginCallback(journeyInstance, ticket); Debug.Assert(state.NationalInsuranceNumber is null); await journeyInstance.UpdateStateAsync(state => state.SetNationalInsuranceNumber(true, TestData.GenerateNationalInsuranceNumber())); @@ -99,7 +99,7 @@ public async Task Get_AlreadyAuthenticated_RedirectsToStateRedirectUri() var oneLoginUser = await TestData.CreateOneLoginUser(person); var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationOnlyVtr, oneLoginUser); - await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + await GetSignInJourneyHelper().OnOneLoginCallback(journeyInstance, ticket); var request = new HttpRequestMessage(HttpMethod.Get, $"/check-answers?{journeyInstance.GetUniqueIdQueryParameter()}"); @@ -121,7 +121,7 @@ public async Task Get_ValidRequest_RendersExpectedContent() var oneLoginUser = await TestData.CreateOneLoginUser(verified: true); var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationOnlyVtr, oneLoginUser); - await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + await GetSignInJourneyHelper().OnOneLoginCallback(journeyInstance, ticket); var nationalInsuranceNumber = TestData.GenerateNationalInsuranceNumber(); var trn = await TestData.GenerateTrn(); @@ -167,7 +167,7 @@ public async Task Post_NotVerifiedWithOneLogin_ReturnsBadRequest() var journeyInstance = await CreateJourneyInstance(state); var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationOnlyVtr, createCoreIdentityVc: false); - await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + await GetSignInJourneyHelper().OnOneLoginCallback(journeyInstance, ticket); var request = new HttpRequestMessage(HttpMethod.Post, $"/check-answers?{journeyInstance.GetUniqueIdQueryParameter()}"); @@ -188,7 +188,7 @@ public async Task Post_NationalInsuranceNumberNotSpecified_RedirectsToNationalIn var oneLoginUser = await TestData.CreateOneLoginUser(verified: true); var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationOnlyVtr, oneLoginUser); - await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + await GetSignInJourneyHelper().OnOneLoginCallback(journeyInstance, ticket); Debug.Assert(state.NationalInsuranceNumber is null); @@ -212,7 +212,7 @@ public async Task Post_TrnNotSpecified_RedirectsToTrnPage() var oneLoginUser = await TestData.CreateOneLoginUser(verified: true); var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationOnlyVtr, oneLoginUser); - await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + await GetSignInJourneyHelper().OnOneLoginCallback(journeyInstance, ticket); Debug.Assert(state.NationalInsuranceNumber is null); await journeyInstance.UpdateStateAsync(state => state.SetNationalInsuranceNumber(true, TestData.GenerateNationalInsuranceNumber())); @@ -238,7 +238,7 @@ public async Task Post_AlreadyAuthenticated_RedirectsToStateRedirectUri() var oneLoginUser = await TestData.CreateOneLoginUser(person); var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationOnlyVtr, oneLoginUser); - await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + await GetSignInJourneyHelper().OnOneLoginCallback(journeyInstance, ticket); var request = new HttpRequestMessage(HttpMethod.Post, $"/check-answers?{journeyInstance.GetUniqueIdQueryParameter()}"); diff --git a/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/PageTests/ConnectTests.cs b/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/PageTests/ConnectTests.cs index 79481720f..84bec6353 100644 --- a/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/PageTests/ConnectTests.cs +++ b/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/PageTests/ConnectTests.cs @@ -26,7 +26,7 @@ public async Task Get_NotVerifiedWithOneLogin_ReturnsBadRequest() var journeyInstance = await CreateJourneyInstance(state); var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationOnlyVtr, createCoreIdentityVc: false); - await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + await GetSignInJourneyHelper().OnOneLoginCallback(journeyInstance, ticket); var request = new HttpRequestMessage(HttpMethod.Get, $"/connect?{journeyInstance.GetUniqueIdQueryParameter()}"); @@ -48,7 +48,7 @@ public async Task Get_AlreadyAuthenticated_RedirectsToStateRedirectUri() var oneLoginUser = await TestData.CreateOneLoginUser(person); var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationOnlyVtr, oneLoginUser); - await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + await GetSignInJourneyHelper().OnOneLoginCallback(journeyInstance, ticket); var request = new HttpRequestMessage(HttpMethod.Get, $"/connect?{journeyInstance.GetUniqueIdQueryParameter()}"); @@ -70,7 +70,7 @@ public async Task Get_ValidRequest_RendersExpectedContent() var oneLoginUser = await TestData.CreateOneLoginUser(verified: true); var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationOnlyVtr, oneLoginUser); - await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + await GetSignInJourneyHelper().OnOneLoginCallback(journeyInstance, ticket); var request = new HttpRequestMessage(HttpMethod.Get, $"/connect?{journeyInstance.GetUniqueIdQueryParameter()}"); @@ -104,7 +104,7 @@ public async Task Post_NotVerifiedWithOneLogin_ReturnsBadRequest() var journeyInstance = await CreateJourneyInstance(state); var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationOnlyVtr, createCoreIdentityVc: false); - await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + await GetSignInJourneyHelper().OnOneLoginCallback(journeyInstance, ticket); var request = new HttpRequestMessage(HttpMethod.Post, $"/connect?{journeyInstance.GetUniqueIdQueryParameter()}"); @@ -126,7 +126,7 @@ public async Task Post_AlreadyAuthenticated_RedirectsToStateRedirectUri() var oneLoginUser = await TestData.CreateOneLoginUser(person); var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationOnlyVtr, oneLoginUser); - await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + await GetSignInJourneyHelper().OnOneLoginCallback(journeyInstance, ticket); var request = new HttpRequestMessage(HttpMethod.Post, $"/connect?{journeyInstance.GetUniqueIdQueryParameter()}"); @@ -148,7 +148,7 @@ public async Task Post_ValidRequest_RedirectsToStartOfMatchingJourney() var oneLoginUser = await TestData.CreateOneLoginUser(verified: true); var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationOnlyVtr, oneLoginUser); - await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + await GetSignInJourneyHelper().OnOneLoginCallback(journeyInstance, ticket); var request = new HttpRequestMessage(HttpMethod.Post, $"/connect?{journeyInstance.GetUniqueIdQueryParameter()}"); diff --git a/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/PageTests/FoundTests.cs b/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/PageTests/FoundTests.cs index f5c00ca93..01487c743 100644 --- a/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/PageTests/FoundTests.cs +++ b/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/PageTests/FoundTests.cs @@ -14,7 +14,7 @@ public async Task Get_NotFullyAuthenticated_RedirectsToStartOfMatchingJourney() var oneLoginUser = await TestData.CreateOneLoginUser(verified: true); var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationOnlyVtr, oneLoginUser); - await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + await GetSignInJourneyHelper().OnOneLoginCallback(journeyInstance, ticket); Debug.Assert(state.AuthenticationTicket is null); var request = new HttpRequestMessage(HttpMethod.Get, $"/found?{journeyInstance.GetUniqueIdQueryParameter()}"); @@ -38,7 +38,7 @@ public async Task Get_ValidRequest_RendersExpectedContent() var oneLoginUser = await TestData.CreateOneLoginUser(person); var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationOnlyVtr, oneLoginUser); - await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + await GetSignInJourneyHelper().OnOneLoginCallback(journeyInstance, ticket); var request = new HttpRequestMessage(HttpMethod.Get, $"/found?{journeyInstance.GetUniqueIdQueryParameter()}"); @@ -59,7 +59,7 @@ public async Task Post_NotAuthenticated_RedirectsToStartOfMatchingJourney() var oneLoginUser = await TestData.CreateOneLoginUser(verified: true); var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationOnlyVtr, oneLoginUser); - await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + await GetSignInJourneyHelper().OnOneLoginCallback(journeyInstance, ticket); Debug.Assert(state.AuthenticationTicket is null); var request = new HttpRequestMessage(HttpMethod.Post, $"/found?{journeyInstance.GetUniqueIdQueryParameter()}"); @@ -83,7 +83,7 @@ public async Task Post_ValidRequest_RedirectsToStateRedirectUri() var oneLoginUser = await TestData.CreateOneLoginUser(person); var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationOnlyVtr, oneLoginUser); - await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + await GetSignInJourneyHelper().OnOneLoginCallback(journeyInstance, ticket); var request = new HttpRequestMessage(HttpMethod.Post, $"/found?{journeyInstance.GetUniqueIdQueryParameter()}"); diff --git a/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/PageTests/NationalInsuranceNumberTests.cs b/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/PageTests/NationalInsuranceNumberTests.cs index 3dca5c216..7efec5452 100644 --- a/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/PageTests/NationalInsuranceNumberTests.cs +++ b/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/PageTests/NationalInsuranceNumberTests.cs @@ -26,7 +26,7 @@ public async Task Get_NotVerifiedWithOneLogin_ReturnsBadRequest() var journeyInstance = await CreateJourneyInstance(state); var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationOnlyVtr, createCoreIdentityVc: false); - await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + await GetSignInJourneyHelper().OnOneLoginCallback(journeyInstance, ticket); var request = new HttpRequestMessage(HttpMethod.Get, $"/national-insurance-number?{journeyInstance.GetUniqueIdQueryParameter()}"); @@ -48,7 +48,7 @@ public async Task Get_AlreadyAuthenticated_RedirectsToStateRedirectUri() var oneLoginUser = await TestData.CreateOneLoginUser(person); var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationOnlyVtr, oneLoginUser); - await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + await GetSignInJourneyHelper().OnOneLoginCallback(journeyInstance, ticket); var request = new HttpRequestMessage(HttpMethod.Get, $"/national-insurance-number?{journeyInstance.GetUniqueIdQueryParameter()}"); @@ -72,7 +72,7 @@ public async Task Get_ValidRequest_RendersExpectedContent(bool haveExistingValue var oneLoginUser = await TestData.CreateOneLoginUser(verified: true); var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationOnlyVtr, oneLoginUser); - await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + await GetSignInJourneyHelper().OnOneLoginCallback(journeyInstance, ticket); var existingNationalInsuranceNumber = haveExistingValueInState ? Faker.Identification.UkNationalInsuranceNumber() : null; if (existingNationalInsuranceNumber is not null) @@ -122,7 +122,7 @@ public async Task Post_NotVerifiedWithOneLogin_ReturnsBadRequest() var journeyInstance = await CreateJourneyInstance(state); var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationOnlyVtr, createCoreIdentityVc: false); - await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + await GetSignInJourneyHelper().OnOneLoginCallback(journeyInstance, ticket); var nationalInsuranceNumber = Faker.Identification.UkNationalInsuranceNumber(); @@ -152,7 +152,7 @@ public async Task Post_AlreadyAuthenticated_RedirectsToStateRedirectUri() var oneLoginUser = await TestData.CreateOneLoginUser(person); var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationOnlyVtr, oneLoginUser); - await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + await GetSignInJourneyHelper().OnOneLoginCallback(journeyInstance, ticket); var nationalInsuranceNumber = person.NationalInsuranceNumber!; @@ -182,7 +182,7 @@ public async Task Post_HaveNationalInsuranceNumberNotAnswered_RendersError() var oneLoginUser = await TestData.CreateOneLoginUser(verified: true); var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationOnlyVtr, oneLoginUser); - await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + await GetSignInJourneyHelper().OnOneLoginCallback(journeyInstance, ticket); var request = new HttpRequestMessage(HttpMethod.Post, $"/national-insurance-number?{journeyInstance.GetUniqueIdQueryParameter()}") { @@ -208,7 +208,7 @@ public async Task Post_EmptyNationalInsuranceNumber_RendersError() var oneLoginUser = await TestData.CreateOneLoginUser(verified: true); var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationOnlyVtr, oneLoginUser); - await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + await GetSignInJourneyHelper().OnOneLoginCallback(journeyInstance, ticket); var nationalInsuranceNumber = ""; @@ -238,7 +238,7 @@ public async Task Post_InvalidNationalInsuranceNumber_RendersError() var oneLoginUser = await TestData.CreateOneLoginUser(verified: true); var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationOnlyVtr, oneLoginUser); - await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + await GetSignInJourneyHelper().OnOneLoginCallback(journeyInstance, ticket); var nationalInsuranceNumber = "xxx"; @@ -268,7 +268,7 @@ public async Task Post_HaveNationalInsuranceNumberNotSpecified_UpdatesStateAndRe var oneLoginUser = await TestData.CreateOneLoginUser(verified: true); var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationOnlyVtr, oneLoginUser); - await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + await GetSignInJourneyHelper().OnOneLoginCallback(journeyInstance, ticket); var request = new HttpRequestMessage(HttpMethod.Post, $"/national-insurance-number?{journeyInstance.GetUniqueIdQueryParameter()}") { @@ -301,7 +301,7 @@ public async Task Post_ValidNationalInsuranceNumberButLookupFailed_UpdatesStateA var oneLoginUser = await TestData.CreateOneLoginUser(verified: true); var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationOnlyVtr, oneLoginUser); - await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + await GetSignInJourneyHelper().OnOneLoginCallback(journeyInstance, ticket); var nationalInsuranceNumber = Faker.Identification.UkNationalInsuranceNumber(); @@ -341,7 +341,7 @@ public async Task Post_ValidNationalInsuranceNumberAndLookupSucceeded_UpdatesSta verifiedInfo: ([person.FirstName, person.LastName], person.DateOfBirth)); var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationOnlyVtr, oneLoginUser); - await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + await GetSignInJourneyHelper().OnOneLoginCallback(journeyInstance, ticket); var nationalInsuranceNumber = person.NationalInsuranceNumber!; diff --git a/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/PageTests/NotFoundTests.cs b/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/PageTests/NotFoundTests.cs index 8631e1d78..ab38b5c7c 100644 --- a/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/PageTests/NotFoundTests.cs +++ b/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/PageTests/NotFoundTests.cs @@ -30,7 +30,7 @@ public async Task Get_NotVerifiedWithOneLogin_ReturnsBadRequest() var oneLoginUser = await TestData.CreateOneLoginUser(verified: false); var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationOnlyVtr, oneLoginUser); - await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + await GetSignInJourneyHelper().OnOneLoginCallback(journeyInstance, ticket); var request = new HttpRequestMessage(HttpMethod.Get, $"/not-found?{journeyInstance.GetUniqueIdQueryParameter()}"); @@ -52,7 +52,7 @@ public async Task Get_AlreadyAuthenticated_RedirectsToStateRedirectUri() var oneLoginUser = await TestData.CreateOneLoginUser(person); var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationOnlyVtr, oneLoginUser); - await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + await GetSignInJourneyHelper().OnOneLoginCallback(journeyInstance, ticket); var request = new HttpRequestMessage(HttpMethod.Get, $"/not-found?{journeyInstance.GetUniqueIdQueryParameter()}"); @@ -74,7 +74,7 @@ public async Task Get_NationalInsuranceNumberNotSpecified_RedirectsToNationalIns var oneLoginUser = await TestData.CreateOneLoginUser(verified: true); var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationOnlyVtr, oneLoginUser); - await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + await GetSignInJourneyHelper().OnOneLoginCallback(journeyInstance, ticket); Debug.Assert(state.NationalInsuranceNumber is null); @@ -98,7 +98,7 @@ public async Task Get_TrnNotSpecified_RedirectsToTrnPage() var oneLoginUser = await TestData.CreateOneLoginUser(verified: true); var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationOnlyVtr, oneLoginUser); - await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + await GetSignInJourneyHelper().OnOneLoginCallback(journeyInstance, ticket); Debug.Assert(state.NationalInsuranceNumber is null); await journeyInstance.UpdateStateAsync(state => state.SetNationalInsuranceNumber(true, TestData.GenerateNationalInsuranceNumber())); @@ -123,7 +123,7 @@ public async Task Get_ValidRequest_RendersExpectedContent() var oneLoginUser = await TestData.CreateOneLoginUser(verified: true); var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationOnlyVtr, oneLoginUser); - await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + await GetSignInJourneyHelper().OnOneLoginCallback(journeyInstance, ticket); await journeyInstance.UpdateStateAsync(async state => { @@ -150,7 +150,7 @@ public async Task Post_ValidRequest_RedirectsToCheckAnswersPage() var oneLoginUser = await TestData.CreateOneLoginUser(verified: true); var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationOnlyVtr, oneLoginUser); - await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + await GetSignInJourneyHelper().OnOneLoginCallback(journeyInstance, ticket); await journeyInstance.UpdateStateAsync(async state => { diff --git a/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/PageTests/NotVerifiedTests.cs b/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/PageTests/NotVerifiedTests.cs index d3b5f1e2a..706c53b4b 100644 --- a/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/PageTests/NotVerifiedTests.cs +++ b/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/PageTests/NotVerifiedTests.cs @@ -29,7 +29,7 @@ public async Task Get_AlreadyAuthenticated_RedirectsToStateRedirectUri() var oneLoginUser = await TestData.CreateOneLoginUser(person); var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationOnlyVtr, oneLoginUser); - await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + await GetSignInJourneyHelper().OnOneLoginCallback(journeyInstance, ticket); var request = new HttpRequestMessage(HttpMethod.Get, $"/not-verified?{journeyInstance.GetUniqueIdQueryParameter()}"); @@ -51,7 +51,7 @@ public async Task Get_VerifiedWithOneLogin_ReturnsBadRequest() var oneLoginUser = await TestData.CreateOneLoginUser(verified: true); var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationOnlyVtr, oneLoginUser); - await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + await GetSignInJourneyHelper().OnOneLoginCallback(journeyInstance, ticket); var request = new HttpRequestMessage(HttpMethod.Get, $"/not-verified?{journeyInstance.GetUniqueIdQueryParameter()}"); @@ -72,7 +72,7 @@ public async Task Get_ValidRequest_ReturnsExpectedContent() var oneLoginUser = await TestData.CreateOneLoginUser(verified: false); var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationOnlyVtr, oneLoginUser); - await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + await GetSignInJourneyHelper().OnOneLoginCallback(journeyInstance, ticket); var request = new HttpRequestMessage(HttpMethod.Get, $"/not-verified?{journeyInstance.GetUniqueIdQueryParameter()}"); diff --git a/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/PageTests/TrnTests.cs b/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/PageTests/TrnTests.cs index 463d87ad5..aafdccbb6 100644 --- a/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/PageTests/TrnTests.cs +++ b/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/PageTests/TrnTests.cs @@ -28,7 +28,7 @@ public async Task Get_NotVerifiedWithOneLogin_ReturnsBadRequest() var journeyInstance = await CreateJourneyInstance(state); var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationOnlyVtr, createCoreIdentityVc: false); - await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + await GetSignInJourneyHelper().OnOneLoginCallback(journeyInstance, ticket); var request = new HttpRequestMessage(HttpMethod.Get, $"/trn?{journeyInstance.GetUniqueIdQueryParameter()}"); @@ -50,7 +50,7 @@ public async Task Get_AlreadyAuthenticated_RedirectsToStateRedirectUri() var oneLoginUser = await TestData.CreateOneLoginUser(person); var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationOnlyVtr, oneLoginUser); - await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + await GetSignInJourneyHelper().OnOneLoginCallback(journeyInstance, ticket); var request = new HttpRequestMessage(HttpMethod.Get, $"/trn?{journeyInstance.GetUniqueIdQueryParameter()}"); @@ -72,7 +72,7 @@ public async Task Get_NationalInsuranceNumberNotSpecified_RedirectsToNationalIns var oneLoginUser = await TestData.CreateOneLoginUser(verified: true); var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationOnlyVtr, oneLoginUser); - await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + await GetSignInJourneyHelper().OnOneLoginCallback(journeyInstance, ticket); Debug.Assert(state.NationalInsuranceNumber is null); @@ -98,7 +98,7 @@ public async Task Get_ValidRequest_RendersExpectedContent(bool haveExistingValue var oneLoginUser = await TestData.CreateOneLoginUser(verified: true); var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationOnlyVtr, oneLoginUser); - await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + await GetSignInJourneyHelper().OnOneLoginCallback(journeyInstance, ticket); var existingTrn = haveExistingValueInState ? await TestData.GenerateTrn() : null; @@ -159,7 +159,7 @@ public async Task Post_NotVerifiedWithOneLogin_ReturnsBadRequest() var trn = await TestData.GenerateTrn(); var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationOnlyVtr, createCoreIdentityVc: false); - await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + await GetSignInJourneyHelper().OnOneLoginCallback(journeyInstance, ticket); var request = new HttpRequestMessage(HttpMethod.Post, $"/trn?{journeyInstance.GetUniqueIdQueryParameter()}") { @@ -188,7 +188,7 @@ public async Task Post_AlreadyAuthenticated_RedirectsToStateRedirectUri() var oneLoginUser = await TestData.CreateOneLoginUser(person); var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationOnlyVtr, oneLoginUser); - await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + await GetSignInJourneyHelper().OnOneLoginCallback(journeyInstance, ticket); var trn = person.Trn!; @@ -219,7 +219,7 @@ public async Task Post_NationalInsuranceNumberNotSpecified_RedirectsToNationalIn var oneLoginUser = await TestData.CreateOneLoginUser(verified: true); var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationOnlyVtr, oneLoginUser); - await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + await GetSignInJourneyHelper().OnOneLoginCallback(journeyInstance, ticket); var trn = await TestData.GenerateTrn(); @@ -252,7 +252,7 @@ public async Task Post_HaveTrnNotAnswered_RendersError() var oneLoginUser = await TestData.CreateOneLoginUser(verified: true); var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationOnlyVtr, oneLoginUser); - await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + await GetSignInJourneyHelper().OnOneLoginCallback(journeyInstance, ticket); await journeyInstance.UpdateStateAsync(state => state.SetNationalInsuranceNumber(true, TestData.GenerateNationalInsuranceNumber())); @@ -280,7 +280,7 @@ public async Task Post_EmptyTrn_RendersError() var oneLoginUser = await TestData.CreateOneLoginUser(verified: true); var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationOnlyVtr, oneLoginUser); - await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + await GetSignInJourneyHelper().OnOneLoginCallback(journeyInstance, ticket); var trn = ""; @@ -312,7 +312,7 @@ public async Task Post_InvalidTrn_RendersError() var oneLoginUser = await TestData.CreateOneLoginUser(verified: true); var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationOnlyVtr, oneLoginUser); - await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + await GetSignInJourneyHelper().OnOneLoginCallback(journeyInstance, ticket); var trn = "xxx"; @@ -344,7 +344,7 @@ public async Task Post_NoTrnSpecified_UpdatesStateAndRedirectsToNotFoundPage() var oneLoginUser = await TestData.CreateOneLoginUser(verified: true); var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationOnlyVtr, oneLoginUser); - await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + await GetSignInJourneyHelper().OnOneLoginCallback(journeyInstance, ticket); var trn = await TestData.GenerateTrn(); @@ -382,7 +382,7 @@ public async Task Post_ValidTrnButLookupFailed_UpdatesStateAndRedirectsToNotFoun var oneLoginUser = await TestData.CreateOneLoginUser(verified: true); var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationOnlyVtr, oneLoginUser); - await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + await GetSignInJourneyHelper().OnOneLoginCallback(journeyInstance, ticket); var trn = await TestData.GenerateTrn(); @@ -424,7 +424,7 @@ public async Task Post_ValidTrnAndLookupSucceeded_UpdatesStateUpdatesOneLoginUse verifiedInfo: ([person.FirstName, person.LastName], person.DateOfBirth)); var ticket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationOnlyVtr, oneLoginUser); - await GetSignInJourneyHelper().OnSignedInWithOneLogin(journeyInstance, ticket); + await GetSignInJourneyHelper().OnOneLoginCallback(journeyInstance, ticket); var trn = person.Trn!; diff --git a/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/SignInJourneyHelperTests.cs b/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/SignInJourneyHelperTests.cs index c5b963c91..6655cfdf0 100644 --- a/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/SignInJourneyHelperTests.cs +++ b/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/SignInJourneyHelperTests.cs @@ -11,7 +11,7 @@ namespace TeachingRecordSystem.AuthorizeAccess.Tests; public class SignInJourneyHelperTests(HostFixture hostFixture) : TestBase(hostFixture) { [Fact] - public Task OnSignedInWithOneLogin_AuthenticationOnly_UserAlreadyExistsAndTeachingRecordKnown_CompletesJourney() => + public Task OnOneLoginCallback_AuthenticationOnly_UserAlreadyExistsAndTeachingRecordKnown_CompletesJourney() => WithDbContext(async dbContext => { // Arrange @@ -27,7 +27,7 @@ public Task OnSignedInWithOneLogin_AuthenticationOnly_UserAlreadyExistsAndTeachi var authenticationTicket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationOnlyVtr, sub: user.Subject); // Act - var result = await helper.OnSignedInWithOneLogin(journeyInstance, authenticationTicket); + var result = await helper.OnOneLoginCallback(journeyInstance, authenticationTicket); // Assert Assert.NotNull(state.AuthenticationTicket); @@ -43,7 +43,7 @@ public Task OnSignedInWithOneLogin_AuthenticationOnly_UserAlreadyExistsAndTeachi }); [Fact] - public Task OnSignedInWithOneLogin_AuthenticationOnly_UserAlreadyExistsButTeachingNotRecordKnown_RequestsIdentityVerification() => + public Task OnOneLoginCallback_AuthenticationOnly_UserAlreadyExistsButTeachingNotRecordKnown_RequestsIdentityVerification() => WithDbContext(async dbContext => { // Arrange @@ -58,7 +58,7 @@ public Task OnSignedInWithOneLogin_AuthenticationOnly_UserAlreadyExistsButTeachi var authenticationTicket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationOnlyVtr, sub: user.Subject); // Act - var result = await helper.OnSignedInWithOneLogin(journeyInstance, authenticationTicket); + var result = await helper.OnOneLoginCallback(journeyInstance, authenticationTicket); // Assert Assert.NotNull(state.OneLoginAuthenticationTicket); @@ -75,7 +75,7 @@ public Task OnSignedInWithOneLogin_AuthenticationOnly_UserAlreadyExistsButTeachi }); [Fact] - public Task OnSignedInWithOneLogin_AuthenticationOnly_UserDoesNotExist_RequestsIdentityVerification() => + public Task OnOneLoginCallback_AuthenticationOnly_UserDoesNotExist_RequestsIdentityVerification() => WithDbContext(async dbContext => { // Arrange @@ -88,7 +88,7 @@ public Task OnSignedInWithOneLogin_AuthenticationOnly_UserDoesNotExist_RequestsI var authenticationTicket = CreateOneLoginAuthenticationTicket(vtr: SignInJourneyHelper.AuthenticationOnlyVtr, sub: subject); // Act - var result = await helper.OnSignedInWithOneLogin(journeyInstance, authenticationTicket); + var result = await helper.OnOneLoginCallback(journeyInstance, authenticationTicket); // Assert Assert.NotNull(state.OneLoginAuthenticationTicket); @@ -106,7 +106,7 @@ public Task OnSignedInWithOneLogin_AuthenticationOnly_UserDoesNotExist_RequestsI }); [Fact] - public Task OnSignedInWithOneLogin_AuthenticationAndVerification_VerificationFailed_RedirectsToErrorPage() => + public Task OnOneLoginCallback_AuthenticationAndVerification_VerificationFailed_RedirectsToErrorPage() => WithDbContext(async dbContext => { // Arrange @@ -121,12 +121,13 @@ public Task OnSignedInWithOneLogin_AuthenticationAndVerification_VerificationFai var authenticationTicket = CreateOneLoginAuthenticationTicket( vtr: SignInJourneyHelper.AuthenticationOnlyVtr, sub: user.Subject, + email: user.Subject, createCoreIdentityVc: false); - var onAuthenticatedResult = await helper.OnSignedInWithOneLogin(journeyInstance, authenticationTicket); - Debug.Assert(onAuthenticatedResult is ChallengeHttpResult); + var callbackResult = await helper.OnOneLoginCallback(journeyInstance, authenticationTicket); + Debug.Assert(callbackResult is ChallengeHttpResult); // Act - var result = await helper.OnUserVerificationWithOneLoginFailed(journeyInstance); + var result = await helper.OnVerificationFailed(journeyInstance); // Assert Assert.NotNull(state.OneLoginAuthenticationTicket); @@ -143,7 +144,7 @@ public Task OnSignedInWithOneLogin_AuthenticationAndVerification_VerificationFai }); [Fact] - public Task OnSignedInWithOneLogin_AuthenticationAndVerification_VerificationSucceeded_RedirectsToStartOfMatchingJourney() => + public Task OnOneLoginCallback_AuthenticationAndVerification_VerificationSucceeded_RedirectsToStartOfMatchingJourney() => WithDbContext(async dbContext => { // Arrange @@ -162,12 +163,10 @@ public Task OnSignedInWithOneLogin_AuthenticationAndVerification_VerificationSuc var authenticationTicket = CreateOneLoginAuthenticationTicket( vtr: SignInJourneyHelper.AuthenticationOnlyVtr, sub: user.Subject, - firstName: firstName, - lastName: lastName, - dateOfBirth: dateOfBirth, + email: user.Subject, createCoreIdentityVc: false); - var onAuthenticatedResult = await helper.OnSignedInWithOneLogin(journeyInstance, authenticationTicket); - Debug.Assert(onAuthenticatedResult is ChallengeHttpResult); + var callbackResult = await helper.OnOneLoginCallback(journeyInstance, authenticationTicket); + Debug.Assert(callbackResult is ChallengeHttpResult); var verifiedTicket = CreateOneLoginAuthenticationTicket( vtr: SignInJourneyHelper.AuthenticationAndIdentityVerificationVtr, @@ -178,7 +177,7 @@ public Task OnSignedInWithOneLogin_AuthenticationAndVerification_VerificationSuc createCoreIdentityVc: true); // Act - var result = await helper.OnSignedInWithOneLogin(journeyInstance, verifiedTicket); + var result = await helper.OnOneLoginCallback(journeyInstance, verifiedTicket); // Assert Assert.NotNull(state.OneLoginAuthenticationTicket); @@ -200,6 +199,274 @@ public Task OnSignedInWithOneLogin_AuthenticationAndVerification_VerificationSuc Assert.Equal($"/Connect?{journeyInstance.GetUniqueIdQueryParameter()}", redirectResult.Url); }); + [Fact] + public Task OnOneLoginCallback_AuthenticationAndVerification_VerificationSucceededAndTrnTokenMatchesVerifiedLastNameAndDateOfBirth_CompletesJourney() => + WithDbContext(async dbContext => + { + // Arrange + var helper = CreateHelper(dbContext); + + var person = await TestData.CreatePerson(b => b.WithTrn(true)); + var user = await TestData.CreateOneLoginUser(personId: null); + Clock.Advance(); + + var trnToken = await CreateTrnToken(person.Trn!, user.Email); + + var firstName = person.FirstName; + var lastName = person.LastName; + var dateOfBirth = person.DateOfBirth; + + var state = new SignInJourneyState(redirectUri: "/", serviceName: "Test Service", serviceUrl: "https://service", oneLoginAuthenticationScheme: "dummy", trnToken); + var journeyInstance = await CreateJourneyInstance(state); + + var authenticationTicket = CreateOneLoginAuthenticationTicket( + vtr: SignInJourneyHelper.AuthenticationOnlyVtr, + sub: user.Subject, + email: user.Subject, + createCoreIdentityVc: false); + var callbackResult = await helper.OnOneLoginCallback(journeyInstance, authenticationTicket); + Debug.Assert(callbackResult is ChallengeHttpResult); + + var verifiedTicket = CreateOneLoginAuthenticationTicket( + vtr: SignInJourneyHelper.AuthenticationAndIdentityVerificationVtr, + sub: user.Subject, + firstName: firstName, + lastName: lastName, + dateOfBirth: dateOfBirth, + createCoreIdentityVc: true); + + // Act + var result = await helper.OnOneLoginCallback(journeyInstance, verifiedTicket); + + // Assert + Assert.NotNull(state.AuthenticationTicket); + + user = await WithDbContext(dbContext => dbContext.OneLoginUsers.SingleAsync(u => u.Subject == user.Subject)); + Assert.NotEqual(Clock.UtcNow, user.FirstOneLoginSignIn); + Assert.Equal(Clock.UtcNow, user.LastOneLoginSignIn); + Assert.Equal(Clock.UtcNow, user.FirstSignIn); + Assert.Equal(Clock.UtcNow, user.LastSignIn); + + var redirectResult = Assert.IsType(result); + Assert.Equal($"{state.RedirectUri}?{journeyInstance.GetUniqueIdQueryParameter()}", redirectResult.Url); + }); + + [Fact] + public Task OnOneLoginCallback_AuthenticationAndVerification_VerificationSucceededButTrnTokenDoesNotExist_RedirectsToStartOfMatchingJourney() => + WithDbContext(async dbContext => + { + // Arrange + var helper = CreateHelper(dbContext); + + var person = await TestData.CreatePerson(b => b.WithTrn(true)); + var user = await TestData.CreateOneLoginUser(personId: null); + Clock.Advance(); + + var trnToken = Guid.NewGuid().ToString(); + + var firstName = person.FirstName; + var lastName = person.LastName; + var dateOfBirth = person.DateOfBirth; + + var state = new SignInJourneyState(redirectUri: "/", serviceName: "Test Service", serviceUrl: "https://service", oneLoginAuthenticationScheme: "dummy", trnToken); + var journeyInstance = await CreateJourneyInstance(state); + + var authenticationTicket = CreateOneLoginAuthenticationTicket( + vtr: SignInJourneyHelper.AuthenticationOnlyVtr, + sub: user.Subject, + email: user.Subject, + createCoreIdentityVc: false); + var callbackResult = await helper.OnOneLoginCallback(journeyInstance, authenticationTicket); + Debug.Assert(callbackResult is ChallengeHttpResult); + + var verifiedTicket = CreateOneLoginAuthenticationTicket( + vtr: SignInJourneyHelper.AuthenticationAndIdentityVerificationVtr, + sub: user.Subject, + firstName: firstName, + lastName: lastName, + dateOfBirth: dateOfBirth, + createCoreIdentityVc: true); + + // Act + var result = await helper.OnOneLoginCallback(journeyInstance, verifiedTicket); + + // Assert + var redirectResult = Assert.IsType(result); + Assert.Equal($"/Connect?{journeyInstance.GetUniqueIdQueryParameter()}", redirectResult.Url); + }); + + [Fact] + public Task OnOneLoginCallback_AuthenticationAndVerification_VerificationSucceededButTrnTokenHasExpired_RedirectsToStartOfMatchingJourney() => + WithDbContext(async dbContext => + { + // Arrange + var helper = CreateHelper(dbContext); + + var person = await TestData.CreatePerson(b => b.WithTrn(true)); + var user = await TestData.CreateOneLoginUser(personId: null); + Clock.Advance(); + + var trnToken = await CreateTrnToken(person.Trn!, user.Email, expires: TimeSpan.FromSeconds(-1)); + + var firstName = person.FirstName; + var lastName = person.LastName; + var dateOfBirth = person.DateOfBirth; + + var state = new SignInJourneyState(redirectUri: "/", serviceName: "Test Service", serviceUrl: "https://service", oneLoginAuthenticationScheme: "dummy", trnToken); + var journeyInstance = await CreateJourneyInstance(state); + + var authenticationTicket = CreateOneLoginAuthenticationTicket( + vtr: SignInJourneyHelper.AuthenticationOnlyVtr, + sub: user.Subject, + email: user.Subject, + createCoreIdentityVc: false); + var callbackResult = await helper.OnOneLoginCallback(journeyInstance, authenticationTicket); + Debug.Assert(callbackResult is ChallengeHttpResult); + + var verifiedTicket = CreateOneLoginAuthenticationTicket( + vtr: SignInJourneyHelper.AuthenticationAndIdentityVerificationVtr, + sub: user.Subject, + firstName: firstName, + lastName: lastName, + dateOfBirth: dateOfBirth, + createCoreIdentityVc: true); + + // Act + var result = await helper.OnOneLoginCallback(journeyInstance, verifiedTicket); + + // Assert + var redirectResult = Assert.IsType(result); + Assert.Equal($"/Connect?{journeyInstance.GetUniqueIdQueryParameter()}", redirectResult.Url); + }); + + [Fact] + public Task OnOneLoginCallback_AuthenticationAndVerification_VerificationSucceededButTrnTokenHasAlreadyBeenUsed_RedirectsToStartOfMatchingJourney() => + WithDbContext(async dbContext => + { + // Arrange + var helper = CreateHelper(dbContext); + + var person = await TestData.CreatePerson(b => b.WithTrn(true)); + var user = await TestData.CreateOneLoginUser(personId: null); + Clock.Advance(); + + var trnToken = await CreateTrnToken(person.Trn!, user.Email, userId: Guid.NewGuid()); + + var state = new SignInJourneyState(redirectUri: "/", serviceName: "Test Service", serviceUrl: "https://service", oneLoginAuthenticationScheme: "dummy", trnToken); + var journeyInstance = await CreateJourneyInstance(state); + + var authenticationTicket = CreateOneLoginAuthenticationTicket( + vtr: SignInJourneyHelper.AuthenticationOnlyVtr, + sub: user.Subject, + email: user.Subject, + createCoreIdentityVc: false); + var callbackResult = await helper.OnOneLoginCallback(journeyInstance, authenticationTicket); + Debug.Assert(callbackResult is ChallengeHttpResult); + + var verifiedTicket = CreateOneLoginAuthenticationTicket( + vtr: SignInJourneyHelper.AuthenticationAndIdentityVerificationVtr, + sub: user.Subject, + firstName: person.FirstName, + lastName: person.LastName, + dateOfBirth: person.DateOfBirth, + createCoreIdentityVc: true); + + // Act + var result = await helper.OnOneLoginCallback(journeyInstance, verifiedTicket); + + // Assert + var redirectResult = Assert.IsType(result); + Assert.Equal($"/Connect?{journeyInstance.GetUniqueIdQueryParameter()}", redirectResult.Url); + }); + + [Fact] + public Task OnOneLoginCallback_AuthenticationAndVerification_VerificationSucceededButRecordFromTrnTokenDoesNotMatchLastName_RedirectsToStartOfMatchingJourney() => + WithDbContext(async dbContext => + { + // Arrange + var helper = CreateHelper(dbContext); + + var person = await TestData.CreatePerson(b => b.WithTrn(true)); + var user = await TestData.CreateOneLoginUser(personId: null); + Clock.Advance(); + + var trnToken = await CreateTrnToken(person.Trn!, user.Email); + + var firstName = person.FirstName; + var lastName = TestData.GenerateChangedLastName(person.LastName); + var dateOfBirth = person.DateOfBirth; + + var state = new SignInJourneyState(redirectUri: "/", serviceName: "Test Service", serviceUrl: "https://service", oneLoginAuthenticationScheme: "dummy", trnToken); + var journeyInstance = await CreateJourneyInstance(state); + + var authenticationTicket = CreateOneLoginAuthenticationTicket( + vtr: SignInJourneyHelper.AuthenticationOnlyVtr, + sub: user.Subject, + email: user.Subject, + createCoreIdentityVc: false); + var callbackResult = await helper.OnOneLoginCallback(journeyInstance, authenticationTicket); + Debug.Assert(callbackResult is ChallengeHttpResult); + + var verifiedTicket = CreateOneLoginAuthenticationTicket( + vtr: SignInJourneyHelper.AuthenticationAndIdentityVerificationVtr, + sub: user.Subject, + firstName: firstName, + lastName: lastName, + dateOfBirth: dateOfBirth, + createCoreIdentityVc: true); + + // Act + var result = await helper.OnOneLoginCallback(journeyInstance, verifiedTicket); + + // Assert + var redirectResult = Assert.IsType(result); + Assert.Equal($"/Connect?{journeyInstance.GetUniqueIdQueryParameter()}", redirectResult.Url); + }); + + [Fact] + public Task OnOneLoginCallback_AuthenticationAndVerification_VerificationSucceededButRecordFromTrnTokenDoesNotMatchDateOfBirth_RedirectsToStartOfMatchingJourney() => + WithDbContext(async dbContext => + { + // Arrange + var helper = CreateHelper(dbContext); + + var person = await TestData.CreatePerson(b => b.WithTrn(true)); + var user = await TestData.CreateOneLoginUser(personId: null); + Clock.Advance(); + + var trnToken = await CreateTrnToken(person.Trn!, user.Email); + + var firstName = person.FirstName; + var lastName = person.LastName; + var dateOfBirth = person.DateOfBirth.AddDays(1); + + var state = new SignInJourneyState(redirectUri: "/", serviceName: "Test Service", serviceUrl: "https://service", oneLoginAuthenticationScheme: "dummy", trnToken); + var journeyInstance = await CreateJourneyInstance(state); + + var authenticationTicket = CreateOneLoginAuthenticationTicket( + vtr: SignInJourneyHelper.AuthenticationOnlyVtr, + sub: user.Subject, + email: user.Subject, + createCoreIdentityVc: false); + var callbackResult = await helper.OnOneLoginCallback(journeyInstance, authenticationTicket); + Debug.Assert(callbackResult is ChallengeHttpResult); + + var verifiedTicket = CreateOneLoginAuthenticationTicket( + vtr: SignInJourneyHelper.AuthenticationAndIdentityVerificationVtr, + sub: user.Subject, + firstName: firstName, + lastName: lastName, + dateOfBirth: dateOfBirth, + createCoreIdentityVc: true); + + // Act + var result = await helper.OnOneLoginCallback(journeyInstance, verifiedTicket); + + // Assert + var redirectResult = Assert.IsType(result); + Assert.Equal($"/Connect?{journeyInstance.GetUniqueIdQueryParameter()}", redirectResult.Url); + }); + [Fact] public Task TryMatchToTeachingRecord_MatchesZeroResults_ReturnsFalseAndDoesNotSetAuthenticationTicket() => WithDbContext(async dbContext => @@ -221,7 +488,7 @@ public Task TryMatchToTeachingRecord_MatchesZeroResults_ReturnsFalseAndDoesNotSe vtr: SignInJourneyHelper.AuthenticationOnlyVtr, sub: user.Subject, createCoreIdentityVc: false); - await helper.OnSignedInWithOneLogin(journeyInstance, authenticationTicket); + await helper.OnOneLoginCallback(journeyInstance, authenticationTicket); await journeyInstance.UpdateStateAsync(state => state.SetNationalInsuranceNumber(true, Faker.Identification.UkNationalInsuranceNumber())); @@ -269,7 +536,7 @@ public Task TryMatchToTeachingRecord_MatchesMultipleResults_ReturnsFalseAndDoesN vtr: SignInJourneyHelper.AuthenticationOnlyVtr, sub: user.Subject, createCoreIdentityVc: false); - await helper.OnSignedInWithOneLogin(journeyInstance, authenticationTicket); + await helper.OnOneLoginCallback(journeyInstance, authenticationTicket); await journeyInstance.UpdateStateAsync(state => state.SetNationalInsuranceNumber(true, person1.NationalInsuranceNumber)); @@ -338,7 +605,7 @@ public Task TryMatchToTeachingRecord_MatchesSingleResultWithoutTrn_ReturnsFalseA vtr: SignInJourneyHelper.AuthenticationOnlyVtr, sub: user.Subject, createCoreIdentityVc: false); - await helper.OnSignedInWithOneLogin(journeyInstance, authenticationTicket); + await helper.OnOneLoginCallback(journeyInstance, authenticationTicket); await journeyInstance.UpdateStateAsync(state => state.SetNationalInsuranceNumber(true, person.NationalInsuranceNumber)); @@ -397,7 +664,7 @@ public Task TryMatchToTeachingRecord_MatchesSingleResult_ReturnsTrueAndUpdatesOn vtr: SignInJourneyHelper.AuthenticationOnlyVtr, sub: user.Subject, createCoreIdentityVc: false); - await helper.OnSignedInWithOneLogin(journeyInstance, authenticationTicket); + await helper.OnOneLoginCallback(journeyInstance, authenticationTicket); await journeyInstance.UpdateStateAsync(state => state.SetNationalInsuranceNumber(true, person.NationalInsuranceNumber)); @@ -445,6 +712,29 @@ private SignInJourneyHelper CreateHelper(TrsDbContext dbContext, IPersonSearchSe return ActivatorUtilities.CreateInstance(HostFixture.Services, dbContext, personSearchService, linkGenerator, options, Clock); } + private async Task CreateTrnToken(string trn, string email, TimeSpan? expires = null, Guid? userId = null) + { + var trnToken = Guid.NewGuid().ToString(); + var expiresUtc = Clock.UtcNow.Add(expires ?? TimeSpan.FromHours(1)); + + using (var idDbContext = HostFixture.Services.GetRequiredService()) + { + idDbContext.TrnTokens.Add(new IdTrnToken() + { + TrnToken = trnToken, + Trn = trn, + CreatedUtc = Clock.UtcNow, + ExpiresUtc = expiresUtc, + Email = email, + UserId = userId + }); + + await idDbContext.SaveChangesAsync(); + } + + return trnToken; + } + private class FakeLinkGenerator : AuthorizeAccessLinkGenerator { protected override string GetRequiredPathByPage(string page, string? handler = null, object? routeValues = null) => page; diff --git a/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/TeachingRecordSystem.AuthorizeAccess.Tests.csproj b/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/TeachingRecordSystem.AuthorizeAccess.Tests.csproj index d619a25ab..28fe8f9b1 100644 --- a/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/TeachingRecordSystem.AuthorizeAccess.Tests.csproj +++ b/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/TeachingRecordSystem.AuthorizeAccess.Tests.csproj @@ -10,6 +10,7 @@ +