Skip to content

Commit

Permalink
Add "Classification" to UI Required Exception #1148 (#1236)
Browse files Browse the repository at this point in the history
  • Loading branch information
bgavrilMS authored Jun 27, 2019
1 parent 396913a commit 49e45f0
Show file tree
Hide file tree
Showing 6 changed files with 380 additions and 234 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ internal class SilentRequest : RequestBase
{
private readonly AcquireTokenSilentParameters _silentParameters;
private const string TheOnlyFamilyId = "1";
private const string FociClientMismatchSubError = "client_mismatch";

public SilentRequest(
IServiceBundle serviceBundle,
Expand Down Expand Up @@ -163,7 +162,7 @@ private async Task<MsalTokenResponse> TryGetTokenUsingFociAsync(CancellationToke
return null;
#else
if (MsalError.InvalidGrantError.Equals(ex?.ErrorCode, StringComparison.OrdinalIgnoreCase) &&
FociClientMismatchSubError.Equals(ex?.SubError, StringComparison.OrdinalIgnoreCase))
OAuth2SubError.ClientMismatch.Equals(ex?.SubError, StringComparison.OrdinalIgnoreCase))
{
logger.Error("[FOCI] FRT refresh failed - client mismatch");
return null;
Expand All @@ -179,7 +178,6 @@ private async Task<MsalTokenResponse> TryGetTokenUsingFociAsync(CancellationToke
}

return null;

}

private async Task<MsalTokenResponse> RefreshAccessTokenAsync(MsalRefreshTokenCacheItem msalRefreshTokenItem, CancellationToken cancellationToken)
Expand Down
18 changes: 10 additions & 8 deletions src/Microsoft.Identity.Client/MsalServiceException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,15 @@ namespace Microsoft.Identity.Client
/// </summary>
public class MsalServiceException : MsalException
{
private const string ClaimsKey = "claims";
private const string ResponseBodyKey = "response_body";
private const string CorrelationIdKey = "correlation_id";
private const string SubErrorKey = "sub_error";

private HttpResponse _httpResponse;
private OAuth2ResponseBase _oauth2ResponseBase;

#region Constructors
/// <summary>
/// Initializes a new instance of the exception class with a specified
/// error code, error message and a reference to the inner exception that is the cause of
Expand Down Expand Up @@ -123,7 +132,7 @@ public MsalServiceException(
Claims = claims;
}

private HttpResponse _httpResponse;
#endregion

internal HttpResponse HttpResponse
{
Expand All @@ -140,8 +149,6 @@ internal HttpResponse HttpResponse
}
}

private OAuth2ResponseBase _oauth2ResponseBase;

internal OAuth2ResponseBase OAuth2Response
{
get => _oauth2ResponseBase;
Expand Down Expand Up @@ -220,11 +227,6 @@ public override string ToString()
Headers);
}

private const string ClaimsKey = "claims";
private const string ResponseBodyKey = "response_body";
private const string CorrelationIdKey = "correlation_id";
private const string SubErrorKey = "sub_error";

internal override void PopulateJson(JObject jobj)
{
base.PopulateJson(jobj);
Expand Down
64 changes: 64 additions & 0 deletions src/Microsoft.Identity.Client/MsalUiRequiredException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT License.

using System;
using Microsoft.Identity.Client.OAuth2;

namespace Microsoft.Identity.Client
{
Expand All @@ -14,6 +15,37 @@ namespace Microsoft.Identity.Client
/// </summary>
public class MsalUiRequiredException : MsalServiceException
{
/// <summary>
/// Condition can be resolved by user interaction during the interactive authentication flow.
/// See https://aka.ms/msal-net-uirequiredexception-classification for details
/// </summary>
public const string BasicAction = OAuth2SubError.BasicAction;

/// <summary>
/// Condition can be resolved by additional remedial interaction with the system, outside of the interactive authentication flow.
/// See https://aka.ms/msal-net-uirequiredexception-classification for details
/// </summary>
public const string AdditionalAction = OAuth2SubError.AdditionalAction;

/// <summary>
/// Condition cannot be resolved at this time. Launching interactive authentication flow will show a message explaining the condition.
/// See https://aka.ms/msal-net-uirequiredexception-classification for details
/// </summary>
public const string MessageOnly = OAuth2SubError.MessageOnly;

/// <summary>
/// User's password has expired.
/// See https://aka.ms/msal-net-uirequiredexception-classification for details
/// </summary>
public const string UserPasswordExpired = OAuth2SubError.UserPasswordExpired;

/// <summary>
/// User consent is missing, or has been revoked.
/// See https://aka.ms/msal-net-uirequiredexception-classification for details
/// </summary>
public const string ConsentRequired = OAuth2SubError.ConsentRequired;


/// <summary>
/// Initializes a new instance of the exception class with a specified
/// error code and error message.
Expand All @@ -40,6 +72,38 @@ public MsalUiRequiredException(string errorCode, string errorMessage) :
/// <param name="innerException">Represents the root cause of the exception.</param>
public MsalUiRequiredException(string errorCode, string errorMessage, Exception innerException) : base(errorCode, errorMessage, innerException)
{

}

/// <summary>
/// Classification of the conditional access error, enabling you to do more actions or inform the user depending on your scenario. See https://aka.ms/msal-net-uirequiredexception-classification for details.
/// </summary>
/// <remarks>This class <see cref="MsalUiRequiredException"/> lists most classification strings as constants. </remarks>
public string Classification
{
get
{
switch (base.SubError)
{
case OAuth2SubError.BasicAction:
case OAuth2SubError.AdditionalAction:
case OAuth2SubError.MessageOnly:
case OAuth2SubError.ConsentRequired:
case OAuth2SubError.UserPasswordExpired:
return SubError;

case OAuth2SubError.BadToken:
case OAuth2SubError.TokenExpired:
case OAuth2SubError.ProtectionPolicyRequired:
case OAuth2SubError.ClientMismatch:
case OAuth2SubError.DeviceAuthenticationFailed:
return string.Empty;

// Forward compatibility - new sub-errors bubble through
default:
return SubError;
}
}
}
}
}
53 changes: 53 additions & 0 deletions src/Microsoft.Identity.Client/OAuth2/OAuthConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,59 @@ internal static class OAuth2Error
public const string AuthorizationPending = "authorization_pending";
}

internal static class OAuth2SubError
{
/// <summary>
/// Condition can be resolved by user interaction during the interactive authentication flow.
/// </summary>
public const string BasicAction = "basic_action";

/// <summary>
/// Condition can be resolved by additional remedial interaction with the system, outside of the interactive authentication flow.
/// </summary>
public const string AdditionalAction = "additional_action";

/// <summary>
/// Condition cannot be resolved at this time. Launching interactive authentication flow will show a message explaining the condition.
/// </summary>
public const string MessageOnly = "message_only";

/// <summary>
/// User's password has expired.
/// </summary>
public const string UserPasswordExpired = "user_password_expired";

/// <summary>
/// User consent is missing, or has been revoked.
/// </summary>
public const string ConsentRequired = "consent_required";

/// <summary>
/// Internal to MSALs. Indicates that no further silent calls should be made with this refresh token.
/// </summary>
public const string BadToken = "bad_token";

/// <summary>
/// Internal to MSALs. Indicates that no further silent calls should be made with this refresh token.
/// </summary>
public const string TokenExpired = "token_expired";

/// <summary>
/// Internal to MSALs. Needed in ios/android to complete the end-to-end true MAM flow. This suberror code is re-mapped to a different top level error code (IntuneAppProtectionPoliciesRequired), and not InteractionRequired
/// </summary>
public const string ProtectionPolicyRequired = "protection_policy_required";

/// <summary>
/// Internal to MSALs. Used in scenarios where an application is using family refresh token even though it is not part of FOCI (or vice versa). Needed to handle cases where app changes FOCI membership after being shipped. This is handled internally and doesn't need to be exposed to the calling app. Please see FOCI design document for more details.
/// </summary>
public const string ClientMismatch = "client_mismatch";

/// <summary>
/// Internal to MSALs. Indicates that device should be re-registered.
/// </summary>
public const string DeviceAuthenticationFailed = "device_authentication_failed";
}

internal static class OAuth2Value
{
public static readonly string[] ReservedScopes = { ScopeOpenId, ScopeProfile, ScopeOfflineAccess };
Expand Down
Loading

0 comments on commit 49e45f0

Please sign in to comment.