這很重要
自 2025 年 5 月 1 日起,Azure AD B2C 將不再可供新客戶購買。 在我們的常見問題中深入瞭解。
本文說明如何將 Azure Active Directory B2C (Azure AD B2C) 驗證新增至您自己的 iOS Swift 行動應用程式。 瞭解如何整合 iOS Swift 應用程式與適用於 iOS 的 Microsoft 驗證連結庫 (MSAL)。
使用本文搭配 在範例 iOS Swift 應用程式中設定驗證,將範例 iOS Swift 應用程式取代為您自己的 iOS Swift 應用程式。 完成本文中的指示之後,您的應用程式會透過 Azure AD B2C 接受登入。
先決條件
檢閱使用 Azure AD B2C 在 iOS Swift 應用程式中設定驗證範例中的必要條件和整合指示。
建立 iOS Swift 應用程式專案
如果您還沒有 iOS Swift 應用程式,請執行下列步驟來設定新的專案:
- 開啟 Xcode,然後選取 [ 檔案>新>專案]。
- 針對 iOS 應用程式,選取 [iOS>應用程式],然後選取 [ 下一步]。
- 針對 選擇您新專案的選項,請提供以下事項:
-
產品名稱,例如
MSALiOS。 -
組織識別碼,例如
contoso.com。 - 針對 介面,選取 分鏡腳本。
- 針對 生命週期,選取 UIKit 應用程式代理。
- 針對 [ 語言],選取 [ Swift]。
-
產品名稱,例如
- 選取 下一步。
- 選取要在其中建立應用程式的資料夾,然後選取 [ 建立]。
步驟 1:安裝 MSAL 程式庫
使用 CocoaPods 安裝 MSAL 程式庫。 在與專案 .xcodeproj 檔案相同的資料夾中,如果 podfile 檔案不存在,請建立空白檔案,並將它命名為 podfile。 將下列程式代碼新增至 podfile 檔案:
use_frameworks! target '<your-target-here>' do pod 'MSAL' end將
<your-target-here>取代為您的項目名稱(例如MSALiOS)。 如需詳細資訊,請參閱 Podfile 語法參考。在終端機視窗中,移至包含 Podfile 檔案的資料夾,然後執行 Pod 安裝以安裝 MSAL 連結庫。
執行
pod install命令之後, <就會建立專案名稱>.xcworkspace 檔案。 若要在 Xcode 中重載專案,請關閉 Xcode,然後開啟 <您的專案名稱>.xcworkspace 檔案。
步驟 2:設定應用程式 URL 配置
當使用者進行驗證時,Azure AD B2C 會使用 Azure AD B2C 應用程式註冊上設定的重新導向 URI,將授權碼傳送至應用程式。
MSAL 預設重新導向 URI 格式為 msauth.[Your_Bundle_Id]://auth。 例如 msauth.com.microsoft.identitysample.MSALiOS://auth,其中 msauth.com.microsoft.identitysample.MSALiOS 是 URL 配置。
在此步驟中,使用 CFBundleURLSchemes 陣列註冊 URL 配置。 您的應用程式會在 URL 架構中接聽來自 Azure AD B2C 的回呼。
在 Xcode 中,開啟 Info.plist 檔案 作為原始程式碼檔案。 在區 <dict> 段中,新增下列 XML 代碼段:
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>msauth.com.microsoft.identitysample.MSALiOS</string>
</array>
</dict>
</array>
<key>LSApplicationQueriesSchemes</key>
<array>
<string>msauthv2</string>
<string>msauthv3</string>
</array>
步驟 3:新增驗證碼
範例程式代碼是由 類別UIViewController所組成。 類別:
- 定義使用者介面的結構。
- 包含 Azure AD B2C 識別提供者的相關信息。 應用程式會使用此資訊來建立與 Azure AD B2C 的信任關係。
- 包含驗證程式代碼來驗證使用者、取得令牌,以及驗證它們。
UIViewController選擇用戶驗證的位置。 在您的專案中,將程式與 GitHub 中提供的程式代碼合併。
步驟 4:設定 iOS Swift 應用程式
新增驗證碼之後,請使用您的 Azure AD B2C 設定來設定 iOS Swift 應用程式。 Azure AD B2C 識別提供者設定是在 UIViewController 上一節中選擇的類別中設定。
若要瞭解如何設定 iOS Swift 應用程式,請參閱 使用 Azure AD B2C 在範例 iOS Swift 應用程式中設定驗證。
步驟 5:執行及測試行動應用程式
- 使用 連線 iOS 裝置的模擬器建置並執行專案。
- 選取 [登入],然後使用您的 Azure AD B2C 本機或社交帳戶註冊或登入。
- 成功驗證之後,您會在導覽列中看到您的顯示名稱。
步驟 6:自定義程式碼建置組塊
本節說明啟用 iOS Swift 應用程式驗證的程式代碼建置組塊。 它會列出 UIViewController 的方法,並討論如何自定義程式代碼。
步驟 6.1:具現化公用用戶端應用程式
公用用戶端應用程式不受信任,無法安全地保留應用程式秘密,而且它們沒有客戶端密碼。 在 viewDidLoad 中,使用公用用戶端應用程式物件具現化 MSAL。
下列 Swift 代碼段示範如何使用組態物件初始化 MSAL MSALPublicClientApplicationConfig 。
組態物件會提供 Azure AD B2C 環境的相關信息。 例如,它會提供用戶端標識碼、重新導向 URI 和授權單位,以建置對 Azure AD B2C 的驗證要求。 如需設定對象的相關信息,請參閱 設定範例行動應用程式。
do {
let signinPolicyAuthority = try self.getAuthority(forPolicy: self.kSignupOrSigninPolicy)
let editProfileAuthority = try self.getAuthority(forPolicy: self.kEditProfilePolicy)
let pcaConfig = MSALPublicClientApplicationConfig(clientId: kClientID, redirectUri: kRedirectUri, authority: signinPolicyAuthority)
pcaConfig.knownAuthorities = [signinPolicyAuthority, editProfileAuthority]
self.applicationContext = try MSALPublicClientApplication(configuration: pcaConfig)
self.initWebViewParams()
} catch {
self.updateLoggingText(text: "Unable to create application \(error)")
}
方法 initWebViewParams 會設定 互動式驗證 體驗。
下列 Swift 代碼段會初始化 webViewParameters 類別成員,並使用系統網頁檢視。 如需詳細資訊,請參閱 自定義適用於 iOS/macOS 的瀏覽器和 WebViews。
func initWebViewParams() {
self.webViewParameters = MSALWebviewParameters(authPresentationViewController: self)
self.webViewParameters?.webviewType = .default
}
步驟 6.2:啟動互動式授權要求
互動式授權要求是一種流程,系統會提示使用者使用系統 Web 檢視註冊或登入。 當使用者選取 [ 登入 ] 按鈕時,會 authorizationButton 呼叫 方法。
方法 authorizationButton 會為 MSALInteractiveTokenParameters 物件準備有關授權請求的相關資料。
acquireToken方法會使用 MSALInteractiveTokenParameters 透過系統 Web 檢視來驗證使用者。
下列代碼段示範如何啟動互動式授權要求:
let parameters = MSALInteractiveTokenParameters(scopes: kScopes, webviewParameters: self.webViewParameters!)
parameters.promptType = .selectAccount
parameters.authority = authority
applicationContext.acquireToken(with: parameters) { (result, error) in
// On error code
guard let result = result else {
self.updateLoggingText(text: "Could not acquire token: \(error ?? "No error information" as! Error)")
return
}
// On success code
self.accessToken = result.accessToken
self.updateLoggingText(text: "Access token is \(self.accessToken ?? "Empty")")
}
在使用者完成授權流程之後,不論成功或失敗,結果會傳回至方法的acquireToken。
方法acquireToken會傳回result和error物件。 使用此閉包來:
- 在驗證完成後,使用資訊更新行動應用程式UI。
- 使用存取令牌呼叫 Web API 服務。
- 處理驗證錯誤(例如,當使用者取消登入流程時)。
步驟 6.3:呼叫 Web API
若要呼叫 令牌型授權 Web API,應用程式需要有效的存取令牌。 應用程式會執行下列動作:
- 取得具有 Web API 端點所需許可權(範圍)的存取令牌。
- 使用下列格式,將存取令牌作為持有者的憑證傳遞到 HTTP 要求的授權標頭中:
Authorization: Bearer <access-token>
當使用者 以互動方式進行驗證時,應用程式會在關閉時 acquireToken 取得存取令牌。 針對後續的 Web API 呼叫,請使用靜默取得令牌 ( acquireTokenSilent ) 方法,如本節所述。
方法 acquireTokenSilent 會執行下列動作:
- 它會嘗試從令牌快取擷取具有要求範圍的存取令牌。 如果令牌存在且尚未過期,則會傳回令牌。
- 如果令牌不存在於令牌快取中或已過期,MSAL 函式庫會嘗試使用刷新令牌來取得新的存取令牌。
- 如果重新整理令牌不存在或已過期,則會傳回例外狀況。 在此情況下,您應該提示使用者 以互動方式登入。
下列代碼段示範如何取得存取權杖:
do {
// Get the authority using the sign-in or sign-up user flow
let authority = try self.getAuthority(forPolicy: self.kSignupOrSigninPolicy)
// Get the current account from the application context
guard let thisAccount = try self.getAccountByPolicy(withAccounts: applicationContext.allAccounts(), policy: kSignupOrSigninPolicy) else {
self.updateLoggingText(text: "There is no account available!")
return
}
// Configure the acquire token silent parameters
let parameters = MSALSilentTokenParameters(scopes: kScopes, account:thisAccount)
parameters.authority = authority
parameters.loginHint = "username"
// Acquire token silent
self.applicationContext.acquireTokenSilent(with: parameters) { (result, error) in
if let error = error {
let nsError = error as NSError
// interactionRequired means we need to ask the user to sign in. This usually happens
// when the user's Refresh Token is expired or if the user has changed their password
// among other possible reasons.
if (nsError.domain == MSALErrorDomain) {
if (nsError.code == MSALError.interactionRequired.rawValue) {
// Start an interactive authorization code
// Notice we supply the account here. This ensures we acquire token for the same account
// as we originally authenticated.
...
}
}
self.updateLoggingText(text: "Could not acquire token: \(error)")
return
}
guard let result = result else {
self.updateLoggingText(text: "Could not acquire token: No result returned")
return
}
// On success, set the access token to the accessToken class member.
// The callGraphAPI method uses the access token to call a web API
self.accessToken = result.accessToken
...
}
} catch {
self.updateLoggingText(text: "Unable to construct parameters before calling acquire token \(error)")
}
方法 callGraphAPI 會擷取存取令牌並呼叫Web API,如下所示:
@objc func callGraphAPI(_ sender: UIButton) {
guard let accessToken = self.accessToken else {
self.updateLoggingText(text: "Operation failed because could not find an access token!")
return
}
let sessionConfig = URLSessionConfiguration.default
sessionConfig.timeoutIntervalForRequest = 30
let url = URL(string: self.kGraphURI)
var request = URLRequest(url: url!)
request.setValue("Bearer \(accessToken)", forHTTPHeaderField: "Authorization")
let urlSession = URLSession(configuration: sessionConfig, delegate: self, delegateQueue: OperationQueue.main)
self.updateLoggingText(text: "Calling the API....")
urlSession.dataTask(with: request) { data, response, error in
guard let validData = data else {
self.updateLoggingText(text: "Could not call API: \(error ?? "No error information" as! Error)")
return
}
let result = try? JSONSerialization.jsonObject(with: validData, options: [])
guard let validResult = result as? [String: Any] else {
self.updateLoggingText(text: "Nothing returned from API")
return
}
self.updateLoggingText(text: "API response: \(validResult.debugDescription)")
}.resume()
}
步驟 6.4:註銷使用者
使用 MSAL 註銷會從應用程式移除使用者的所有已知資訊。 使用註銷方法註銷使用者並更新UI。 例如,您可以隱藏受保護的UI元素、隱藏註銷按鈕,或顯示登入按鈕。
下列代碼段示範如何註銷使用者:
@objc func signoutButton(_ sender: UIButton) {
do {
let thisAccount = try self.getAccountByPolicy(withAccounts: applicationContext.allAccounts(), policy: kSignupOrSigninPolicy)
if let accountToRemove = thisAccount {
try applicationContext.remove(accountToRemove)
} else {
self.updateLoggingText(text: "There is no account to signing out!")
}
...
} catch {
self.updateLoggingText(text: "Received error signing out: \(error)")
}
}
後續步驟
學習怎麼做: