適用於 Android 的 ADAL 至 MSAL 移轉指南

本文將特別說明在將使用 Azure Active Directory 驗證程式庫 (ADAL) 的應用程式,要移轉為使用 Microsoft 驗證程式庫 (MSAL) 時,所需進行的變更。

差異摘要

ADAL 適用於 Azure Active Directory 1.0 版端點。 Microsoft 驗證程式庫 (MSAL) 可搭配 Microsoft 身分識別平台一起使用 -- 先前稱為 Azure Active Directory 2.0 版端點。 Microsoft 身分識別平台與 Azure Active Directory 1.0 版的差異在於:

支援:

  • 組織身分識別 (Azure Active Directory)

  • 非組織身分識別,例如 Outlook.com、Xbox Live 等等

  • (僅 Azure AD B2C) 使用 Google、Facebook、Twitter 和 Amazon 的同盟登入

  • 與下列標準相容:

    • OAuth 2.0 版
    • OpenID Connect (OIDC)

MSAL 公用 API 引進重要的變更,包括:

  • 用來存取權杖的新模型:
    • ADAL 可透過表示伺服器的 AuthenticationContext 來存取權杖。 MSAL 可透過表示用戶端的 PublicClientApplication 來存取權杖。 用戶端開發人員不需要為需要互動的每個授權單位建立新 PublicClientApplication 的執行個體。 僅需要一個 PublicClientApplication 設定。
    • 除了資源識別碼之外,還支援使用範圍來要求存取權杖。
    • 支援增量同意。 開發人員可以要求範圍,因為使用者會在應用程式中存取更多功能,包括應用程式註冊期間未包含的功能。
    • 在執行階段不會再驗證授權單位。 相反地,開發人員會在開發期間宣告「已知授權單位」清單。
  • 權杖 API 變更:
    • 在 ADAL 中,AcquireToken() 會先發出無訊息要求。 若失敗,則會提出互動式要求。 這種行為會導致某些開發人員只依賴 AcquireToken,而導致使用者有時會收到輸入認證的意外提示。 MSAL 需要開發人員注意使用者何時收到 UI 提示。
      • AcquireTokenSilent 一律會產生成功或失敗的無訊息要求。
      • AcquireToken 一律會產生透過 UI 提示使用者的要求。
  • MSAL 支援從預設瀏覽器或內嵌的 Web 檢視登入:
    • 預設會使用裝置上的預設瀏覽器。 這可讓 MSAL 使用已有一或多個已登入帳戶的驗證狀態 (Cookie)。 如果沒有任何驗證狀態,在授權期間透過 MSAL 進行驗證,會導致驗證狀態 (Cookie) 的建立,以利在相同瀏覽器中使用之其他 Web 應用程式使用。
  • 新的例外狀況模型:
    • 例外狀況更清楚地定義發生的錯誤類型,以及開發人員解決此問題所需採取的動作。
  • MSAL 支援 AcquireTokenAcquireTokenSilent 呼叫的參數物件。
  • MSAL 支援以下項目的宣告式設定:
    • 用戶端識別碼,重新導向 URI。
    • 內嵌與預設瀏覽器
    • 授權單位
    • HTTP 設定,例如讀取和連接逾時

應用程式註冊和移轉至 MSAL

您不需要變更現有的應用程式註冊,就能使用 MSAL。 如果您想要利用增量/漸進式同意,您可能需要檢閱註冊,以找出您想要以增量方式要求的特定範圍。 有關範圍和增量同意的詳細資訊如下。

在入口網站的應用程式註冊中,您會看到 [API 權限] 索引標籤。這會提供應用程式目前設定為要求存取權的 API 和權限清單 (範圍)。 其也會顯示與每個 API 權限相關聯的範圍名稱清單。

使用 ADAL 和 Azure AD 第 1 版端點時,使用者在第一次使用時,會授與他們所擁有之資源的同意。 使用 MSAL 和 Microsoft 身分識別平台,就可以以增量方式要求同意。 增量同意適用於使用者可能認為是高權限的權限,如果沒有提供為什麼需要權限的清楚說明,則可能會有問題。 在 ADAL 中,這些權限可能會導致使用者放棄登入應用程式。

提示

使用「增量同意」為您的使用者提供其他內容,來瞭解應用程式需要權限的原因。

組織管理員可以同意您的應用程式代表其組織的所有成員所需的權限。 某些組織只允許管理員同意應用程式。 管理員同意需要您在應用程式註冊中包含應用程式所使用的所有 API 權限和範圍。

提示

雖然您可以使用 MSAL,為不包含在應用程式註冊中的某個項目要求範圍,但建議您更新應用程式註冊,以包含使用者可授與權限的所有資源和範圍。

從資源識別碼移轉至範圍

第一次使用時,驗證並要求所有權限的授權

如果您目前使用 ADAL,而不需要使用增量同意,開始使用 MSAL 最簡單的方式就是使用新 AcquireTokenParameter 的物件提出 acquireToken 要求,並設定資源識別碼值。

警告

不可能同時設定範圍和資源識別碼。嘗試設定兩者都會產生 IllegalArgumentException

這會導致您所使用的相同第 1 版行為。 在應用程式註冊中要求的所有權限都是在使用者第一次互動時要求的。

只在必要時驗證和要求權限

若要利用增量同意,請製作從應用程式註冊使用的權限清單 (範圍),並根據下列項目將其分成兩個清單:

  • 當使用者在登入期間第一次與應用程式互動時,您想要要求的範圍。
  • 與應用程式重要功能相關聯的權限,您也必須向使用者說明這些權限。

組織範圍之後,請依照您想要要求權杖的資源 (API) 來組織每個清單。 以及任何其他您希望使用者同時授權的範圍。

用來向 MSAL 提出要求的參數物件支援:

  • Scope:您想要要求授權並接收存取權杖的範圍清單。
  • ExtraScopesToConsent:當您要求其他資源的存取權杖時,您想要要求授權的其他範圍清單。 這份範圍清單可讓您將要求使用者授權所需的次數降到最低。 這表示較少的使用者授權或同意提示。

從 AuthenticationCoNtext 移轉至 PublicClientApplications

建立 PublicClientApplication

當您使用 MSAL 時,會將 PublicClientApplication 具現化。 此物件會建立應用程式身分識別的模型,並用來對一或多個授權單位提出要求。 使用這個物件時,您將會設定用戶端身分識別、重新導向 URI、預設授權單位、是否要使用裝置瀏覽器與內嵌的 Web 檢視、記錄等級等等。

您可以使用 JSON,以宣告方式設定此物件,您可以在 APK 中提供此物件作為檔案,或作為資源儲存。

雖然這個物件不是單一的,但其會在內部針對互動式和無訊息要求使用共用 Executors

企業對企業

在 ADAL 中,您要求存取權杖的每個組織都需要個別的 AuthenticationContext 執行個體。 MSAL 中已不再需要這麼做。 您可以指定您想要在無訊息或互動式要求中要求權杖的授權單位。

從授權單位驗證移轉至已知授權單位

MSAL 沒有啟用或停用授權驗證的旗標。 授權驗證是 ADAL 中的一項功能,在舊版的 MSAL 中,可防止程式碼要求可能有惡意授權的權杖。 MSAL 現在會擷取 Microsoft 已知的授權清單,並將該清單與您在設定中指定的授權單位合併。

提示

如果您是 Azure 企業對消費者 (B2C) 使用者,這表示您不再需要停用授權驗證。 相反地,請在 MSAL 設定中包含每個支援的 Azure AD B2C 原則作為授權單位。

如果您嘗試使用 Microsoft 不知道的授權單位,而且該授權單位不包含在設定中,您將會收到 UnknownAuthorityException

記錄

您現在可以在設定過程中,以宣告方式設定記錄,如下所示:

"logging": {
  "pii_enabled": false,
  "log_level": "WARNING",
  "logcat_enabled": true
}

從 UserInfo 移轉至帳戶

在 ADAL 中,AuthenticationResult 會提供 UserInfo 物件,該物件會用來擷取已驗證帳戶的相關資訊。 「使用者」一詞是指「人」或「軟體代理程式」,其應用方式難以傳達某些應用程式支援的單一使用者 (人或軟體代理程式) 是否有多個帳戶。

請考慮銀行帳戶。 您在多個金融機構可能會有一個以上的帳戶。 當您開啟帳戶時,您 (使用者) 會獲得用來存取每個帳戶餘額、帳單付款等的認證,例如 ATM 卡 & PIN。 這些認證只能在發行這些認證的金融機構使用。

就像金融機構的帳戶一樣,Microsoft 身分識別平台中的帳戶是使用認證來存取。 這些認證是透過 Microsoft 註冊,或是由 Microsoft 發行。 或由 Microsoft 代表組織。

Microsoft 身分識別平台與金融機構的不同之處在於,Microsoft 身分識別平台所提供的架構可讓使用者使用一個帳戶與其相關聯的認證,來存取屬於多個個人和組織的資源。 這就像是能夠在另一家金融機構使用某一家銀行所發行的卡片。 這之所以能運作,是因為所有有問題的組織都使用 Microsoft 身分識別平台,該平台允許在多個組織中使用一個帳戶。 以下為範例:

Sam 為 Contoso.com 工作,但管理屬於 Fabrikam.com 的 Azure 虛擬機器。 為了讓 Sam 管理 Fabrikam 的虛擬機器,他必須獲得授權才能存取那些機器。 您可以藉由將 Sam 的帳戶新增至 Fabrikam.com,並授與帳戶可讓他使用虛擬機器的角色,來授與此存取權。 這會透過 Azure 入口網站完成。

將 Sam 的 Contoso.com 帳戶新增為 Fabrikam.com 的成員,會為 Sam 在 Fabrikam.com 的 Azure Active Directory 中建立新記錄。 Sam 在 Azure Active Directory 中的記錄稱為使用者物件。 在此情況下,該使用者物件會指向 Sam 在 Contoso.com 中的使用者物件。 Sam 的 Fabrikam 使用者物件是 Sam 的本機表示法,其會用來在 Fabrikam.com 的內容中儲存與 Sam 相關聯的帳戶資訊。 在 Contoso.com 中,Sam 的職稱是資深 DevOps 顧問。 在 Fabrikam 中,Sam 的職稱是 Contractor-Virtual Machines。 在 Contoso.com 中,Sam 不負責管理虛擬機器,也沒有管理那些機器的授權。 在 Fabrikam.com 中,那是他唯一的工作職能。 但 Sam 仍只會有一組認證來追蹤由 Contoso.com 所發行的認證。

一旦成功 acquireToken 呼叫之後,您就會看到可在後續 acquireTokenSilent 要求中使用之 IAccount 物件的參考。

IMultiTenantAccount

如果您的應用程式會從代表帳戶的每個租用戶存取有關帳戶的宣告,您可以將物件 IAccount 轉換為 IMultiTenantAccount。 此介面提供依租用戶識別碼的對應 ITenantProfiles,可讓您在每個要求權杖的租用戶 (相對於目前的帳戶) 中存取屬於帳戶的宣告。

位於根 IAccountIMultiTenantAccount 的宣告一律會包含來自主租用戶的宣告。 如果您尚未對主租用戶中的權杖提出要求,此集合將會是空的。

其他變更

使用新的 AuthenticationCallback

// Existing ADAL Interface
public interface AuthenticationCallback<T> {

    /**
     * This will have the token info.
     *
     * @param result returns <T>
     */
    void onSuccess(T result);

    /**
     * Sends error information. This can be user related error or server error.
     * Cancellation error is AuthenticationCancelError.
     *
     * @param exc return {@link Exception}
     */
    void onError(Exception exc);
}
// New Interface for Interactive AcquireToken
public interface AuthenticationCallback {

    /**
     * Authentication finishes successfully.
     *
     * @param authenticationResult {@link IAuthenticationResult} that contains the success response.
     */
    void onSuccess(final IAuthenticationResult authenticationResult);

    /**
     * Error occurs during the authentication.
     *
     * @param exception The {@link MsalException} contains the error code, error message and cause if applicable. The exception
     *                  returned in the callback could be {@link MsalClientException}, {@link MsalServiceException}
     */
    void onError(final MsalException exception);

    /**
     * Will be called if user cancels the flow.
     */
    void onCancel();
}

// New Interface for Silent AcquireToken
public interface SilentAuthenticationCallback {

    /**
     * Authentication finishes successfully.
     *
     * @param authenticationResult {@link IAuthenticationResult} that contains the success response.
     */
    void onSuccess(final IAuthenticationResult authenticationResult);

    /**
     * Error occurs during the authentication.
     *
     * @param exception The {@link MsalException} contains the error code, error message and cause if applicable. The exception
     *                  returned in the callback could be {@link MsalClientException}, {@link MsalServiceException} or
     *                  {@link MsalUiRequiredException}.
     */
    void onError(final MsalException exception);
}

移轉至新的例外狀況

在 ADAL 中,有一種例外狀況 (AuthenticationException),其中包含用來擷取 ADALError 列舉值的方法。 在 MSAL 中,有例外狀況的階層,每個都有一組相關的特定錯誤碼。

例外狀況 描述
MsalArgumentException 如果有一或多個輸入引數無效,則擲回。
MsalClientException 如果錯誤為用戶端,則擲回。
MsalDeclinedScopeException 如果伺服器拒絕一或多個要求的範圍,則會擲回。
MsalException MSAL 擲回的預設檢查例外狀況。
MsalIntuneAppProtectionPolicyRequiredException 如果資源已啟用 MAMCA 保護原則,則擲回。
MsalServiceException 如果錯誤為伺服器端,則擲回。
MsalUiRequiredException 如果無法以無訊息方式重新整理權杖,則會擲回。
MsalUserCancelException 如果使用者取消驗證流程,則擲回。

ADALError 至 MsalException 轉譯

如果您要在 ADAL 中擷取這些錯誤… …擷取這些 MSAL 例外狀況:
沒有對等的 ADALError MsalArgumentException
  • ADALError.ANDROIDKEYSTORE_FAILED
  • ADALError.AUTH_FAILED_USER_MISMATCH
  • ADALError.DECRYPTION_FAILED
  • ADALError.DEVELOPER_AUTHORITY_CAN_NOT_BE_VALIDED
  • ADALError.DEVELOPER_AUTHORITY_IS_NOT_VALID_INSTANCE
  • ADALError.DEVELOPER_AUTHORITY_IS_NOT_VALID_URL
  • ADALError.DEVICE_CONNECTION_IS_NOT_AVAILABLE
  • ADALError.DEVICE_NO_SUCH_ALGORITHM
  • ADALError.ENCODING_IS_NOT_SUPPORTED
  • ADALError.ENCRYPTION_ERROR
  • ADALError.IO_EXCEPTION
  • ADALError.JSON_PARSE_ERROR
  • ADALError.NO_NETWORK_CONNECTION_POWER_OPTIMIZATION
  • ADALError.SOCKET_TIMEOUT_EXCEPTION
MsalClientException
沒有對等的 ADALError MsalDeclinedScopeException
  • ADALError.APP_PACKAGE_NAME_NOT_FOUND
  • ADALError.BROKER_APP_VERIFICATION_FAILED
  • ADALError.PACKAGE_NAME_NOT_FOUND
MsalException
沒有對等的 ADALError MsalIntuneAppProtectionPolicyRequiredException
  • ADALError.SERVER_ERROR
  • ADALError.SERVER_INVALID_REQUEST
MsalServiceException
  • ADALError.AUTH_REFRESH_FAILED_PROMPT_NOT_ALLOWED
MsalUiRequiredException
沒有對等的 ADALError MsalUserCancelException

ADAL 記錄至 MSAL 記錄

// Legacy Interface
    StringBuilder logs = new StringBuilder();
    Logger.getInstance().setExternalLogger(new ILogger() {
            @Override
            public void Log(String tag, String message, String additionalMessage, LogLevel logLevel, ADALError errorCode) {
                logs.append(message).append('\n');
            }
        });
// New interface
  StringBuilder logs = new StringBuilder();
  Logger.getInstance().setExternalLogger(new ILoggerCallback() {
      @Override
      public void log(String tag, Logger.LogLevel logLevel, String message, boolean containsPII) {
          logs.append(message).append('\n');
      }
  });

// New Log Levels:
public enum LogLevel
{
    /**
     * Error level logging.
     */
    ERROR,
    /**
     * Warning level logging.
     */
    WARNING,
    /**
     * Info level logging.
     */
    INFO,
    /**
     * Verbose level logging.
     */
    VERBOSE
}