Nota
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Se aplica a: inquilinos de personal
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.
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
deMSALInteractiveTokenParameters
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.
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 elapplicationContext
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.
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 objetoresult.account
. A continuación, recupera el valor de la reclamación "preferred_username" del diccionario de reclamaciones y lo asigna a la variablepreferredUsername
.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
yacquireTokenSilently
. La funciónloadCurrentAccount
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. ConMSALSilentTokenParameters
, 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 propiedadaccessToken
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
, currentAccount
y 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.