使用 Azure AD B2C 在您自己的 iOS Swift 應用程式中啟用驗證
本文說明如何將 Azure Active Directory B2C (Azure AD B2C) 驗證新增至您自己的 iOS Swift 行動應用程式。 了解如何使用適用於 iOS 的 Microsoft 驗證程式庫 (MSAL) 來整合 iOS Swift 應用程式。
使用在範例 iOS Swift 應用程式中設定驗證搭配本文,將範例 iOS Swift 應用程式取代為您自己的 iOS Swift 應用程式。 當您完成此文章的指示之後,您的應用程式將透過 Azure AD B2C 接受登入。
必要條件
請參閱使用 Azure AD B2C 在範例 iOS Swift 應用程式中設定驗證中的必要條件和整合指示。
建立 iOS Swift 應用程式專案
如果您還沒有 iOS Swift 應用程式,請執行下列步驟來設定新的專案:
- 開啟 Xcode,然後選取 [檔案] > [新增] > [專案]。
- 針對 iOS Swift 應用程式,選取 [ 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,然後開啟<您的專案名稱>cworkspace 檔案。
步驟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 配置。 您的應用程式會接聽 Azure AD B2C 的回呼 URL 配置。
在 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
選擇使用者驗證的位置。 在您的 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 程式碼片段示範如何使用 MSALPublicClientApplicationConfig
設定物件來初始化 MSAL。
設定物件提供您 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 程式碼片段會使用 System WebView 來初始化 webViewParameters
類別成員。 如需詳細資訊,請參閱自訂 iOS/macOS 的瀏覽器和 WebView。
func initWebViewParams() {
self.webViewParameters = MSALWebviewParameters(authPresentationViewController: self)
self.webViewParameters?.webviewType = .default
}
步驟 6.2:啟動互動式授權要求
互動式授權要求是 System WebView 用來提示使用者註冊或登入的流程。 當使用者選取 [登入] 按鈕時,便會呼叫 authorizationButton
方法。
authorizationButton
方法會使用有關授權要求的相關資料來準備 MSALInteractiveTokenParameters
物件。
acquireToken
方法會使用 MSALInteractiveTokenParameters
以透過 System WebView 驗證使用者。
下列程式碼片段示範如何啟動互動式授權要求:
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
方法的 closure。
acquireToken
方法會傳回 result
和 error
物件。 使用此 closure 以:
- 在驗證完成後,以資訊更新行動裝置應用程式 UI。
- 使用存取權杖呼叫 Web API 服務。
- 處理驗證錯誤 (例如,當使用者取消登入流程時)。
步驟 6.3:呼叫 Web API
若要呼叫權杖型授權 Web API,應用程式必須具備有效的存取權杖。 應用程式會執行下列動作:
- 使用 Web API 端點的必要權限 (範圍) 取得存取權杖。
- 使用下列格式,在 HTTP 要求的授權標頭中,以持有人權杖的形式傳遞存取權杖:
Authorization: Bearer <access-token>
當使用者以互動方式進行驗證時,應用程式會取得 acquireToken
closure 中的存取權杖。 針對後續的 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)")
}
}
後續步驟
了解如何: