Интеграция MSAL B2C AD в Swift XCode Ошибка при получении после авторизации "Не удалось получить токен - PullRequest
0 голосов
/ 11 ноября 2019

Есть некоторые похожие вопросы, но ни один из них не решает мою проблему.

Использование Xcode 11.1 MacOS Cataline 10.15

Я клонирую этот "active-directory-b2c-ios-swift-native-"msal "и попытаться запустить получение ошибки" Не удалось получить токен: Ошибка Domain = MSALErrorDomain Code = -50000 "(null)" UserInfo = {MSALErrorDescriptionKey = Не удалось запустить интерактивный сеанс, MSALInternalErrorCodeKey = -42008, MSALCorrelationIDKey = C9207A45-6A7416B-90E4-93E08F28A637} "После смены B2C-деталей такая же проблема получается. Пожалуйста, дайте мне знать, в чем проблема, это проблема для версии Xcode / OS / MSAL или какая-то проблема с кодом ??

Я пытался с упоминанием конфигурации по умолчанию в git repo "active-directory-b2c-ios-swift"-native-msal ", а также попытался после изменения, приведенного ниже, let kTenantName =" dovervsg.onmicrosoft.com "// Ваше имя арендатора let kClientID =" xxxxxxxxxxxxxxxxxxxxxxx "// Ваш идентификатор клиента на портале при создании приложения let kSignupOrSigninPolicy =" B2C_1-policy "// Ваша политика регистрации и входа, которую вы создали на портале, позволяют kEditProfilePolicy =" b2c_1_edit_profile "// Ваша политика редактирования, которую вы создали на портале, позволяют kResetPasswordPolicy =" B2C_1_reset_password "// Ваша политика сброса пароля, которую вы создали на порталеlet kGraphURI = "https://dev -vsg.dovertech.co.in " // Это ваш внутренний API, который вы настроили для приема токенов вашего приложения let kScopes: [String] = ["https://dovervsg.onmicrosoft.com/User.Read"] // Это область, которую вы настроили для поиска своего внутреннего API. // пробовал с этим форматом областиТакже давайте kScopes: [String] = ["https://dovervsg.onmicrosoft.com/api/User.Read"] // Это область, в которой вы настроили свой бэкэнд-API для поиска.

let kTenantName = "dovervsg.onmicrosoft.com" // Your tenant name
let kClientID = "xxxxxxxxxxxxxxxxxxxxxxx" // Your client ID from the portal when you created your application
let kSignupOrSigninPolicy = "B2C_1-policy" // Your signup and sign-in policy you created in the portal
let kEditProfilePolicy = "b2c_1_edit_profile" // Your edit policy you created in the portal
let kResetPasswordPolicy = "B2C_1_reset_password" // Your reset password policy you created in the portal
let kGraphURI = "https://dev-vsg.dovertech.co.in" // This is your backend API that you've configured to accept your app's tokens
let kScopes: [String] = ["https://dovervsg.onmicrosoft.com/User.Read"] // This is a scope that you've configured your backend API to look for.
// tried with this scope format as well, let kScopes: [String] = ["https://dovervsg.onmicrosoft.com/api/User.Read"] // This is a scope that you've configured your backend API to look for



// DO NOT CHANGE - This is the format of OIDC Token and Authorization endpoints for Azure AD B2C.
let kEndpoint = "https://login.microsoftonline.com/tfp/%@/%@"

var application: MSALPublicClientApplication!

var accessToken: String?

@IBOutlet weak var loggingText: UITextView!
@IBOutlet weak var signoutButton: UIButton!
@IBOutlet weak var callGraphApiButton: UIButton!
@IBOutlet weak var editProfileButton: UIButton!
@IBOutlet weak var refreshTokenButton: UIButton!

override func viewDidAppear(_ animated: Bool) {

    //super.viewDidLoad()

              do {
                  /**

                   Initialize a MSALPublicClientApplication with a MSALPublicClientApplicationConfig.
                   MSALPublicClientApplicationConfig can be initialized with client id, redirect uri and authority.
                   Redirect uri will be constucted automatically in the form of "msal<your-client-id-here>://auth" if not provided.
                   The scheme part, i.e. "msal<your-client-id-here>", needs to be registered in the info.plist of the project
                   */

                  let authority = try self.getAuthority(forPolicy: self.kSignupOrSigninPolicy)

                  // Provide configuration for MSALPublicClientApplication
                  // MSAL will use default redirect uri when you provide nil
                  let pcaConfig = MSALPublicClientApplicationConfig(clientId: kClientID, redirectUri: nil, authority: authority)
                  self.application = try MSALPublicClientApplication(configuration: pcaConfig)
              } catch {
                  self.updateLoggingText(text: "Unable to create application \(error)")
              }
}

/**
 This button will invoke the authorization flow and send the policy specified to the B2C server.
 Here we are using the `kSignupOrSignInPolicy` to sign the user in to the app. We will store this 
 accessToken for subsequent calls.
 */




@IBAction func authorizationButton(_ sender: UIButton) {
    do {
        /**

         authority is a URL indicating a directory that MSAL can use to obtain tokens. In Azure B2C
         it is of the form `https://<instance/tfp/<tenant>/<policy>`, where `<instance>` is the
         directory host (e.g. https://login.microsoftonline.com), `<tenant>` is a
         identifier within the directory itself (e.g. a domain associated to the
         tenant, such as contoso.onmicrosoft.com), and `<policy>` is the policy you wish to
         use for the current user flow.

         */

        let authority = try self.getAuthority(forPolicy: self.kSignupOrSigninPolicy)

        /**
         Acquire a token for a new account using interactive authentication

         - scopes: Permissions you want included in the access token received
         in the result in the completionBlock. Not all scopes are
         gauranteed to be included in the access token returned.
         - completionBlock: The completion block that will be called when the authentication
         flow completes, or encounters an error.
         */

        let webViewParameters = MSALWebviewParameters(parentViewController: self)
        let parameters = MSALInteractiveTokenParameters(scopes: kScopes, webviewParameters: webViewParameters)
        parameters.promptType = .selectAccount
        print( parameters.promptType = .selectAccount)
        parameters.authority = authority
         debugPrint(  parameters.authority = authority)
        application.acquireToken(with: parameters) { (result, error) in

            guard let result = result else {
                self.updateLoggingText(text: "Could not acquire token: \(error ?? "No error informarion" as! Error)")
                return
            }

            self.accessToken = result.accessToken
            self.updateLoggingText(text: "Access token is \(self.accessToken ?? "Empty")")
            self.signoutButton.isEnabled = true
            self.callGraphApiButton.isEnabled = true
            self.editProfileButton.isEnabled = true
            self.refreshTokenButton.isEnabled = true
        }
    } catch {
        self.updateLoggingText(text: "Unable to create authority \(error)")
    }
}

@IBAction func editProfile(_ sender: UIButton) {
    do {

        /**

         authority is a URL indicating a directory that MSAL can use to obtain tokens. In Azure B2C
         it is of the form `https://<instance/tfp/<tenant>/<policy>`, where `<instance>` is the
         directory host (e.g. https://login.microsoftonline.com), `<tenant>` is a
         identifier within the directory itself (e.g. a domain associated to the
         tenant, such as contoso.onmicrosoft.com), and `<policy>` is the policy you wish to
         use for the current user flow.

         */

        let authority = try self.getAuthority(forPolicy: self.kEditProfilePolicy)

        /**
         Acquire a token for a new account using interactive authentication

         - scopes: Permissions you want included in the access token received
         in the result in the completionBlock. Not all scopes are
         gauranteed to be included in the access token returned.
         - completionBlock: The completion block that will be called when the authentication
         flow completes, or encounters an error.
         */

        let thisAccount = try self.getAccountByPolicy(withAccounts: application.allAccounts(), policy: kEditProfilePolicy)
        let webViewParameters = MSALWebviewParameters(parentViewController: self)
        let parameters = MSALInteractiveTokenParameters(scopes: kScopes, webviewParameters: webViewParameters)
        parameters.authority = authority
        parameters.account = thisAccount

        application.acquireToken(with: parameters) { (result, error) in
            if let error = error {
                self.updateLoggingText(text: "Could not edit profile: \(error)")
            } else {
                self.updateLoggingText(text: "Successfully edited profile")
            }
        }
    } catch {
        self.updateLoggingText(text: "Unable to construct parameters before calling acquire token \(error)")
    }
}

@IBAction func refreshToken(_ sender: UIButton) {

    do {
        /**

         authority is a URL indicating a directory that MSAL can use to obtain tokens. In Azure B2C
         it is of the form `https://<instance/tfp/<tenant>/<policy>`, where `<instance>` is the
         directory host (e.g. https://login.microsoftonline.com), `<tenant>` is a
         identifier within the directory itself (e.g. a domain associated to the
         tenant, such as contoso.onmicrosoft.com), and `<policy>` is the policy you wish to
         use for the current user flow.

         */

        let authority = try self.getAuthority(forPolicy: self.kSignupOrSigninPolicy)

        /**

         Acquire a token for an existing account silently

         - scopes: Permissions you want included in the access token received
         in the result in the completionBlock. Not all scopes are
         gauranteed to be included in the access token returned.
         - account: An account object that we retrieved from the application object before that the
         authentication flow will be locked down to.
         - completionBlock: The completion block that will be called when the authentication
         flow completes, or encounters an error.
         */

        guard let thisAccount = try self.getAccountByPolicy(withAccounts: application.allAccounts(), policy: kSignupOrSigninPolicy) else {
            self.updateLoggingText(text: "There is no account available!")
            return
        }

        let parameters = MSALSilentTokenParameters(scopes: kScopes, account:thisAccount)
        parameters.authority = authority
        self.application.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) {

                        // Notice we supply the account here. This ensures we acquire token for the same account
                        // as we originally authenticated.

                        let webviewParameters = MSALWebviewParameters(parentViewController: self)
                        let parameters = MSALInteractiveTokenParameters(scopes: self.kScopes, webviewParameters: webviewParameters)
                        parameters.account = thisAccount

                        self.application.acquireToken(with: parameters) { (result, error) in

                            guard let result = result else {
                                self.updateLoggingText(text: "Could not acquire new token: \(error ?? "No error informarion" as! Error)")
                                return
                            }

                            self.accessToken = result.accessToken
                            self.updateLoggingText(text: "Access token is \(self.accessToken ?? "empty")")
                        }
                        return
                    }
                }

                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
            }

            self.accessToken = result.accessToken
            self.updateLoggingText(text: "Refreshing token silently")
            self.updateLoggingText(text: "Refreshed access token is \(self.accessToken ?? "empty")")
        }
    } catch {
        self.updateLoggingText(text: "Unable to construct parameters before calling acquire token \(error)")
    }
}

@IBAction func callApi(_ 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 informarion" 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()
}

@IBAction func signoutButton(_ sender: UIButton) {
    do {
        /**
         Removes all tokens from the cache for this application for the provided account

         - account:    The account to remove from the cache
         */

        let thisAccount = try self.getAccountByPolicy(withAccounts: application.allAccounts(), policy: kSignupOrSigninPolicy)

        if let accountToRemove = thisAccount {
            try application.remove(accountToRemove)
        } else {
            self.updateLoggingText(text: "There is no account to signing out!")
        }

        self.signoutButton.isEnabled = false
        self.callGraphApiButton.isEnabled = false
        self.editProfileButton.isEnabled = false
        self.refreshTokenButton.isEnabled = false

        self.updateLoggingText(text: "Signed out")

    } catch  {
        self.updateLoggingText(text: "Received error signing out: \(error)")
    }
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}

override func viewWillAppear(_ animated: Bool) {

    if self.accessToken == nil {
        signoutButton.isEnabled = false
        callGraphApiButton.isEnabled = false
        editProfileButton.isEnabled = false
        refreshTokenButton.isEnabled = false
    }
}

func getAccountByPolicy (withAccounts accounts: [MSALAccount], policy: String) throws -> MSALAccount? {

    for account in accounts {
        // This is a single account sample, so we only check the suffic part of the object id,
        // where object id is in the form of <object id>-<policy>.
        // For multi-account apps, the whole object id needs to be checked.
        if let homeAccountId = account.homeAccountId, let objectId = homeAccountId.objectId {
            if objectId.hasSuffix(policy.lowercased()) {
                return account
            }
        }
    }
    return nil
}

/**

 The way B2C knows what actions to perform for the user of the app is through the use of `Authority URL`.
 It is of the form `https://<instance/tfp/<tenant>/<policy>`, where `<instance>` is the
 directory host (e.g. https://login.microsoftonline.com), `<tenant>` is a
 identifier within the directory itself (e.g. a domain associated to the
 tenant, such as contoso.onmicrosoft.com), and `<policy>` is the policy you wish to
 use for the current user flow.
 */
func getAuthority(forPolicy policy: String) throws -> MSALB2CAuthority {
    guard let authorityURL = URL(string: String(format: self.kEndpoint, self.kTenantName, policy)) else {
        throw NSError(domain: "SomeDomain",
                      code: 1,
                      userInfo: ["errorDescription": "Unable to create authority URL!"])
    }
    return try MSALB2CAuthority(url: authorityURL)
}

func updateLoggingText(text: String) {
    DispatchQueue.main.async{
        self.loggingText.text = text
    }
}

}

После запуска появляется ошибка выше

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...