Aplicación web que inicia sesión de usuarios: Inicio y cierre de sesión
Artículo
Obtenga información sobre cómo agregar el inicio de sesión al código de una aplicación web que permita iniciar sesión a los usuarios. A continuación, obtenga información sobre cómo permitirles cerrar sesión.
Inicio de sesión
El inicio de sesión consta de dos partes:
El botón de inicio de sesión en la página HTML
La acción de inicio de sesión en el código subyacente en el controlador
En ASP.NET Core, en el caso de las aplicaciones de la plataforma de identidad de Microsoft, el botón Iniciar sesión se expone en Views\Shared\_LoginPartial.cshtml (para una aplicación MVC) o Pages\Shared\_LoginPartial.cshtm (para una aplicación Razor). Solo se muestra cuando el usuario no está autenticado. Es decir, se muestra cuando el usuario todavía no ha iniciado sesión o ha cerrado la sesión. Por el contrario, el botón Cerrar sesión se muestra cuando el usuario ya ha iniciado sesión. Tenga en cuenta que el controlador de cuentas se define en el paquete NuGet Microsoft.Identity.Web.UI, en el área denominada MicrosoftIdentity.
En ASP.NET MVC, el botón Iniciar sesión se expone en Views\Shared\_LoginPartial.cshtml. Solo se muestra cuando el usuario no está autenticado. Es decir, se muestra cuando el usuario todavía no ha iniciado sesión o ha cerrado la sesión.
Cuando un usuario no autenticado visita la página principal, la ruta index en app.py redirige al usuario a la ruta login.
@app.route("/")
def index():
if not (app.config["CLIENT_ID"] and app.config["CLIENT_SECRET"]):
# This check is not strictly necessary.
# You can remove this check from your production code.
return render_template('config_error.html')
if not auth.get_user():
return redirect(url_for("login"))
return render_template('index.html', user=auth.get_user(), version=identity.__version__)
La login ruta determina el adecuado auth_uri y representa la plantilla delogin.html.
@app.route("/login")
def login():
return render_template("login.html", version=identity.__version__, **auth.log_in(
scopes=app_config.SCOPE, # Have user consent to scopes during log-in
redirect_uri=url_for("auth_response", _external=True), # Optional. If present, this absolute URL must match your app's redirect_uri registered in Azure Portal
))
En ASP.NET, seleccionar el botón Iniciar sesión en la aplicación web desencadena la acción SignIn en el controlador AccountController. En las versiones anteriores de las plantillas de ASP.NET Core, el controlador Account se incrustó con la aplicación web. Ya no es así porque, ahora, el controlador forma parte del paquete NuGet Microsoft.Identity.Web.UI. Consulte AccountController.cs para obtener más información.
Este controlador también controla las aplicaciones de Azure AD B2C.
En ASP.NET, el inicio de sesión se desencadena desde el método SignIn() en un controlador (por ejemplo, AccountController.cs#L16-L23). Este método no forma parte del marco de .NET (lo contrario de lo que sucede en ASP.NET Core). Envía un desafío de inicio de sesión OpenID tras proponer un URI de redirección.
public void SignIn()
{
// Send an OpenID Connect sign-in request.
if (!Request.IsAuthenticated)
{
HttpContext.GetOwinContext().Authentication.Challenge(new AuthenticationProperties { RedirectUri = "/" }, OpenIdConnectAuthenticationDefaults.AuthenticationType);
}
}
En Java, el cierre de sesión se controla llamando directamente al punto de conexión logout de la Plataforma de identidad de Microsoft y proporcionando el valor post_logout_redirect_uri. Para obtener detalles, consulte AuthPageController.java#L30-L48.
@Controller
public class AuthPageController {
@Autowired
AuthHelper authHelper;
@RequestMapping("/msal4jsample")
public String homepage(){
return "index";
}
@RequestMapping("/msal4jsample/secure/aad")
public ModelAndView securePage(HttpServletRequest httpRequest) throws ParseException {
ModelAndView mav = new ModelAndView("auth_page");
setAccountInfo(mav, httpRequest);
return mav;
}
// More code omitted for simplicity
Cuando el usuario selecciona el vínculo Iniciar sesión, que desencadena la ruta /auth/signin, el controlador de inicio de sesión toma el control para autenticar al usuario con la Plataforma de identidad de Microsoft.
login(options = {}) {
return async (req, res, next) => {
/**
* MSAL Node library allows you to pass your custom state as state parameter in the Request object.
* The state parameter can also be used to encode information of the app's state before redirect.
* You can pass the user's state in the app, such as the page or view they were on, as input to this parameter.
*/
const state = this.cryptoProvider.base64Encode(
JSON.stringify({
successRedirect: options.successRedirect || '/',
})
);
const authCodeUrlRequestParams = {
state: state,
/**
* By default, MSAL Node will add OIDC scopes to the auth code url request. For more information, visit:
* https://docs.microsoft.com/azure/active-directory/develop/v2-permissions-and-consent#openid-connect-scopes
*/
scopes: options.scopes || [],
redirectUri: options.redirectUri,
};
const authCodeRequestParams = {
state: state,
/**
* By default, MSAL Node will add OIDC scopes to the auth code request. For more information, visit:
* https://docs.microsoft.com/azure/active-directory/develop/v2-permissions-and-consent#openid-connect-scopes
*/
scopes: options.scopes || [],
redirectUri: options.redirectUri,
};
/**
* If the current msal configuration does not have cloudDiscoveryMetadata or authorityMetadata, we will
* make a request to the relevant endpoints to retrieve the metadata. This allows MSAL to avoid making
* metadata discovery calls, thereby improving performance of token acquisition process. For more, see:
* https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-node/docs/performance.md
*/
if (!this.msalConfig.auth.cloudDiscoveryMetadata || !this.msalConfig.auth.authorityMetadata) {
const [cloudDiscoveryMetadata, authorityMetadata] = await Promise.all([
this.getCloudDiscoveryMetadata(this.msalConfig.auth.authority),
this.getAuthorityMetadata(this.msalConfig.auth.authority)
]);
this.msalConfig.auth.cloudDiscoveryMetadata = JSON.stringify(cloudDiscoveryMetadata);
this.msalConfig.auth.authorityMetadata = JSON.stringify(authorityMetadata);
}
const msalInstance = this.getMsalInstance(this.msalConfig);
// trigger the first leg of auth code flow
return this.redirectToAuthCodeUrl(
authCodeUrlRequestParams,
authCodeRequestParams,
msalInstance
)(req, res, next);
};
}
redirectToAuthCodeUrl(authCodeUrlRequestParams, authCodeRequestParams, msalInstance) {
return async (req, res, next) => {
// Generate PKCE Codes before starting the authorization flow
const { verifier, challenge } = await this.cryptoProvider.generatePkceCodes();
// Set generated PKCE codes and method as session vars
req.session.pkceCodes = {
challengeMethod: 'S256',
verifier: verifier,
challenge: challenge,
};
/**
* By manipulating the request objects below before each request, we can obtain
* auth artifacts with desired claims. For more information, visit:
* https://azuread.github.io/microsoft-authentication-library-for-js/ref/modules/_azure_msal_node.html#authorizationurlrequest
* https://azuread.github.io/microsoft-authentication-library-for-js/ref/modules/_azure_msal_node.html#authorizationcoderequest
**/
req.session.authCodeUrlRequest = {
...authCodeUrlRequestParams,
responseMode: msal.ResponseMode.FORM_POST, // recommended for confidential clients
codeChallenge: req.session.pkceCodes.challenge,
codeChallengeMethod: req.session.pkceCodes.challengeMethod,
};
req.session.authCodeRequest = {
...authCodeRequestParams,
code: '',
};
try {
const authCodeUrlResponse = await msalInstance.getAuthCodeUrl(req.session.authCodeUrlRequest);
res.redirect(authCodeUrlResponse);
} catch (error) {
next(error);
}
};
}
/**
* Retrieves cloud discovery metadata from the /discovery/instance endpoint
* @returns
*/
async getCloudDiscoveryMetadata(authority) {
const endpoint = 'https://login.microsoftonline.com/common/discovery/instance';
try {
const response = await axios.get(endpoint, {
params: {
'api-version': '1.1',
'authorization_endpoint': `${authority}/oauth2/v2.0/authorize`
}
});
return await response.data;
} catch (error) {
throw error;
}
}
Cuando el usuario selecciona el vínculo Iniciar sesión, se le lleva al punto de conexión de autorización de la Plataforma de identidad de Microsoft.
Un inicio de sesión correcto redirige al usuario a la ruta auth_response, que completa el proceso de inicio de sesión mediante auth.complete_login, genera errores, si los hay, y redirige al usuario ahora autenticado a la página de inicio.
@app.route(app_config.REDIRECT_PATH)
def auth_response():
result = auth.complete_log_in(request.args)
if "error" in result:
return render_template("auth_error.html", result=result)
return redirect(url_for("index"))
Una vez que el usuario ha iniciado sesión en la aplicación, probablemente quiera permitirle cerrar la sesión.
Cierre de sesión
Cerrar sesión desde una aplicación web implica más que tan solo quitar la información sobre la cuenta con sesión iniciada del estado de la aplicación web.
La aplicación web también debe redirigir al usuario al punto de conexión logout de la Plataforma de identidad de Microsoft para cerrar la sesión.
Cuando la aplicación web redirige al usuario a punto de conexión logout, este punto de conexión borra la sesión del usuario del explorador. Si la aplicación no llegó al punto de conexión logout, el usuario se volverá a autenticar en la aplicación sin volver a escribir sus credenciales. La razón es que tendrá una sesión de inicio de sesión único válida con la Plataforma de identidad de Microsoft.
Durante el registro de la aplicación, registrará una dirección URL de cierre de sesión del canal frontal. En este tutorial, ha registrado https://localhost:44321/signout-oidc en el campo Dirección URL de cierre de sesión de canal frontal de la página Autenticación. Para obtener información detallada, consulte Registrar la aplicación webApp.
Durante el registro de la aplicación, no es necesario registrar una dirección URL de cierre de sesión de canal frontal. Se volverá a llamar a la aplicación en su dirección URL principal.
En el registro de la aplicación no se necesita una dirección URL de cierre de sesión del canal frontal.
En el registro de la aplicación no se necesita una dirección URL de cierre de sesión del canal frontal.
Durante el registro de la aplicación, no es necesario registrar una dirección URL de cierre de sesión de canal frontal. Se volverá a llamar a la aplicación en su dirección URL principal.
En ASP.NET, seleccionar el botón Cerrar sesión en la aplicación web desencadena la acción SignOut en el controlador AccountController (más información a continuación).
En ASP.NET MVC, el botón de inicio de sesión se expone en Views\Shared\_LoginPartial.cshtml. Solo se muestra cuando hay una cuenta autenticada. Es decir, se muestra cuando el usuario ha iniciado sesión previamente.
En las versiones anteriores de las plantillas de ASP.NET Core, el controlador Account se incrustó con la aplicación web. Ya no es así porque, ahora, el controlador forma parte del paquete NuGet Microsoft.Identity.Web.UI. Consulte AccountController.cs para obtener más información.
Establece un URI de redirección de OpenID a /Account/SignedOut para volver a llamar al controlador cuando Microsoft Entra ID haya completado el cierre de sesión.
Llama a Signout(), que permite que el middleware de OpenID Connect se ponga en contacto con el punto de conexión logout de la Plataforma de identidad de Microsoft. A continuación, el punto de conexión:
Borra la cookie de sesión del explorador.
Devuelve el URI de redireccionamiento posterior al cierre de sesión. De forma predeterminada, el URI de redireccionamiento posterior al cierre de sesión muestra la página de vista que ha cerrado la sesión SignedOut.cshtml.cs. Esta página también se proporciona como parte de Microsoft.Identity.Web.
En ASP.NET, el cierre de sesión se desencadena desde el método SignOut() en un controlador (por ejemplo, AccountController.cs#L25-L31). Este método no forma parte del marco de .NET, al contrario de lo que sucede en ASP.NET Core. Este:
Envía un desafío de cierre de sesión de OpenID.
Elimina la caché.
Redirige a la página que quiere.
/// <summary>
/// Send an OpenID Connect sign-out request.
/// </summary>
public void SignOut()
{
HttpContext.GetOwinContext()
.Authentication
.SignOut(CookieAuthenticationDefaults.AuthenticationType);
Response.Redirect("/");
}
En Java, el cierre de sesión se controla llamando directamente al punto de conexión logout de la Plataforma de identidad de Microsoft y proporcionando el valor post_logout_redirect_uri. Para obtener detalles, consulte AuthPageController.java#L50-L60
Cuando el usuario selecciona el botón Cerrar sesión, la aplicación desencadena la ruta /auth/signout, que destruye la sesión y redirige el explorador al punto de conexión de cierre de sesión de la Plataforma de identidad de Microsoft.
logout(options = {}) {
return (req, res, next) => {
/**
* Construct a logout URI and redirect the user to end the
* session with Azure AD. For more information, visit:
* https://docs.microsoft.com/azure/active-directory/develop/v2-protocols-oidc#send-a-sign-out-request
*/
let logoutUri = `${this.msalConfig.auth.authority}/oauth2/v2.0/`;
if (options.postLogoutRedirectUri) {
logoutUri += `logout?post_logout_redirect_uri=${options.postLogoutRedirectUri}`;
}
req.session.destroy(() => {
res.redirect(logoutUri);
});
}
}
Cuando el usuario selecciona Cerrar sesión, la aplicación activa la ruta logout, que redirige el explorador al extremo de cierre de sesión de la plataforma de identidad de Microsoft.
El middleware OpenID Connect de ASP.NET Core permite a la aplicación interceptar la llamada al punto de conexión logout de la Plataforma de identidad de Microsoft al proporcionar un evento de OpenID Connect denominado OnRedirectToIdentityProviderForSignOut. Microsoft.Identity.Web lo controla automáticamente, lo que borra las cuentas en el caso de que la aplicación web llame a las API web.
En ASP.NET, se delega en el middleware la ejecución del cierre de sesión, borrando la cookie de sesión:
public class AccountController : Controller
{
...
public void EndSession()
{
Request.GetOwinContext().Authentication.SignOut();
Request.GetOwinContext().Authentication.SignOut(Microsoft.AspNet.Identity.DefaultAuthenticationTypes.ApplicationCookie);
this.HttpContext.GetOwinContext().Authentication.SignOut(CookieAuthenticationDefaults.AuthenticationType);
}
}
En el inicio rápido de Java, el URI de redireccionamiento posterior al cierre de sesión solo muestra la página index.html.
En el inicio rápido de Node, el URI de redireccionamiento posterior al cierre de sesión se usará para redirigir el explorador a la página principal de ejemplo una vez que el usuario complete el proceso de cierre de sesión con la Plataforma de identidad de Microsoft.
En el inicio rápido de Python, el URI de redireccionamiento posterior al cierre de sesión solo muestra la página index.html.
Protocolo
Si quiere más información sobre el cierre de sesión, lea la documentación del protocolo, que está disponible en OpenID Connect.
Pasos siguientes
Para más información, cree una aplicación web de ASP.NET Core que inicie sesión a los usuarios en las siguientes series de tutoriales de varias partes