Compartir vía


Tutorial: Inicio de sesión de usuarios en la aplicación móvil de iOS (Swift)

Se aplica a:Círculo verde con un símbolo de marca de verificación blanca. inquilinos de personal Círculo verde con un símbolo de marca de verificación blanca. inquilinos externos (más información)

Este es el tercer tutorial de la serie de tutoriales que le guía en el inicio de sesión de los usuarios mediante el identificador de Entra de Microsoft.

Antes de empezar, use el selector Elegir un tipo de inquilino selector en la parte superior de esta página para seleccionar el tipo de inquilino. Microsoft Entra ID proporciona dos configuraciones de inquilino, recursos y externa. Una configuración de entorno de trabajo está destinada a los empleados, las aplicaciones internas y otros recursos de la organización. Un inquilino externo es para las aplicaciones orientadas al cliente.

En este tutorial, harás lo siguiente:

  • Iniciar sesión de usuario.
  • Cierre la sesión del usuario.
  • Creación de la interfaz de usuario de la aplicación

Prerrequisitos

Iniciar sesión de usuario

Tiene dos opciones principales para iniciar sesión de usuarios mediante la Biblioteca de autenticación de Microsoft (MSAL) para iOS: adquirir tokens de forma interactiva o silenciosa.

  1. Para iniciar sesión de forma interactiva, use el código siguiente:

    func acquireTokenInteractively() {
    
        guard let applicationContext = self.applicationContext else { return }
        guard let webViewParameters = self.webViewParameters else { return }
    
        // #1
        let parameters = MSALInteractiveTokenParameters(scopes: kScopes, webviewParameters: webViewParameters)
        parameters.promptType = .selectAccount
    
        // #2
        applicationContext.acquireToken(with: parameters) { (result, error) in
    
            // #3
            if let error = error {
    
                self.updateLogging(text: "Could not acquire token: \(error)")
                return
            }
    
            guard let result = result else {
    
                self.updateLogging(text: "Could not acquire token: No result returned")
                return
            }
    
            // #4
            self.accessToken = result.accessToken
            self.updateLogging(text: "Access token is \(self.accessToken)")
            self.updateCurrentAccount(account: result.account)
            self.getContentWithToken()
        }
    }
    

    La propiedad promptType de MSALInteractiveTokenParameters configura el comportamiento de la solicitud de consentimiento y autenticación. Se admiten los siguientes valores:

    • .promptIfNecessary (valor predeterminado): solo se solicita al usuario si es necesario. La experiencia de autenticación única viene determinada por la presencia de cookies en el navegador web y el tipo de cuenta. Si hay varios usuarios que han iniciado sesión, se presenta la experiencia de selección de cuentas. Este es el comportamiento predeterminado.
    • .selectAccount: si no se especifica ningún usuario, la vista web de autenticación muestra una lista de las cuentas con una sesión iniciada actualmente para que el usuario realice su selección.
    • .login: requiere que el usuario se autentique en la vista web. Solo una cuenta puede iniciar sesión a la vez si se especifica este valor.
    • .consent: requiere que el usuario dé su consentimiento al conjunto de ámbitos actual de la solicitud.
  2. Para iniciar sesión de forma silenciosa, use el código siguiente:

    
        func acquireTokenSilently(_ account : MSALAccount!) {
    
            guard let applicationContext = self.applicationContext else { return }
    
            /**
    
             Acquire a token for an existing account silently
    
             - forScopes:           Permissions you want included in the access token received
             in the result in the completionBlock. Not all scopes are
             guaranteed 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.
             */
    
            let parameters = MSALSilentTokenParameters(scopes: kScopes, account: account)
    
            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) {
    
                            DispatchQueue.main.async {
                                self.acquireTokenInteractively()
                            }
                            return
                        }
                    }
    
                    self.updateLogging(text: "Could not acquire token silently: \(error)")
                    return
                }
    
                guard let result = result else {
    
                    self.updateLogging(text: "Could not acquire token: No result returned")
                    return
                }
    
                self.accessToken = result.accessToken
                self.updateLogging(text: "Refreshed Access token is \(self.accessToken)")
                self.updateSignOutButton(enabled: true)
                self.getContentWithToken()
            }
        }
    

    El método acquireTokenSilently, intenta adquirir silenciosamente un token de acceso para una cuenta de MSAL existente. Usa el applicationContext para solicitar el token con los alcances especificados. Si se produce un error, comprueba si se requiere la interacción del usuario y, si es así, inicia una adquisición de tokens interactiva. Tras la operación correcta, actualiza el token de acceso, registra el resultado, habilita el botón de cierre de sesión y recupera el contenido mediante el token.

Control de la devolución de llamada de inicio de sesión (solo iOS)

Abra el archivo AppDelegate.swift. Para controlar el callback después de iniciar sesión, agregue MSALPublicClientApplication.handleMSALResponse a la clase appDelegate de la siguiente manera:

// Inside AppDelegate...
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {

        return MSALPublicClientApplication.handleMSALResponse(url, sourceApplication: options[UIApplication.OpenURLOptionsKey.sourceApplication] as? String)
}

Si usa Xcode 11, debe colocar la devolución de llamada de MSAL en SceneDelegate.swift en su lugar. Si admite tanto UISceneDelegate como UIApplicationDelegate para lograr compatibilidad con sistemas operativos iOS anteriores, la devolución de llamada de MSAL debe colocarse en ambos archivos.

func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {

        guard let urlContext = URLContexts.first else {
            return
        }

        let url = urlContext.url
        let sourceApp = urlContext.options.sourceApplication

        MSALPublicClientApplication.handleMSALResponse(url, sourceApplication: sourceApp)
    }

Cerrar la sesión del usuario

Importante

Al cerrar sesión con MSAL, se elimina de la aplicación toda la información conocida sobre un usuario, así como al eliminar una sesión activa en el dispositivo, cuando lo permite la configuración de este. Opcionalmente, también puede cerrar la sesión del usuario desde el explorador.

Para agregar la funcionalidad de cierre de sesión, agregue el siguiente código a la clase ViewController.

@objc func signOut(_ sender: AnyObject) {

        guard let applicationContext = self.applicationContext else { return }

        guard let account = self.currentAccount else { return }

        do {

            /**
             Removes all tokens from the cache for this application for the provided account

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

            let signoutParameters = MSALSignoutParameters(webviewParameters: self.webViewParameters!)
            signoutParameters.signoutFromBrowser = false // set this to true if you also want to signout from browser or webview

            applicationContext.signout(with: account, signoutParameters: signoutParameters, completionBlock: {(success, error) in

                if let error = error {
                    self.updateLogging(text: "Couldn't sign out account with error: \(error)")
                    return
                }

                self.updateLogging(text: "Sign out completed successfully")
                self.accessToken = ""
                self.updateCurrentAccount(account: nil)
            })

        }
    }

Creación de la interfaz de usuario de la aplicación

Ahora cree una interfaz de usuario que incluya un botón para llamar a Microsoft Graph API, otro para cerrar la sesión y una vista de texto para parte de la salida agregando el siguiente código a la clase ViewController:

Interfaz de usuario de iOS

var loggingText: UITextView!
var signOutButton: UIButton!
var callGraphButton: UIButton!
var usernameLabel: UILabel!

func initUI() {

    usernameLabel = UILabel()
    usernameLabel.translatesAutoresizingMaskIntoConstraints = false
    usernameLabel.text = ""
    usernameLabel.textColor = .darkGray
    usernameLabel.textAlignment = .right

    self.view.addSubview(usernameLabel)

    usernameLabel.topAnchor.constraint(equalTo: view.topAnchor, constant: 50.0).isActive = true
    usernameLabel.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -10.0).isActive = true
    usernameLabel.widthAnchor.constraint(equalToConstant: 300.0).isActive = true
    usernameLabel.heightAnchor.constraint(equalToConstant: 50.0).isActive = true

    // Add call Graph button
    callGraphButton  = UIButton()
    callGraphButton.translatesAutoresizingMaskIntoConstraints = false
    callGraphButton.setTitle("Call Microsoft Graph API", for: .normal)
    callGraphButton.setTitleColor(.blue, for: .normal)
    callGraphButton.addTarget(self, action: #selector(callGraphAPI(_:)), for: .touchUpInside)
    self.view.addSubview(callGraphButton)

    callGraphButton.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
    callGraphButton.topAnchor.constraint(equalTo: view.topAnchor, constant: 120.0).isActive = true
    callGraphButton.widthAnchor.constraint(equalToConstant: 300.0).isActive = true
    callGraphButton.heightAnchor.constraint(equalToConstant: 50.0).isActive = true

    // Add sign out button
    signOutButton = UIButton()
    signOutButton.translatesAutoresizingMaskIntoConstraints = false
    signOutButton.setTitle("Sign Out", for: .normal)
    signOutButton.setTitleColor(.blue, for: .normal)
    signOutButton.setTitleColor(.gray, for: .disabled)
    signOutButton.addTarget(self, action: #selector(signOut(_:)), for: .touchUpInside)
    self.view.addSubview(signOutButton)

    signOutButton.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
    signOutButton.topAnchor.constraint(equalTo: callGraphButton.bottomAnchor, constant: 10.0).isActive = true
    signOutButton.widthAnchor.constraint(equalToConstant: 150.0).isActive = true
    signOutButton.heightAnchor.constraint(equalToConstant: 50.0).isActive = true

    let deviceModeButton = UIButton()
    deviceModeButton.translatesAutoresizingMaskIntoConstraints = false
    deviceModeButton.setTitle("Get device info", for: .normal);
    deviceModeButton.setTitleColor(.blue, for: .normal);
    deviceModeButton.addTarget(self, action: #selector(getDeviceMode(_:)), for: .touchUpInside)
    self.view.addSubview(deviceModeButton)

    deviceModeButton.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
    deviceModeButton.topAnchor.constraint(equalTo: signOutButton.bottomAnchor, constant: 10.0).isActive = true
    deviceModeButton.widthAnchor.constraint(equalToConstant: 150.0).isActive = true
    deviceModeButton.heightAnchor.constraint(equalToConstant: 50.0).isActive = true

    // Add logging textfield
    loggingText = UITextView()
    loggingText.isUserInteractionEnabled = false
    loggingText.translatesAutoresizingMaskIntoConstraints = false

    self.view.addSubview(loggingText)

    loggingText.topAnchor.constraint(equalTo: deviceModeButton.bottomAnchor, constant: 10.0).isActive = true
    loggingText.leftAnchor.constraint(equalTo: self.view.leftAnchor, constant: 10.0).isActive = true
    loggingText.rightAnchor.constraint(equalTo: self.view.rightAnchor, constant: -10.0).isActive = true
    loggingText.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: 10.0).isActive = true
}

func platformViewDidLoadSetup() {

    NotificationCenter.default.addObserver(self,
                        selector: #selector(appCameToForeGround(notification:)),
                        name: UIApplication.willEnterForegroundNotification,
                        object: nil)

}

@objc func appCameToForeGround(notification: Notification) {
    self.loadCurrentAccount()
}

Interfaz de usuario de macOS


var callGraphButton: NSButton!
var loggingText: NSTextView!
var signOutButton: NSButton!

var usernameLabel: NSTextField!

func initUI() {

    usernameLabel = NSTextField()
    usernameLabel.translatesAutoresizingMaskIntoConstraints = false
    usernameLabel.stringValue = ""
    usernameLabel.isEditable = false
    usernameLabel.isBezeled = false
    self.view.addSubview(usernameLabel)

    usernameLabel.topAnchor.constraint(equalTo: view.topAnchor, constant: 30.0).isActive = true
    usernameLabel.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -10.0).isActive = true

    // Add call Graph button
    callGraphButton  = NSButton()
    callGraphButton.translatesAutoresizingMaskIntoConstraints = false
    callGraphButton.title = "Call Microsoft Graph API"
    callGraphButton.target = self
    callGraphButton.action = #selector(callGraphAPI(_:))
    callGraphButton.bezelStyle = .rounded
    self.view.addSubview(callGraphButton)

    callGraphButton.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
    callGraphButton.topAnchor.constraint(equalTo: view.topAnchor, constant: 50.0).isActive = true
    callGraphButton.heightAnchor.constraint(equalToConstant: 34.0).isActive = true

    // Add sign out button
    signOutButton = NSButton()
    signOutButton.translatesAutoresizingMaskIntoConstraints = false
    signOutButton.title = "Sign Out"
    signOutButton.target = self
    signOutButton.action = #selector(signOut(_:))
    signOutButton.bezelStyle = .texturedRounded
    self.view.addSubview(signOutButton)

    signOutButton.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
    signOutButton.topAnchor.constraint(equalTo: callGraphButton.bottomAnchor, constant: 10.0).isActive = true
    signOutButton.heightAnchor.constraint(equalToConstant: 34.0).isActive = true
    signOutButton.isEnabled = false

    // Add logging textfield
    loggingText = NSTextView()
    loggingText.translatesAutoresizingMaskIntoConstraints = false

    self.view.addSubview(loggingText)

    loggingText.topAnchor.constraint(equalTo: signOutButton.bottomAnchor, constant: 10.0).isActive = true
    loggingText.leftAnchor.constraint(equalTo: self.view.leftAnchor, constant: 10.0).isActive = true
    loggingText.rightAnchor.constraint(equalTo: self.view.rightAnchor, constant: -10.0).isActive = true
    loggingText.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: -10.0).isActive = true
    loggingText.widthAnchor.constraint(equalToConstant: 500.0).isActive = true
    loggingText.heightAnchor.constraint(equalToConstant: 300.0).isActive = true
}

func platformViewDidLoadSetup() {}

A continuación, también en la clase ViewController, reemplace el método viewDidLoad() por:

    override func viewDidLoad() {

        super.viewDidLoad()

        initUI()

        do {
            try self.initMSAL()
        } catch let error {
            self.updateLogging(text: "Unable to create Application Context \(error)")
        }

        self.loadCurrentAccount()
        self.platformViewDidLoadSetup()
    }

Pasos siguientes

Este es el tercer tutorial de la serie de tutoriales que le guía en el inicio de sesión de los usuarios mediante el identificador de Entra de Microsoft.

Antes de empezar, use el selector Elegir un tipo de inquilino selector en la parte superior de esta página para seleccionar el tipo de inquilino. Microsoft Entra ID proporciona dos configuraciones de inquilino, recursos y externa. Una configuración de entorno de trabajo está destinada a los empleados, las aplicaciones internas y otros recursos de la organización. Un inquilino externo es para las aplicaciones orientadas al cliente.

En este tutorial, harás lo siguiente:

  • Iniciar sesión de usuario.
  • Cierre la sesión del usuario.

Prerrequisitos

Iniciar sesión de usuario

Tiene dos opciones principales para iniciar sesión de usuarios mediante la Biblioteca de autenticación de Microsoft (MSAL) para iOS: adquirir tokens de forma interactiva o silenciosa.

  1. Para iniciar sesión de forma interactiva, use el código siguiente:

    acquireTokenInteractively() {
        guard let applicationContext = self.applicationContext else { return }
        guard let webViewParameters = self.webViewParameters else { return }
    
        updateLogging(text: "Acquiring token interactively...")
    
        let parameters = MSALInteractiveTokenParameters(scopes: Configuration.kScopes, webviewParameters: webViewParameters)
        parameters.promptType = .selectAccount
    
        applicationContext.acquireToken(with: parameters) { (result, error) in
    
            if let error = error {
    
                self.updateLogging(text: "Could not acquire token: \(error)")
                return
            }
    
            guard let result = result else {
    
                self.updateLogging(text: "Could not acquire token: No result returned")
                return
            }
    
            self.accessToken = result.accessToken
            self.updateLogging(text: "Access token is \(self.accessToken)")
            self.updateCurrentAccount(account: result.account)
        }
    }
    

    El código comprueba primero si los parámetros de contexto de aplicación y vista web están disponibles. A continuación, actualiza el registro para indicar que adquiere el token de forma interactiva. A continuación, configura parámetros para la adquisición interactiva de tokens, especificando los ámbitos y los parámetros de vista web. También establece el tipo de aviso para seleccionar una cuenta.

    Después, llama al método acquireToken en el contexto de la aplicación con los parámetros definidos. En el controlador de finalización, comprueba si hay errores. Si se produce un error, actualiza el registro con el mensaje de error. Si se ejecuta correctamente, recupera el token de acceso del resultado, actualiza el registro con el token y actualiza la cuenta actual.

    Una vez que la aplicación adquiere un token de acceso, puede recuperar las reclamaciones asociadas a la cuenta actual. Para ello, use el siguiente fragmento de código:

    let claims = result.account.accountClaims
    let preferredUsername = claims?["preferred_username"] as? String
    

    El código lee las reclamaciones de la cuenta accediendo a la propiedad accountClaims del objeto result.account. A continuación, recupera el valor de la reclamación "preferred_username" del diccionario de reclamaciones y lo asigna a la variable preferredUsername.

  2. Para iniciar sesión de forma silenciosa, use el código siguiente:

    func acquireTokenSilently() {
        self.loadCurrentAccount { (account) in
    
            guard let currentAccount = account else {
    
                self.updateLogging(text: "No token found, try to acquire a token interactively first")
                return
            }
    
            self.acquireTokenSilently(currentAccount)
        }
    }
    

    El código inicia el proceso de adquisición de tokens de forma silenciosa. Primero intenta cargar la cuenta actual. Si se encuentra una cuenta actual, procede a adquirir el token de forma silenciosa mediante esa cuenta. Si no se encuentra ninguna cuenta actual, actualiza el registro para indicar que no se encuentra ningún token y sugiere intentar adquirir un token de forma interactiva.

    En el código anterior, llamamos a dos funciones, loadCurrentAccount y acquireTokenSilently. La función loadCurrentAccount debe tener el código siguiente:

    func loadCurrentAccount(completion: AccountCompletion? = nil) {
    
        guard let applicationContext = self.applicationContext else { return }
    
        let msalParameters = MSALParameters()
        msalParameters.completionBlockQueue = DispatchQueue.main
    
        // Note that this sample showcases an app that signs in a single account at a time
        applicationContext.getCurrentAccount(with: msalParameters, completionBlock: { (currentAccount, previousAccount, error) in
    
            if let error = error {
                self.updateLogging(text: "Couldn't query current account with error: \(error)")
                return
            }
    
            if let currentAccount = currentAccount {
    
                self.updateCurrentAccount(account: currentAccount)
                self.acquireTokenSilently(currentAccount)
    
                if let completion = completion {
                    completion(self.currentAccount)
                }
    
                return
            }
    
            // If testing with Microsoft's shared device mode, see the account that has been signed out from another app. More details here:
            // https://docs.microsoft.com/azure/active-directory/develop/msal-ios-shared-devices
            if let previousAccount = previousAccount {
    
                self.updateLogging(text: "The account with username \(String(describing: previousAccount.username)) has been signed out.")
    
            } else {
    
                self.updateLogging(text: "")
            }
    
            self.accessToken = ""
            self.updateCurrentAccount(account: nil)
    
            if let completion = completion {
                completion(nil)
            }
        })
    }
    

    El código usa MSAL para iOS para cargar la cuenta actual. Comprueba si hay errores y actualiza el registro en consecuencia. Si se encuentra una cuenta actual, la actualiza e intenta adquirir tokens de forma silenciosa. Si existe una cuenta anterior, registra el cierre de sesión. Si no se encuentra ninguna cuenta, borra el token de acceso. Por último, ejecuta un bloque de finalización, en caso de que se haya proporcionado.

    La función acquireTokenSilently debe contener el código siguiente:

    func acquireTokenSilently(_ account : MSALAccount) {
        guard let applicationContext = self.applicationContext else { return }
    
        /**
    
         Acquire a token for an existing account silently
    
         - forScopes:           Permissions you want included in the access token received
         in the result in the completionBlock. Not all scopes are
         guaranteed 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.
         */
    
        updateLogging(text: "Acquiring token silently...")
    
        let parameters = MSALSilentTokenParameters(scopes: Configuration.kScopes, account: account)
    
        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) {
    
                        DispatchQueue.main.async {
                            self.acquireTokenInteractively()
                        }
                        return
                    }
                }
    
                self.updateLogging(text: "Could not acquire token silently: \(error)")
                return
            }
    
            guard let result = result else {
    
                self.updateLogging(text: "Could not acquire token: No result returned")
                return
            }
    
            self.accessToken = result.accessToken
            self.updateLogging(text: "Refreshed Access token is \(self.accessToken)")
            self.updateSignOutButton(enabled: true)
        }
    }
    
    

    Esta función usa MSAL para iOS para adquirir de forma silenciosa un token para una cuenta existente. Después de comprobar el applicationContext, registra el proceso de adquisición de tokens. Con MSALSilentTokenParameters, define los parámetros necesarios. A continuación, intenta adquirir el token de forma silenciosa. Si hay errores, comprueba si hay requisitos de interacción del usuario, iniciando un proceso interactivo si es necesario. Una vez completado con éxito, actualiza la propiedad accessToken y registra el token actualizado, concluyendo habilitando el botón de cerrar sesión.

Cerrar la sesión del usuario

Para cerrar la sesión de un usuario de la aplicación de iOS (Swift) mediante MSAL para iOS, use el código siguiente:

   @IBAction func signOut(_ sender: UIButton) {

        guard let applicationContext = self.applicationContext else { return }

        guard let account = self.currentAccount else { return }

        guard let webViewParameters = self.webViewParameters else { return }

        updateLogging(text: "Signing out...")

        do {

            /**
             Removes all tokens from the cache for this application for the provided account

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

            let signoutParameters = MSALSignoutParameters(webviewParameters: webViewParameters)

            // If testing with Microsoft's shared device mode, trigger signout from browser. More details here:
            // https://docs.microsoft.com/azure/active-directory/develop/msal-ios-shared-devices

            if (self.currentDeviceMode == .shared) {
                signoutParameters.signoutFromBrowser = true
            } else {
                signoutParameters.signoutFromBrowser = false
            }

            applicationContext.signout(with: account, signoutParameters: signoutParameters, completionBlock: {(success, error) in

                if let error = error {
                    self.updateLogging(text: "Couldn't sign out account with error: \(error)")
                    return
                }

                self.updateLogging(text: "Sign out completed successfully")
                self.accessToken = ""
                self.updateCurrentAccount(account: nil)
            })

        }
    }

El código comprueba la existencia del applicationContext, currentAccounty webViewParameters. A continuación, registra el proceso de cierre de sesión. El código quita todos los tokens de la memoria caché de la cuenta proporcionada. Dependiendo del modo de dispositivo actual, determina si se cierra sesión desde el explorador. Tras la finalización, actualiza el texto de registro en consecuencia. Si se produce un error durante el proceso de cierre de sesión, registra el mensaje de error. Tras cerrar la sesión correctamente, actualiza el token de acceso a una cadena vacía y borra la cuenta actual.

Pasos siguientes