次の方法で共有


チュートリアル: ネイティブ認証を使用して iOS アプリで API を呼び出す

適用対象: 白いチェック マーク記号がある緑の円。 iOS (Swift) 白いチェック マーク記号がある緑の円。 macOS (Swift)

このチュートリアルでは、アクセス トークンを取得し、 iOS モバイル アプリで API を呼び出す方法について説明します。 iOS 用 Microsoft 認証ライブラリ (MSAL) ネイティブ認証 SDK を使用すると、シングル サインインを使用して複数のアクセス トークンを取得できます。 この機能を使用すると、ユーザーに再認証を要求する必要なく、1 つ以上のアクセス トークンを取得できます。

このチュートリアルでは、次の作業を行う方法について説明します。

  • 1 つまたは複数のアクセス トークンを取得します。
  • API を呼び出す

前提条件

1 つまたは複数のアクセス トークンを取得します。

MSAL ネイティブ認証 SDK は、複数のアクセス トークンを保存できます。 サインイン後、getAccessToken(scope:)関数を使用し、付与する新しいアクセス トークンのスコープを指定することで、アクセス トークンを取得できます。

  1. 次のコード スニペットを使用して、一連の API スコープを宣言し値を設定します。

    let protectedAPIUrl1: String? = nil
    let protectedAPIUrl2: String? = nil 
    let protectedAPIScopes1: [String] = []
    let protectedAPIScopes2: [String] = []
    
    var accessTokenAPI1: String?
    var accessTokenAPI2: String?
    
    • 最初の Web API の URL を使用して protectedAPIUrl1 を初期化します。
    • 2 つ目の Web API の URL を使用して protectedAPIUrl2 を初期化します。
    • ["api://<Resource_App_ID>/ToDoList.Read", "api://<Resource_App_ID>/ToDoList.ReadWrite"] など、最初の API のスコープを使用して protectedAPIScopes1 を定義します。
    • protectedAPIScopes1と同様に、2 番目の API のスコープを使用してprotectedAPIScopes2を定義します。
    • 省略可能な文字列変数の accessTokenAPI1accessTokenAPI2を宣言します。
  2. 次のコード スニペットを使用してユーザーにサインインさせます。

    @IBAction func signInPressed(_: Any) {
        guard let email = emailTextField.text, let password = passwordTextField.text else {
            resultTextView.text = "Email or password not set"
            return
        }
    
        print("Signing in with email \(email) and password")
    
        showResultText("Signing in...")
    
        nativeAuth.signIn(username: email, password: password, delegate: self)
    }
    

    signInPressed メソッドは、サインイン ボタンの押す処理します。 電子メールとパスワードのフィールドが入力されているかどうかを確認します。 どちらかが空の場合は、「メールまたはパスワードが設定されていません」と表示されます。両方のフィールドに入力すると、メールがログに記録され、"サインイン..." と表示され、指定されたメールとパスワードを使用して nativeAuth から signIn メソッドを使用してサインインが開始されます。 スコープが指定されていないため、SDK は既定の OIDC スコープ (openid、offline_access、プロファイル) に対して有効なトークンを取得します。

  3. 次のコード スニペットを使用して、1 つまたは複数のアクセス トークンを取得します。

    @IBAction func protectedApi1Pressed(_: Any) {
        guard let url = protectedAPIUrl1, !protectedAPIScopes1.isEmpty else {
            showResultText("API 1 not configured.")
            return
        }
    
        if let accessToken = accessTokenAPI1 {
            accessProtectedAPI(apiUrl: url, accessToken: accessToken)
        } else {
            accountResult?.getAccessToken(scopes: protectedAPIScopes1, delegate: self)
            let message = "Retrieving access token to use with API 1..."
            showResultText(message)
            print(message)
        }
    }
    
    @IBAction func protectedApi2Pressed(_: Any) {
        guard let url = protectedAPIUrl2, !protectedAPIScopes2.isEmpty else {
            showResultText("API 2 not configured.")
            return
        }
    
        if let accessToken = accessTokenAPI2 {
            accessProtectedAPI(apiUrl: url, accessToken: accessToken)
        } else {
            accountResult?.getAccessToken(scopes: protectedAPIScopes2, delegate: self)
            let message = "Retrieving access token to use with API 2..."
            showResultText(message)
            print(message)
        }
    }
    

    protectedApi1PressedメソッドとprotectedApi2Pressedメソッドは、2 つの異なるスコープ セットのアクセス トークンを取得するプロセスを管理します。 まず、各 API の URL とスコープが適切に構成されていることを確認します。 API のアクセス トークンが既に使用可能な場合は、API に直接アクセスします。 それ以外の場合は、アクセス トークンを要求し、進行中のトークン取得プロセスについてユーザーに通知します。

    protectedAPIScopes1protectedAPIScopes2にアクセス トークンを割り当てるには、次のスニペットを使用します。

    func onAccessTokenRetrieveCompleted(result: MSALNativeAuthTokenResult) {
        print("Access Token: \(result.accessToken)")
    
        if protectedAPIScopes1.allSatisfy(result.scopes.contains),
           let url = protectedAPIUrl1
        {
            accessTokenAPI1 = result.accessToken
            accessProtectedAPI(apiUrl: url, accessToken: result.accessToken)
        }
    
        if protectedAPIScopes2.allSatisfy(result.scopes.contains(_:)),
           let url = protectedAPIUrl2
        {
            accessTokenAPI2 = result.accessToken
            accessProtectedAPI(apiUrl: url, accessToken: result.accessToken)
        }
    
        showResultText("Signed in." + "\n\n" + "Scopes:\n\(result.scopes)" + "\n\n" + "Access Token:\n\(result.accessToken)")
        updateUI()
    }
    
    func onAccessTokenRetrieveError(error: MSAL.RetrieveAccessTokenError) {
        showResultText("Error retrieving access token: \(error.errorDescription ?? "No error description")")
    }
    

    onAccessTokenRetrieveCompleted メソッドは、アクセス トークンをコンソールに表示します。 次に、protectedAPIScopes1 が結果のスコープに含まれているかどうか、protectedAPIUrl1 が使用可能かどうかを確認します。使用可能な場合は、accessTokenAPI1 を設定し、URL とトークンを使用して accessProtectedAPI を呼び出します。 protectedAPIScopes2protectedAPIUrl2の同様のチェックが実行され、accessTokenAPI2が更新され、条件が満たされた場合に API 呼び出しが行われます。 最後に、サインイン状態、スコープ、アクセス トークンを含むメッセージが表示され、UI が更新されます。

    onAccessTokenRetrieveError メソッドは、アクセス トークン取得エラーの説明を含むエラー メッセージを表示し、説明が指定されていない場合は既定のメッセージを表示します。

API を呼び出す

次のコード スニペットを使用して API を呼び出します。

func accessProtectedAPI(apiUrl: String, accessToken: String) {
    guard let url = URL(string: apiUrl) else {
        let errorMessage = "Invalid API url"
        print(errorMessage)
        DispatchQueue.main.async {
            self.showResultText(errorMessage)
        }
        return
    }
    
    var request = URLRequest(url: url)
    request.httpMethod = "GET"
    request.setValue("Bearer \(accessToken)", forHTTPHeaderField: "Authorization")
    
    let task = URLSession.shared.dataTask(with: request) { data, response, error in
        if let error = error {
            print("Error found when accessing API: \(error.localizedDescription)")
            DispatchQueue.main.async {
                self.showResultText(error.localizedDescription)
            }
            return
        }
        
        guard let httpResponse = response as? HTTPURLResponse, (200...299).contains(httpResponse.statusCode)
        else {
            DispatchQueue.main.async {
                self.showResultText("Unsuccessful response found when accessing the API")
            }
            return
        }
        
        guard let data = data, let result = try? JSONSerialization.jsonObject(with: data, options: []) else {
            DispatchQueue.main.async {
                self.showResultText("Couldn't deserialize result JSON")
            }
            return
        }
        
        DispatchQueue.main.async {
            self.showResultText("""
                            Accessed API successfully using access token.
                            HTTP response code: \(httpResponse.statusCode)
                            HTTP response body: \(result)
                            """)
        }
    }
    
    task.resume()
}

accessProtectedAPI メソッドは、指定されたアクセス トークンを使用して、指定した API エンドポイントにバージョン変更要求を送信します。 Authorization ヘッダーのトークンを使用して要求を構成します。 正常な応答 (HTTP 状態コード 200 から 299) を受け取ると、JSON データが逆シリアル化され、HTTP 状態コードと応答本文で UI が更新されます。 要求または応答の処理中にエラーが発生した場合は、UI にエラー メッセージが表示されます。 このメソッドは、指定された URL とアクセス トークンに応じて、API 1 または API 2 へのアクセスを許可します。