本文重點說明,將使用 Azure Active Directory 認證庫(ADAL)的應用程式遷移至 Microsoft 驗證資源庫(MSAL)時,你需要做的變更。
差異亮點
ADAL 可搭配 Azure AD v1.0 端點運作。 Microsoft 驗證資源庫(MSAL)與 Microsoft 身分識別平台 相容,該平台前身為 Azure AD v2.0 端點。 Microsoft 身分識別平台 與 Azure AD v1.0 的不同之處在於:
支援:
組織身份(Microsoft Entra ID)
非組織身份,如 Outlook.com、Xbox Live 等
(僅限 Azure AD B2C 使用)與 Google、Facebook、X 和 Amazon 的聯邦登入
標準是否相容於:
- OAuth v2.0
- OpenID Connect(OIDC)
MSAL 公開 API 引入了重要變更,包括:
- 一種新的代幣存取模式:
- ADAL 透過
AuthenticationContext代表伺服器的 ,提供對標記的存取。 MSAL 透過代表用戶端的PublicClientApplication來存取權杖。 客戶端開發者不需要為每個需要互動的權威建立新PublicClientApplication實例。 只需要一個PublicClientApplication設定即可。 - 支援除了資源識別碼之外,也可使用範圍來要求存取權杖。
- 支持漸進式同意。 開發者可以隨著使用者在應用程式中存取越來越多功能,而要求更多權限範圍,包括應用程式註冊時未納入的功能所需權限範圍。
- 權限不再在運行時被驗證。 開發商在開發過程中會公布一份「已知權威」名單。
- ADAL 透過
- 代幣 API 變更:
- 在 ADAL 中,
AcquireToken()首先提出無聲請求。 若無法做到,系統會發出互動式要求。 這種行為導致部分開發者僅AcquireToken依賴 ,導致使用者有時會意外被要求輸入憑證。 MSAL 要求開發者有意識地決定使用者何時收到 UI 提示。-
AcquireTokenSilent一律會發出靜默要求,結果不是成功,就是失敗。 -
AcquireToken總是會導致系統透過 UI 提示使用者的要求。
-
- 在 ADAL 中,
- MSAL 支援從預設瀏覽器或嵌入式網頁檢視登入:
- 預設情況下,裝置會使用預設瀏覽器。 這讓 MSAL 可以使用認證狀態(cookie),而這些狀態可能已經存在於一個或多個已登入帳號。 如果不存在任何驗證狀態,則在授權過程中透過 MSAL 進行驗證,會建立驗證狀態(Cookie),供其他也會在同一瀏覽器中使用的 Web 應用程式使用。
- 新例外模型:
- 例外更清楚定義了發生的錯誤類型,以及開發者需要做什麼來解決。
- MSAL 支援在
AcquireToken和AcquireTokenSilent呼叫中使用參數物件。 - MSAL 支援下列項目的宣告式設定:
- 客戶 ID,重定向 URI。
- 嵌入式瀏覽器與預設瀏覽器
- 當局
- HTTP 設定如讀取與連線逾時
你的應用程式註冊與遷移到 MSAL
你不需要更改現有的應用程式註冊就能使用 MSAL。 如果你想利用漸進式/漸進式同意,可能需要檢視註冊表,以確定你想要逐步申請的具體範圍。 以下將提供有關權限範圍和逐步同意的更多資訊。
在你的應用程式註冊中,你會看到一個 API 權限 標籤。這會列出你應用程式目前設定以請求存取權限的 API 和權限(範圍)。 它也會顯示與每個 API 權限相關的範圍名稱清單。
使用者同意
在 ADAL 和 Azure AD v1.0 端,使用者對自己擁有資源的同意是在首次使用時就被授予。 使用 MSAL 與 Microsoft 身分識別平台,可以逐步請求同意。 對於某些權限,漸進式同意很有用,尤其是當使用者可能將其視為高敏感度權限,或是在未清楚說明為何需要這些權限時,可能會提出疑問的情況。 在 ADAL 中,這些權限可能導致使用者放棄登入你的應用程式。
Tip
使用漸進同意,為使用者提供更多背景說明為何你的應用程式需要權限。
系統管理員同意
組織管理員可以代表所有成員同意您的應用程式所需的權限。 有些組織只允許管理員同意申請。 管理員同意要求你在應用程式註冊時包含所有應用程式使用的 API 權限和範圍。
Tip
即使你可以用 MSAL 申請範圍,針對應用程式註冊中未包含的內容,我們建議你更新應用程式註冊,包含使用者可能授權的所有資源與範圍。
從資源 ID 移轉至範圍
首次使用時驗證並請求所有權限授權
如果你目前使用 ADAL,且不需要使用增量同意,最簡單的使用 MSAL 方法是用新acquireToken物件提出AcquireTokenParameter請求並設定資源 ID 值。
注意事項
無法同時設定範圍和資源 ID。嘗試同時設定兩者會得到 IllegalArgumentException。
這會產生與你已經習慣的相同 v1 行為。 你在應用程式註冊時要求的所有權限,都是在使用者第一次互動時向他們請求的。
僅在必要時驗證並請求權限
為了善用增量同意,請列出你的應用程式從應用程式註冊中使用的權限(範圍),並根據以下條件將它們組織成兩個清單:
- 在使用者首次於登入期間與你的應用程式互動時,你想要請求哪些權限範圍?
- 與應用程式重要功能相關的權限,你也需要向使用者說明。
當你組織好範圍後,依照你想申請令牌的資源(API)來組織每個清單。 以及你希望使用者同時授權的任何其他權限範圍。
用來向 MSAL 請求的參數物件支援:
-
Scope: 你想要申請授權並獲得存取權杖的範圍清單。 -
ExtraScopesToConsent: 一份額外的範圍清單,當您在為其他資源申請存取權杖時,想要申請授權。 這份範圍清單能讓你減少申請使用者授權的次數。 這代表用戶授權或同意提示會減少。
從 AuthenticationContext 遷移到 PublicClientApplications
建立 PublicClientApplication
當你使用 MSAL 時,你會實例化一個 PublicClientApplication. 這個物件會模擬你的應用程式身份,並用來向一個或多個權威機構提出請求。 使用這個物件,你可以設定客戶端身份、重定向 URI、預設權限、要使用裝置瀏覽器還是嵌入式網頁檢視、日誌層級等等。
你可以用 JSON 宣告式配置這個物件,你可以把它當成檔案提供,或是把它當作資源儲存在你的 APK 裡。
雖然此物件並非單例,但在內部它同時使用 shared Executors 來處理互動式與靜默式請求。
企業對企業
在 ADAL 中,你向其請求存取權杖的每個組織都需要一個獨立的 AuthenticationContext。 在 MSAL 中,這已不再是必要條件。 你可以指定你想從哪個權限請求代幣,作為無聲或互動請求的一部分。
從權威驗證遷移到已知權威
MSAL 沒有啟用或停用權威驗證的旗標。 權威驗證是 ADAL 中的一項功能,在 MSAL 早期版本中,能防止你的程式碼向潛在惡意權威請求權杖。 MSAL 現在會取得 Microsoft 已知的權威清單,並將該清單與你設定中的權威合併。
Tip
如果你是 Azure 企業對消費者(B2C)使用者,這表示你不必再停用權威驗證。 相反地,請將你支援的每一項 Azure AD B2C 政策都納入 MSAL 設定中的權威。 請注意,自 2025 年 5 月 1 日起,Azure AD B2C 將不再對新客戶開放購買。 欲了解更多,請參閱常見問題中的Azure AD B2C是否仍可購買?。
如果您嘗試使用 Microsoft 不認識,且未包含在您的設定中的授權單位,您會收到UnknownAuthorityException。
Logging
你現在可以宣告式地將日誌設定為設定的一部分,就像這樣:
"logging": {
"pii_enabled": false,
"log_level": "WARNING",
"logcat_enabled": true
}
從 UserInfo 遷移到 Account。
在 ADAL 中,AuthenticationResult 會提供 UserInfo 物件,用來擷取已驗證帳戶的相關資訊。 「使用者」一詞原本可指人類或軟體代理,但對這個詞的使用方式,讓人難以清楚傳達:某些應用程式支援單一使用者(無論是人類或軟體代理)擁有多個帳號。
考慮設立銀行帳戶。 你可能在多家金融機構擁有多個帳戶。 當你開戶時,你(使用者)會被發放憑證,例如提款卡和密碼,用來查詢你的餘額、帳單支付等,適用於每個帳戶。 這些憑證只能在核發憑證的金融機構使用。
類比地,就像金融機構的帳戶一樣,Microsoft 身分識別平台 中的帳戶是透過憑證存取的。 這些憑證要麼是Microsoft註冊的,要麼由核發。 或者由 Microsoft 代表某個組織。
在這個類比中,Microsoft 身分識別平台 與金融機構的不同之處在於,Microsoft 身分識別平台 提供了一個框架,允許使用者使用一個帳號及其相關的憑證,存取屬於多個個人和組織的資源。 這就像能在另一家金融機構使用一張銀行發行的卡一樣。 這之所以有效,是因為所有相關組織都使用 Microsoft 身分識別平台,允許一個帳號跨多個組織使用。 以下為範例:
Sam 為 Contoso.com 工作,但管理Azure Fabrikam.com 的虛擬機。 Sam 要管理 Fabrikam 的虛擬機器,必須獲得授權才能存取它們。 此存取權限可透過將 Sam 的帳號加入 Fabrikam.com,並賦予他的帳號一個角色,讓他能操作虛擬機來獲得。 這會透過 Azure 入口網站來完成。
將 Sam 的 Contoso.com 帳號加入 Fabrikam.com 會讓 Fabrikam.com 的Microsoft Entra ID中為 Sam 建立新紀錄。 Sam 在 Microsoft Entra ID 中的紀錄被稱為使用者物件。 在這種情況下,該使用者物件會指向 Contoso.com 中 Sam 的使用者物件。 Sam 的 Fabrikam 使用者物件是 Sam 的本機表示,會用來儲存在 Fabrikam.com 情境下與 Sam 相關聯的帳戶資訊。 在 Contoso.com,Sam 的職稱是資深 DevOps 顧問。 在 Fabrikam,Sam 的職稱是 Contractor-虛擬機器。 在 Contoso.com 中,Sam 既無需負責管理虛擬機器,也未獲授權管理虛擬機器。 Fabrikam.com,這是他唯一的工作職能。 然而山姆仍然只有一套憑證需要追蹤,那就是 Contoso.com 頒發的憑證。
成功發出 acquireToken 呼叫後,你會看到可在後續 acquireTokenSilent 請求中使用的 IAccount 物件參考。
IMultiTenantAccount
如果你有一個應用程式能存取每個帳號所屬租戶的申訴,你可以將物件投射 IAccount 到 IMultiTenantAccount。 這個介面提供一張以租戶 ID 為鍵的 映射 ITenantProfiles,讓你能存取你申請令牌的每個租戶帳戶相對於目前帳戶所屬的理賠。
位於 IAccount 和 IMultiTenantAccount 根層級的宣告一律包含主租用戶的宣告。 如果你尚未在該房客內提出代幣申請,這個集合將會是空的。
其他變更
使用新的 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 中,有一系列例外的階層,每個例外都有自己對應的特定錯誤碼。
| 例外狀況 | Description |
|---|---|
MsalArgumentException |
當一個或多個輸入參數無效時,會拋出。 |
MsalClientException |
如果錯誤發生在用戶端,就會丟棄。 |
MsalDeclinedScopeException |
如果伺服器拒絕一個或多個請求的範圍,則會丟棄。 |
MsalException |
MSAL 擲回的預設受檢查例外。 |
MsalIntuneAppProtectionPolicyRequiredException |
如果該資源啟用了 MAMCA 保護政策,則會丟棄。 |
MsalServiceException |
如果錯誤發生在伺服器端,則會丟棄。 |
MsalUiRequiredException |
如果標記無法無聲刷新,則會丟棄。 |
MsalUserCancelException |
如果使用者取消了認證流程,就會被丟棄。 |
ADALErrror 到 MsalException 的翻譯
| 如果你在 ADAL 裡發現這些錯誤...... | ...注意以下 MSAL 例外: |
|---|---|
| 沒有相應的 ADALError | MsalArgumentException |
|
MsalClientException |
| 沒有相應的 ADALError | MsalDeclinedScopeException |
|
MsalException |
| 沒有相應的 ADALError | MsalIntuneAppProtectionPolicyRequiredException |
|
MsalServiceException |
|
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
}