處理 iOS/macOS MSAL 中的錯誤與異常

本文概述了不同類型的錯誤,並提出處理常見登入錯誤的建議。

MSAL 錯誤處理基礎

Microsoft 驗證資源庫(MSAL)中的例外是為了讓應用程式開發者進行故障排除,而非顯示給最終使用者。 例外訊息並非本地化。

處理例外與錯誤時,您可以利用例外類型本身及錯誤代碼來區分例外。 有關錯誤代碼列表,請參閱 Microsoft Entra 認證與授權錯誤代碼。

在登入過程中,您可能會遇到關於同意、條件存取(MFA、裝置管理、基於位置的限制)、令牌發放與兌換,以及使用者屬性等錯誤。

以下章節將提供更多關於您應用程式錯誤處理的詳細資訊。

iOS / macOS MSAL 中的錯誤處理

iOS 與 macOS 的完整 MSAL 錯誤清單列在 MSALError 列舉中。

所有 MSAL 產生的錯誤都會以 MSALErrorDomain 域回傳。

系統錯誤時,MSAL 會回傳系統 API 的原始 NSError 檔案。 例如,若因網路連線不足導致令牌擷取失敗,MSAL 會回傳網域 NSURLErrorDomain 與 NSURLErrorNotConnectedToInternet 程式碼錯誤。

我們建議您在用戶端至少處理下列兩個 MSAL 錯誤:

  • MSALErrorInteractionRequired使用者必須進行互動式請求。 造成此錯誤的條件有很多,例如認證會話過期或需要額外的認證要求。 呼叫 MSAL 互動式憑證取得 API 以進行恢復。

  • MSALErrorServerDeclinedScopes:部分或全部範圍遭拒絕。 決定是否只使用已授權的範圍,或停止登入流程。

Note

MSALInternalError列舉應該只用於參考和除錯。 不要試圖在執行時自動處理這些錯誤。 如果你的應用程式遇到任何屬於 MSALInternalError 的錯誤,你可能會想要顯示一則通用的使用者提示訊息,解釋發生了什麼事。

例如,代表 MSALInternalErrorBrokerResponseNotReceived 使用者沒有完成驗證,而是手動返回應用程式。 在這種情況下,你的應用程式應該會顯示一個通用錯誤訊息,說明驗證未完成,並建議他們嘗試重新驗證。

以下 Objective-C 範例程式碼展示了處理一些常見錯誤狀況的最佳實務。

    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.
                    // See more info here: https://aka.ms/intuneMAMSDK
                    
                    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.
                        // See more info here: https://aka.ms/intuneMAMSDK
                        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)

條件存取與理賠挑戰

當以靜默方式取得權杖時,如果您嘗試存取的 API 要求 條件式存取宣告挑戰(例如 MFA 原則),您的應用程式可能會收到錯誤。

處理此錯誤的模式是透過 MSAL 互動式取得令牌。 這會提示使用者,並給予他們機會滿足所需的條件存取政策。

在某些情況下,當呼叫需要條件存取的 API 時,你可能會在錯誤中收到 API 的理據挑戰。 例如,如果條件存取政策是要有受管理的裝置(Intune),錯誤會是像 AADSTS53000:你的裝置必須被管理才能存取此資源 或類似的訊息。 在這種情況下,你可以在取得權杖的呼叫中傳遞宣告,系統便會提示使用者滿足適當的原則。

iOS 和 macOS 的 MSAL 允許你在互動式或無聲令牌取得情境下申請特定權利要求。

若要要求自訂宣告,請在 MSALSilentTokenParameters 或 MSALInteractiveTokenParameters 中指定 claimsRequest。

更多資訊請參閱 iOS 和 macOS 使用 MSAL 請求自訂理賠 。

錯誤與異常後重試

你在打電話給 MSAL 時,預期要自己執行重試政策。 MSAL 會對 Microsoft Entra 服務進行 HTTP 呼叫,偶爾會發生故障。 例如網路可能會當機或伺服器過載。

HTTP 429

當服務權杖伺服器(STS)因請求過多而超載時,會在回應欄位回傳 HTTP 錯誤 429,並提示你還要多久才能再 Retry-After 嘗試。

下一步

建議在 iOS/macOS 啟用 MSAL 的登入 功能,幫助你診斷和除錯問題。