Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow registering without a phone number #730

Merged
merged 1 commit into from
Sep 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ public AuthenticationState(
[JsonInclude]
public bool MobileNumberVerified { get; private set; }
[JsonInclude]
public bool ContinueWithoutMobileNumber { get; private set; }
[JsonInclude]
public Guid? ExistingAccountUserId { get; private set; }
[JsonInclude]
public string? ExistingAccountEmail { get; private set; }
Expand Down Expand Up @@ -162,7 +164,9 @@ public AuthenticationState(
[JsonIgnore]
public bool HasIttProviderSet => HasIttProvider.HasValue;
[JsonIgnore]
public bool ContactDetailsVerified => EmailAddressVerified && MobileNumberVerified;
public bool ContactDetailsVerified => EmailAddressVerified && MobileNumberVerifiedOrSkipped;
[JsonIgnore]
public bool MobileNumberVerifiedOrSkipped => MobileNumberVerified || ContinueWithoutMobileNumber;
[JsonIgnore]
public bool HasTrnToken => !string.IsNullOrEmpty(TrnToken);
[JsonIgnore]
Expand Down Expand Up @@ -242,6 +246,7 @@ public void Reset(DateTime utcNow)
HasPreviousName = default;
MobileNumber = default;
MobileNumberVerified = default;
ContinueWithoutMobileNumber = default;
ExistingAccountUserId = default;
ExistingAccountEmail = default;
ExistingAccountMobileNumber = default;
Expand Down Expand Up @@ -313,6 +318,7 @@ public void OnMobileNumberVerified(User? user = null)

MobileNumberVerified = true;
FirstTimeSignInForEmail = user is null;
ContinueWithoutMobileNumber = false;

if (user is not null)
{
Expand Down Expand Up @@ -390,12 +396,12 @@ public void OnUserRegistered(User user)
throw new InvalidOperationException($"Email has not been verified.");
}

if (MobileNumber is null)
if (MobileNumber is null && !ContinueWithoutMobileNumber)
{
throw new InvalidOperationException($"{nameof(MobileNumber)} is not known.");
}

if (!MobileNumberSet)
if (!MobileNumberVerified && !ContinueWithoutMobileNumber)
{
throw new InvalidOperationException($"Mobile number has not been verified.");
}
Expand Down Expand Up @@ -565,6 +571,14 @@ public void OnMobileNumberSet(string mobileNumber)
{
MobileNumber = mobileNumber;
MobileNumberVerified = false;
ContinueWithoutMobileNumber = false;
}

public void OnContinueWithoutMobileNumber()
{
MobileNumber = null;
MobileNumberVerified = false;
ContinueWithoutMobileNumber = true;
}

public void OnHaveResumedCompletedJourney()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,14 @@ protected IdentityLinkGenerator(

protected abstract bool TryGetAuthenticationState([NotNullWhen(true)] out AuthenticationState? authenticationState);

protected virtual string Page(string pageName, bool authenticationJourneyRequired = true)
protected virtual string Page(string pageName, string? handler = null, bool authenticationJourneyRequired = true)
{
if (!TryGetAuthenticationState(out var authenticationState) && authenticationJourneyRequired)
{
throw new InvalidOperationException($"The current request has no {nameof(AuthenticationState)}.");
}

var url = new Url(LinkGenerator.GetPathByPage(pageName));
var url = new Url(LinkGenerator.GetPathByPage(pageName, handler));

if (authenticationState is not null)
{
Expand Down Expand Up @@ -78,6 +78,8 @@ protected virtual string Page(string pageName, bool authenticationJourneyRequire

public string RegisterPhone() => Page("/SignIn/Register/Phone");

public string RegisterPhoneContinueWithout() => Page("/SignIn/Register/Phone", handler: "ContinueWithout");

public string RegisterPhoneConfirmation() => Page("/SignIn/Register/PhoneConfirmation");

public string RegisterResendPhoneConfirmation() => Page("/SignIn/Register/ResendPhoneConfirmation");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ public override string GetLastAccessibleStepUrl(string? requestedStep)
(Steps.ResendEmailConfirmation, _) => Steps.EmailConfirmation,
(Steps.InstitutionEmail, { EmailAddressVerified: false }) => Steps.EmailConfirmation,
(Steps.InstitutionEmail, { EmailAddressVerified: true }) => shouldCheckAnswers ? Steps.CheckAnswers : Steps.Phone,
(Steps.Phone, { ContinueWithoutMobileNumber: true }) => shouldCheckAnswers ? Steps.CheckAnswers : Steps.Name,
(Steps.Phone, _) => Steps.PhoneConfirmation,
(Steps.PhoneConfirmation, { UserId: not null }) => Steps.PhoneExists,
(Steps.PhoneConfirmation, _) => shouldCheckAnswers ? Steps.CheckAnswers : Steps.Name,
Expand Down Expand Up @@ -123,6 +124,7 @@ public override string GetLastAccessibleStepUrl(string? requestedStep)
(Steps.ResendPhoneConfirmation, _) => Steps.PhoneConfirmation,
(Steps.PhoneExists, { MobileNumberVerified: true }) => Steps.Phone,
(Steps.PhoneExists, { MobileNumberVerified: false }) => Steps.PhoneConfirmation,
(Steps.Name, { ContinueWithoutMobileNumber: true }) => Steps.Phone,
(Steps.Name, { MobileNumberVerified: true }) => Steps.Phone,
(Steps.Name, { MobileNumberVerified: false }) => Steps.PhoneConfirmation,
(Steps.PreferredName, _) => Steps.Name,
Expand Down Expand Up @@ -175,9 +177,8 @@ AuthenticationState is
{
EmailAddressSet: true,
EmailAddressVerified: true,
MobileNumberVerifiedOrSkipped: true,
HasValidEmail: true,
MobileNumberSet: true,
MobileNumberVerified: true,
NameSet: true,
PreferredNameSet: true,
DateOfBirthSet: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,10 @@ public override bool CanAccessStep(string step)
CoreSignInJourney.Steps.PhoneConfirmation => AuthenticationState is { MobileNumberSet: true, MobileNumberVerified: false, EmailAddressVerified: true },
CoreSignInJourney.Steps.PhoneExists => AuthenticationState.UserId is not null,
CoreSignInJourney.Steps.ResendPhoneConfirmation => AuthenticationState is { MobileNumberSet: true, MobileNumberVerified: false },
CoreSignInJourney.Steps.Email => AuthenticationState.MobileNumberVerified,
CoreSignInJourney.Steps.EmailConfirmation => AuthenticationState is { EmailAddressSet: true, EmailAddressVerified: false, MobileNumberVerified: true },
CoreSignInJourney.Steps.Email => AuthenticationState.MobileNumberVerifiedOrSkipped,
CoreSignInJourney.Steps.EmailConfirmation => AuthenticationState is { EmailAddressSet: true, EmailAddressVerified: false, MobileNumberVerifiedOrSkipped: true },
CoreSignInJourney.Steps.ResendEmailConfirmation => AuthenticationState is { EmailAddressSet: true, EmailAddressVerified: false },
CoreSignInJourney.Steps.InstitutionEmail => AuthenticationState is { EmailAddressSet: true, EmailAddressVerified: true, MobileNumberVerified: true, IsInstitutionEmail: true },
CoreSignInJourney.Steps.InstitutionEmail => AuthenticationState is { EmailAddressSet: true, EmailAddressVerified: true, MobileNumberVerifiedOrSkipped: true, IsInstitutionEmail: true },
CoreSignInJourney.Steps.PreferredName => AuthenticationState.ContactDetailsVerified,
CoreSignInJourney.Steps.DateOfBirth => AuthenticationState is { PreferredNameSet: true, ContactDetailsVerified: true },
CoreSignInJourney.Steps.AccountExists => AuthenticationState.ExistingAccountFound,
Expand All @@ -99,6 +99,7 @@ public override bool CanAccessStep(string step)
(SignInJourney.Steps.Email, _) => SignInJourney.Steps.EmailConfirmation,
(SignInJourney.Steps.EmailConfirmation, { UserId: not null }) => CoreSignInJourney.Steps.EmailExists,
(SignInJourney.Steps.EmailConfirmation, _) => shouldCheckAnswers ? Steps.CheckAnswers : CoreSignInJourney.Steps.Phone,
(CoreSignInJourney.Steps.Phone, { ContinueWithoutMobileNumber: true }) => shouldCheckAnswers ? Steps.CheckAnswers : CoreSignInJourney.Steps.PreferredName,
(CoreSignInJourney.Steps.Phone, _) => CoreSignInJourney.Steps.PhoneConfirmation,
(CoreSignInJourney.Steps.PhoneConfirmation, { UserId: not null }) => CoreSignInJourney.Steps.PhoneExists,
(CoreSignInJourney.Steps.PhoneConfirmation, _) => shouldCheckAnswers ? Steps.CheckAnswers : CoreSignInJourney.Steps.PreferredName,
Expand Down Expand Up @@ -138,6 +139,7 @@ public override bool CanAccessStep(string step)
(CoreSignInJourney.Steps.ResendEmailConfirmation, _) => CoreSignInJourney.Steps.EmailConfirmation,
(CoreSignInJourney.Steps.InstitutionEmail, { EmailAddressVerified: false }) => CoreSignInJourney.Steps.EmailConfirmation,
(CoreSignInJourney.Steps.InstitutionEmail, { EmailAddressVerified: true }) => CoreSignInJourney.Steps.Email,
(CoreSignInJourney.Steps.PreferredName, { ContinueWithoutMobileNumber: true }) => CoreSignInJourney.Steps.Phone,
(CoreSignInJourney.Steps.PreferredName, { MobileNumberVerified: true }) => CoreSignInJourney.Steps.Phone,
(CoreSignInJourney.Steps.PreferredName, { MobileNumberVerified: false }) => CoreSignInJourney.Steps.PhoneConfirmation,
(CoreSignInJourney.Steps.DateOfBirth, _) => CoreSignInJourney.Steps.PreferredName,
Expand All @@ -160,9 +162,8 @@ AuthenticationState is
{
EmailAddressSet: true,
EmailAddressVerified: true,
MobileNumberVerifiedOrSkipped: true,
HasValidEmail: true,
MobileNumberSet: true,
MobileNumberVerified: true,
NameSet: true,
PreferredNameSet: true,
DateOfBirthSet: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public async Task<User> CreateUser(AuthenticationState authenticationState)
DateOfBirth = authenticationState.DateOfBirth,
EmailAddress = authenticationState.EmailAddress!,
MobileNumber = authenticationState.MobileNumber,
NormalizedMobileNumber = MobileNumber.Parse(authenticationState.MobileNumber!),
NormalizedMobileNumber = authenticationState.MobileNumber is not null ? MobileNumber.Parse(authenticationState.MobileNumber) : null,
FirstName = authenticationState.FirstName!,
MiddleName = authenticationState.MiddleName,
LastName = authenticationState.LastName!,
Expand Down Expand Up @@ -94,6 +94,7 @@ public async Task<User> CreateUserWithTrnLookup(AuthenticationState authenticati
DateOfBirth = authenticationState.DateOfBirth,
EmailAddress = authenticationState.EmailAddress!,
MobileNumber = authenticationState.MobileNumber,
NormalizedMobileNumber = authenticationState.MobileNumber is not null ? MobileNumber.Parse(authenticationState.MobileNumber) : null,
FirstName = useDqtRecordForNames ? authenticationState.DqtFirstName! : authenticationState.FirstName!,
MiddleName = useDqtRecordForNames ? authenticationState.DqtMiddleName : authenticationState.MiddleName,
LastName = useDqtRecordForNames ? authenticationState.DqtLastName! : authenticationState.LastName!,
Expand Down Expand Up @@ -140,6 +141,7 @@ public async Task<User> CreateUserWithTrnToken(AuthenticationState authenticatio
DateOfBirth = authenticationState.DateOfBirth,
EmailAddress = authenticationState.EmailAddress!,
MobileNumber = authenticationState.MobileNumber,
NormalizedMobileNumber = authenticationState.MobileNumber is not null ? MobileNumber.Parse(authenticationState.MobileNumber) : null,
FirstName = authenticationState.FirstName!,
MiddleName = authenticationState.MiddleName,
LastName = authenticationState.LastName!,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@
</govuk-summary-list-row>
<govuk-summary-list-row>
<govuk-summary-list-row-key>Mobile phone</govuk-summary-list-row-key>
<govuk-summary-list-row-value>@Model.MobilePhoneNumber</govuk-summary-list-row-value>
<govuk-summary-list-row-value>
<span fallback-text="Not provided">@Model.MobilePhoneNumber</span>
</govuk-summary-list-row-value>
<govuk-summary-list-row-actions>
<govuk-summary-list-row-action href="@LinkGenerator.RegisterPhone()" visually-hidden-text="mobile phone">Change</govuk-summary-list-row-action>
</govuk-summary-list-row-actions>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public CheckAnswers(SignInJourney journey)

public bool? RequiresTrnLookup => _journey.AuthenticationState.UserRequirements.RequiresTrnLookup();
public string? EmailAddress => _journey.AuthenticationState.EmailAddress;
public string? MobilePhoneNumber => MobileNumber.Parse(_journey.AuthenticationState.MobileNumber!).ToDisplayString();
public string? MobilePhoneNumber => _journey.AuthenticationState.MobileNumber is not null ? MobileNumber.Parse(_journey.AuthenticationState.MobileNumber).ToDisplayString() : null;
public string? FullName => _journey.AuthenticationState.GetName();
public string? PreferredName => _journey.AuthenticationState.PreferredName;
public DateOnly? DateOfBirth => _journey.AuthenticationState.DateOfBirth;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@page "/sign-in/register/phone"
@page "/sign-in/register/phone/{handler?}"
@model TeacherIdentity.AuthServer.Pages.SignIn.Register.Phone
@{
ViewBag.Title = "Your mobile number";
Expand All @@ -22,6 +22,15 @@
input-class="govuk-input--extra-letter-spacing"
label-class="govuk-label--s" />

<govuk-details>
<govuk-details-summary>I do not have access to a mobile phone</govuk-details-summary>
<govuk-details-text>
<govuk-button type="submit" class="govuk-button--secondary" formaction="@LinkGenerator.RegisterPhoneContinueWithout()">
Continue without it
</govuk-button>
</govuk-details-text>
</govuk-details>

<govuk-button type="submit">Continue</govuk-button>
</form>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,14 @@ public async Task<IActionResult> OnPost()
return pinGenerationResult.Result!;
}

HttpContext.GetAuthenticationState().OnMobileNumberSet(MobileNumber!);
_journey.AuthenticationState.OnMobileNumberSet(MobileNumber!);

return await _journey.Advance(CurrentStep);
}

public async Task<IActionResult> OnPostContinueWithout()
{
_journey.AuthenticationState.OnContinueWithoutMobileNumber();

return await _journey.Advance(CurrentStep);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,18 @@
autocomplete="one-time-code"
label-class="govuk-label--s" />

<p>
<a href="@LinkGenerator.RegisterResendPhoneConfirmation()">I have not received a text message</a>
</p>
<govuk-details>
<govuk-details-summary>I have not received a code</govuk-details-summary>
<govuk-details-text>
<p>
We can <a href="@LinkGenerator.RegisterResendPhoneConfirmation()">send you another code</a>
or you can continue without it.
</p>
<govuk-button type="submit" class="govuk-button--secondary" formaction="@LinkGenerator.RegisterPhoneContinueWithout()">
Continue without it
</govuk-button>
</govuk-details-text>
</govuk-details>

<govuk-button type="submit">Continue</govuk-button>
</form>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,9 @@
</govuk-summary-list-row>
<govuk-summary-list-row>
<govuk-summary-list-row-key>Mobile phone</govuk-summary-list-row-key>
<govuk-summary-list-row-value>@Model.MobilePhoneNumber</govuk-summary-list-row-value>
<govuk-summary-list-row-value>
<span fallback-text="Not provided">@Model.MobilePhoneNumber</span>
</govuk-summary-list-row-value>
<govuk-summary-list-row-actions>
<govuk-summary-list-row-action href="" visually-hidden-text="mobile phone">Change</govuk-summary-list-row-action>
</govuk-summary-list-row-actions>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public CheckAnswers(SignInJourney journey)

public string BackLink => _journey.GetPreviousStepUrl(CurrentStep);
public string? EmailAddress => _journey.AuthenticationState.EmailAddress;
public string? MobilePhoneNumber => MobileNumber.Parse(_journey.AuthenticationState.MobileNumber!).ToDisplayString();
public string? MobilePhoneNumber => _journey.AuthenticationState.MobileNumber is not null ? MobileNumber.Parse(_journey.AuthenticationState.MobileNumber).ToDisplayString() : null;
public string? FirstName => _journey.AuthenticationState.FirstName;
public string? MiddleName => _journey.AuthenticationState.MiddleName;
public string? LastName => _journey.AuthenticationState.LastName;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,6 @@ public async Task Post_ValidMobileNumberWithBlockedClient_ReturnsTooManyRequests
Assert.Equal(StatusCodes.Status429TooManyRequests, (int)response.StatusCode);
}


[Fact]
public async Task Post_EmptyMobileNumber_ReturnsError()
{
Expand Down Expand Up @@ -190,6 +189,25 @@ public async Task Post_NotificationServiceInvalidMobileNumber_ReturnsError()
await AssertEx.HtmlResponseHasError(response, "MobileNumber", "Enter a valid mobile phone number");
}

[Fact]
public async Task PostContinueWithout_SetsContinueWithoutPhoneNumberOnAuthenticationStateAndRedirects()
{
// Arrange
var authStateHelper = await CreateAuthenticationStateHelper(_currentPageAuthenticationState(), additionalScopes: null);

var request = new HttpRequestMessage(HttpMethod.Post, $"/sign-in/register/phone/ContinueWithout?{authStateHelper.ToQueryParam()}")
{
Content = new FormUrlEncodedContentBuilder()
};

// Act
var response = await HttpClient.SendAsync(request);

// Assert
Assert.Equal(StatusCodes.Status302Found, (int)response.StatusCode);
Assert.True(authStateHelper.AuthenticationState.ContinueWithoutMobileNumber);
}

private readonly AuthenticationStateConfigGenerator _currentPageAuthenticationState = RegisterJourneyAuthenticationStateHelper.ConfigureAuthenticationStateForPage(RegisterJourneyPage.Phone);
private readonly AuthenticationStateConfigGenerator _previousPageAuthenticationState = RegisterJourneyAuthenticationStateHelper.ConfigureAuthenticationStateForPage(RegisterJourneyPage.EmailConfirmation);
}
Loading