处理 MSAL for iOS/macOS 中的错误和异常Handle errors and exceptions in MSAL for iOS/macOS

本文概述不同类型的错误,并提供有关处理常见登录错误的建议。This article gives an overview of the different types of errors and recommendations for handling common sign-in errors.

MSAL 错误处理基础知识MSAL error handling basics

Microsoft 身份验证库 (MSAL) 中的异常旨在帮助应用开发人员进行故障排除,而不是用于显示给最终用户。Exceptions in Microsoft Authentication Library (MSAL) are intended for app developers to troubleshoot, not for displaying to end users. 异常消息未经本地化。Exception messages are not localized.

处理异常和错误时,可以使用异常类型本身和错误代码来区分不同的异常。When processing exceptions and errors, you can use the exception type itself and the error code to distinguish between exceptions. 有关错误代码的列表,请参阅 Azure AD 身份验证和授权错误代码For a list of error codes, see Azure AD Authentication and authorization error codes.

在登录体验期间,可能会遇到有关许可、条件访问(MFA、设备管理、基于位置的限制)、令牌颁发和兑换以及用户属性的错误。During the sign-in experience, you may encounter errors about consents, Conditional Access (MFA, Device Management, Location-based restrictions), token issuance and redemption, and user properties.

以下部分提供了有关应用的错误处理的更多详细信息。The following section provides more details about error handling for your app.

MSAL for iOS/macOS 中的错误处理Error handling in MSAL for iOS/macOS

MSALError 枚举中列出了适用于 iOS 和 macOS 的 MSAL 完整错误列表。The complete list of MSAL for iOS and macOS errors is listed in MSALError enum.

生成的所有 MSAL 错误都以 MSALErrorDomain 域返回。All MSAL produced errors are returned with MSALErrorDomain domain.

对于系统错误,MSAL 会从系统 API 返回原始 NSErrorFor system errors, MSAL returns the original NSError from the system API. 例如,如果由于缺少网络连接,令牌获取失败,则 MSAL 会返回包含 NSURLErrorDomain 域和 NSURLErrorNotConnectedToInternet 代码的错误。For example, if token acquisition fails because of a lack of network connectivity, MSAL returns an error with the NSURLErrorDomain domain and NSURLErrorNotConnectedToInternet code.

建议在客户端上至少处理以下两个 MSAL 错误:We recommend that you handle at least the following two MSAL errors on the client side:

  • MSALErrorInteractionRequired:用户必须执行交互式请求。MSALErrorInteractionRequired: The user must do an interactive request. 很多条件都可能会导致此错误,例如身份验证会话过期或需要额外的身份验证要求。There are many conditions that can lead to this error such as an expired authentication session or the need for additional authentication requirements. 调用 MSAL 交互式令牌获取 API 以进行恢复。Call the MSAL interactive token acquisition API to recover.

  • MSALErrorServerDeclinedScopes:部分或全部作用域已被拒绝。MSALErrorServerDeclinedScopes: Some or all scopes were declined. 决定是继续使用唯一允许的作用域,还是停止登录过程。Decide whether to continue with only the granted scopes, or stop the sign-in process.

备注

MSALInternalError 枚举应仅用于引用和调试。The MSALInternalError enum should only be used for reference and debugging. 请勿尝试在运行时自动处理这些错误。Do not try to automatically handle these errors at runtime. 如果应用遇到属于 MSALInternalError 的任何错误,则可能希望显示一条面向用户的通用消息,说明所发生的情况。If your app encounters any of the errors that fall under MSALInternalError, you may want to show a generic user facing message explaining what happened.

例如,MSALInternalErrorBrokerResponseNotReceived 表示用户未完成身份验证并已手动返回到应用。For example, MSALInternalErrorBrokerResponseNotReceived means that user didn't complete authentication and manually returned to the app. 在这种情况下,应用应显示一条通用错误消息,说明身份验证未完成,并建议他们尝试再次进行身份验证。In this case, your app should show a generic error message explaining that authentication didn't complete and suggest that they try to authenticate again.

以下 Objective-C 示例代码演示了处理某些常见错误状况的最佳做法。The following Objective-C sample code demonstrates best practices for handling some common error conditions.

    MSALInteractiveTokenParameters *interactiveParameters = ...;
    MSALSilentTokenParameters *silentParameters = ...;
    
    MSALCompletionBlock completionBlock;
    __block __weak MSALCompletionBlock weakCompletionBlock;
    
    weakCompletionBlock = completionBlock = ^(MSALResult *result, NSError *error)
    {
        if (!error)
        {
            // Use result.accessToken
            NSString *accessToken = result.accessToken;
            return;
        }
        
        if ([error.domain isEqualToString:MSALErrorDomain])
        {
            switch (error.code)
            {
                case MSALErrorInteractionRequired:
                {
                    // Interactive auth will be required
                    [application acquireTokenWithParameters:interactiveParameters
                                            completionBlock:weakCompletionBlock];
                    
                    break;
                }
                    
                case MSALErrorServerDeclinedScopes:
                {
                    // These are list of granted and declined scopes.
                    NSArray *grantedScopes = error.userInfo[MSALGrantedScopesKey];
                    NSArray *declinedScopes = error.userInfo[MSALDeclinedScopesKey];
                    
                    // To continue acquiring token for granted scopes only, do the following
                    silentParameters.scopes = grantedScopes;
                    [application acquireTokenSilentWithParameters:silentParameters
                                                  completionBlock:weakCompletionBlock];
                    
                    // Otherwise, instead, handle error fittingly to the application context
                    break;
                }
                    
                case MSALErrorServerProtectionPoliciesRequired:
                {
                    // Integrate the Intune SDK and call the
                    // remediateComplianceForIdentity:silent: API.
                    // Handle this error only if you integrated Intune SDK.
                                        
                    break;
                }
                    
                case MSALErrorUserCanceled:
                {
                    // The user cancelled the web auth session.
                    // You may want to ask the user to try again.
                    // Handling of this error is optional.
                    
                    break;
                }
                    
                case MSALErrorInternal:
                {
                    // Log the error, then inspect the MSALInternalErrorCodeKey
                    // in the userInfo dictionary.
                    // Display generic error message to the end user
                    // More detailed information about the specific error
                    // under MSALInternalErrorCodeKey can be found in MSALInternalError enum.
                    NSLog(@"Failed with error %@", error);
                    
                    break;
                }
                    
                default:
                    NSLog(@"Failed with unknown MSAL error %@", error);
                    
                    break;
            }
            
            return;
        }
        
        // Handle no internet connection.
        if ([error.domain isEqualToString:NSURLErrorDomain] && error.code == NSURLErrorNotConnectedToInternet)
        {
            NSLog(@"No internet connection.");
            return;
        }
        
        // Other errors may require trying again later,
        // or reporting authentication problems to the user.
        NSLog(@"Failed with error %@", error);
    };
    
    // Acquire token silently
    [application acquireTokenSilentWithParameters:silentParameters
                                  completionBlock:completionBlock];

     // or acquire it interactively.
     [application acquireTokenWithParameters:interactiveParameters
                             completionBlock:completionBlock];
    let interactiveParameters: MSALInteractiveTokenParameters = ...
    let silentParameters: MSALSilentTokenParameters = ...
            
    var completionBlock: MSALCompletionBlock!
    completionBlock = { (result: MSALResult?, error: Error?) in
                
        if let result = result
        {
            // Use result.accessToken
            let accessToken = result.accessToken
            return
        }

        guard let error = error as NSError? else { return }

        if error.domain == MSALErrorDomain, let errorCode = MSALError(rawValue: error.code)
        {
            switch errorCode
            {
                case .interactionRequired:
                    // Interactive auth will be required
                    application.acquireToken(with: interactiveParameters, completionBlock: completionBlock)

                case .serverDeclinedScopes:
                    let grantedScopes = error.userInfo[MSALGrantedScopesKey]
                    let declinedScopes = error.userInfo[MSALDeclinedScopesKey]

                    if let scopes = grantedScopes as? [String] {
                        silentParameters.scopes = scopes
                        application.acquireTokenSilent(with: silentParameters, completionBlock: completionBlock)
                    }
                        
                    case .serverProtectionPoliciesRequired:
                        // Integrate the Intune SDK and call the
                        // remediateComplianceForIdentity:silent: API.
                        // Handle this error only if you integrated Intune SDK.
                        
                        break
                        
                    case .userCanceled:
                       // The user cancelled the web auth session.
                       // You may want to ask the user to try again.
                       // Handling of this error is optional.
                       break
                        
                    case .internal:
                        // Log the error, then inspect the MSALInternalErrorCodeKey
                        // in the userInfo dictionary.
                        // Display generic error message to the end user
                        // More detailed information about the specific error
                        // under MSALInternalErrorCodeKey can be found in MSALInternalError enum.
                        print("Failed with error \(error)");
                        
                    default:
                        print("Failed with unknown MSAL error \(error)")
            }
        }
                
        // Handle no internet connection.
        if error.domain == NSURLErrorDomain && error.code == NSURLErrorNotConnectedToInternet
        {
            print("No internet connection.")
            return
        }
                
        // Other errors may require trying again later,
        // or reporting authentication problems to the user.
        print("Failed with error \(error)");    
    }
   
    // Acquire token silently
    application.acquireToken(with: interactiveParameters, completionBlock: completionBlock)
 
    // or acquire it interactively.
    application.acquireTokenSilent(with: silentParameters, completionBlock: completionBlock)

条件访问和声明质询Conditional access and claims challenges

以无提示方式获取令牌时,如果你尝试访问的 API 需要条件访问声明质询(例如 MFA 策略),则应用程序可能会收到错误。When getting tokens silently, your application may receive errors when a Conditional Access claims challenge such as MFA policy is required by an API you're trying to access.

处理此错误的模式是使用 MSAL 以交互方式获取令牌。The pattern for handling this error is to interactively acquire a token using MSAL. 这会提示用户,并为他们提供机会来满足所需的条件访问策略。This prompts the user and gives them the opportunity to satisfy the required Conditional Access policy.

在某些情况下调用需要条件访问的 API 时,API 返回的错误中可能会包含声明质询。In certain cases when calling an API requiring Conditional Access, you can receive a claims challenge in the error from the API. 例如,如果条件访问策略要求使用托管设备 (Intune),则错误将类似于 AADSTS53000:需要管理你的设备才能访问此资源For instance if the Conditional Access policy is to have a managed device (Intune) the error will be something like AADSTS53000: Your device is required to be managed to access this resource or something similar. 在这种情况下,可以在令牌获取调用中传递声明,使系统提示用户,以满足相应的策略。In this case, you can pass the claims in the acquire token call so that the user is prompted to satisfy the appropriate policy.

借助适用于 iOS 和 macOS 的 MSAL,你可以在交互式和静默令牌获取方案中请求特定的声明。MSAL for iOS and macOS allows you to request specific claims in both interactive and silent token acquisition scenarios.

要请求自定义声明,请在 MSALSilentTokenParametersMSALInteractiveTokenParameters 中指定 claimsRequestTo request custom claims, specify claimsRequest in MSALSilentTokenParameters or MSALInteractiveTokenParameters.

有关详细信息,请参阅使用适用于 iOS 和 macOS 的 MSAL 请求自定义声明See Request custom claims using MSAL for iOS and macOS for more info.

出现错误和异常后重试Retrying after errors and exceptions

在调用 MSAL 时,应实现自己的重试策略。You're expected to implement your own retry policies when calling MSAL. MSAL 会对 Azure AD 服务进行 HTTP 调用,并且有时会发生失败。MSAL makes HTTP calls to the Azure AD service, and occasionally failures can occur. 例如,网络可能出现故障,或者服务器发生过载。For example the network can go down or the server is overloaded.

HTTP 429HTTP 429

如果过多的请求导致服务令牌服务器 (STS) 过载,Retry-After 响应字段中将返回 HTTP 错误 429,并提示可在多长时间后重试。When the Service Token Server (STS) is overloaded with too many requests, it returns HTTP error 429 with a hint about how long until you can try again in the Retry-After response field.

后续步骤Next steps

请考虑启用 MSAL for iOS/macOS 中的日志记录,以帮助你诊断和调试问题。Consider enabling Logging in MSAL for iOS/macOS to help you diagnose and debug issues.