Skip to content

Commit bd0c266

Browse files
authored
Add optional HttpResponse parameter to ReplyForbiddenWithWwwAuthenticateHeaderAsync. Handle null context. (#412)
1 parent 4870017 commit bd0c266

File tree

4 files changed

+30
-15
lines changed

4 files changed

+30
-15
lines changed

src/Microsoft.Identity.Web/Constants/IDWebErrorMessage.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ namespace Microsoft.Identity.Web
99
internal static class IDWebErrorMessage
1010
{
1111
// General IDW10000 = "IDW10000:"
12-
public const string HttpContextIsNull = "IDW10000: HttpContext is null. ";
12+
public const string HttpContextIsNull = "IDW10001: HttpContext is null. ";
13+
public const string HttpContextAndHttpResponseAreNull = "IDW10002: Current HttpContext and HttpResponse argument are null. Pass an HttpResponse argument. ";
1314

1415
// Configuration IDW10100 = "IDW10100:"
1516
public const string ProvideEitherScopeKeySectionOrScopes = "IDW10101: Either provide the '{0}' or the '{1}' to the 'AuthorizeForScopes'. ";

src/Microsoft.Identity.Web/ITokenAcquisition.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,12 @@ Task<string> GetAccessTokenForUserAsync(
5050
/// the client can trigger an interaction with the user so the user can consent to more scopes.
5151
/// </summary>
5252
/// <param name="scopes">Scopes to consent to.</param>
53-
/// <param name="msalSeviceException"><see cref="MsalUiRequiredException"/> triggering the challenge.</param>
53+
/// <param name="msalServiceException"><see cref="MsalUiRequiredException"/> triggering the challenge.</param>
54+
/// <param name="httpResponse">The <see cref="HttpResponse"/> to update.</param>
5455
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
5556
Task ReplyForbiddenWithWwwAuthenticateHeaderAsync(
5657
IEnumerable<string> scopes,
57-
MsalUiRequiredException msalSeviceException);
58+
MsalUiRequiredException msalServiceException,
59+
HttpResponse? httpResponse = null);
5860
}
5961
}

src/Microsoft.Identity.Web/Microsoft.Identity.Web.xml

Lines changed: 11 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Microsoft.Identity.Web/TokenAcquisition.cs

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -533,8 +533,9 @@ private async Task<string> GetAccessTokenForWebAppWithAccountFromCacheAsync(
533533
/// </summary>
534534
/// <param name="scopes">Scopes to consent to.</param>
535535
/// <param name="msalServiceException">The <see cref="MsalUiRequiredException"/> that triggered the challenge.</param>
536+
/// <param name="httpResponse">The <see cref="HttpResponse"/> to update.</param>
536537
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
537-
public async Task ReplyForbiddenWithWwwAuthenticateHeaderAsync(IEnumerable<string> scopes, MsalUiRequiredException msalServiceException)
538+
public async Task ReplyForbiddenWithWwwAuthenticateHeaderAsync(IEnumerable<string> scopes, MsalUiRequiredException msalServiceException, HttpResponse? httpResponse = null)
538539
{
539540
// A user interaction is required, but we are in a web API, and therefore, we need to report back to the client through a 'WWW-Authenticate' header https://tools.ietf.org/html/rfc6750#section-3.1
540541
string proposedAction = Constants.Consent;
@@ -562,23 +563,26 @@ public async Task ReplyForbiddenWithWwwAuthenticateHeaderAsync(IEnumerable<strin
562563

563564
string parameterString = string.Join(", ", parameters.Select(p => $"{p.Key}=\"{p.Value}\""));
564565

565-
if (CurrentHttpContext != null)
566-
{
567-
var httpResponse = CurrentHttpContext.Response;
568-
var headers = httpResponse.Headers;
569-
httpResponse.StatusCode = (int)HttpStatusCode.Forbidden;
566+
httpResponse ??= CurrentHttpContext?.Response;
570567

571-
headers[HeaderNames.WWWAuthenticate] = new StringValues($"{Constants.Bearer} {parameterString}");
568+
if (httpResponse == null)
569+
{
570+
throw new InvalidOperationException(IDWebErrorMessage.HttpContextAndHttpResponseAreNull);
572571
}
572+
573+
var headers = httpResponse.Headers;
574+
httpResponse.StatusCode = (int)HttpStatusCode.Forbidden;
575+
576+
headers[HeaderNames.WWWAuthenticate] = new StringValues($"{Constants.Bearer} {parameterString}");
573577
}
574578

575-
private static bool AcceptedTokenVersionMismatch(MsalUiRequiredException msalSeviceException)
579+
private static bool AcceptedTokenVersionMismatch(MsalUiRequiredException msalServiceException)
576580
{
577581
// Normally app developers should not make decisions based on the internal AAD code
578582
// however until the STS sends sub-error codes for this error, this is the only
579583
// way to distinguish the case.
580584
// This is subject to change in the future
581-
return msalSeviceException.Message.Contains(ErrorCodes.B2CPasswordResetErrorCode, StringComparison.InvariantCulture);
585+
return msalServiceException.Message.Contains(ErrorCodes.B2CPasswordResetErrorCode, StringComparison.InvariantCulture);
582586
}
583587

584588
private async Task<ClaimsPrincipal?> GetAuthenticatedUserAsync(ClaimsPrincipal? user)

0 commit comments

Comments
 (0)