Integrar Azure Active Directory B2C con ASP.NET MVC

Revisando la documentación oficial para integrar Azure Active Directory B2C con una aplicación ASP.NET MVC quizás no quede muy claro cuál es el proceso, ya que se basa en un proyecto ya creado y preconfigurado. En este post te voy a mostrar los pasos que necesitas para integrarlo desde el principio, con el fin de que entiendas bien cada uno de ellos y puedas adaptarlo a cualquier proyecto que tengas e  incluso en uno nuevo. En este articulo se obvia la creación del directorio, ya que puedes guiarte a través de aquí, y la creación de las políticas que puedes configurarlas siguiendo este otro apartado.

Crea un proyecto ASP.NET MVC sin ningún tipo de autenticación predefinida

En este ejemplo partimos un de proyecto nuevo, aunque puedes integrarlo con uno que ya tengas. Para este primer paso sólo necesitas asegurarte de que no hay seleccionado ningún método de autenticación durante su creación:

B2CWebApp – No Authentication

Habilita SSL para el proyecto y modifica la URL de arranque

Para este tipo de integraciones es necesario que la comunicación sea segura de extremo a extremo. Por ello, debes habilitar SSL en el proyecto web que acabas de crear. Haz clic sobre el proyecto web y en sus propiedades cambia el valor de  SSL Enabled a True.

B2CWebApp – SSL Enabled

Copia la URL que aparece en el apartado SSL URL, justo debajo de SSL Enabled, y abre las propiedades del proyecto haciendo clic  sobre él con el botón derecho y seleccionando Properties. Accede al apartado Web y copia la URL en el campo Project Url.

B2CWebApp – SSL URL to Project URL

Instalar los nugets de Owin

Es necesario que instales los siguientes paquetes para Owin, ya que te ayudarán tanto a recuperar los token como validar los mismos dentro de tu aplicación:

  • Microsoft.Owin.Security.OpenIdConnect
  • Microsoft.Owin.Security.Cookies
  • Microsoft.Owin.Host.SystemWeb
  • Microsoft.IdentityModel.Protocol.Extensions (Actualizalo).

Valores en el web.config

Dentro del apartado App Settings es necesario que crees algunas claves con los valores propios de tu tenant de AAD B2C:

[xml] <appSettings>
<add key="webpages:Version" value="3.0.0.0" />
<add key="webpages:Enabled" value="false" />
<add key="ClientValidationEnabled" value="true" />
<add key="UnobtrusiveJavaScriptEnabled" value="true" />

<!– Azure Active Directory B2C configuration –>
<add key="ida:Tenant" value="returngisb2c.onmicrosoft.com" />
<add key="ida:ClientId" value="fcc40767-1d1a-474f-9d86-c514c5cfa986" />
<add key="ida:AadInstance" value="https://login.microsoftonline.com/{0}/v2.0/.well-known/openid-configuration?p={1}" />
<add key="ida:SignUpPolicyId" value="B2C_1_SignUpWebApp" />
<add key="ida:SignInPolicyId" value="B2C_1_SignInWebApp" />
<add key="ida:UserProfilePolicyId" value="B2C_1_EditingProfileWebApp" />
<add key="ida:RedirectUri" value="https://localhost:44365/" />
<!– End Azure Active Directory B2C configuration–>

</appSettings>[/xml]

Para que te hagas una idea de cuáles son los valores para dichas claves he dejado unos de ejemplo:

  • Tenant: el nombre que elegiste para tu directorio.
  • ClientId: el Id de la aplicación que debes crear para asociar tu aplicación web con tu directorio. Puedes ver los pasos aquí.
  • AddInstance: este valor puedes dejarlo tal cual está, ya que se concatenará en el siguiente paso.
  • SignUpPolicyId: se trata del nombre de la política de tipo SignUp, puedes crearla siguiendo estos pasos.
  • SignInPolicyId: nombre de la política de tipo SignIn.
  • UserProfilePolicyId: nombre de la política de tipo Profile Editing.
  • RedirectUri: debe coincidir con la URL de tu sitio local (puedes recuperarla del apartado SSL URL visto anteriormente).

Clase StartUp

Para poder gestionar la autenticación con Owin y AAD B2C es necesario configurar la aplicación a través de un archivo llamado StartUp.cs. Crea este nuevo archivo dentro de la carpeta App_Start y no te olvides de eliminar .App_Start del namespace de la clase para que siga la convención de Owin y no sea necesario un paso adicional. Este es el contenido de la clase:

[csharp]using Microsoft.IdentityModel.Protocols;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Cookies;
using Microsoft.Owin.Security.Notifications;
using Microsoft.Owin.Security.OpenIdConnect;
using Owin;
using System;
using System.Configuration;
using System.IdentityModel.Tokens;
using System.Threading.Tasks;

namespace B2CWebApp
{
public class Startup
{
// B2C settings
private static string clientId = ConfigurationManager.AppSettings["ida:ClientId"];
private static string aadInstance = ConfigurationManager.AppSettings["ida:AadInstance"];
private static string tenant = ConfigurationManager.AppSettings["ida:Tenant"];
private static string redirectUri = ConfigurationManager.AppSettings["ida:RedirectUri"];

// B2C policies
public static string SignUpPolicyId = ConfigurationManager.AppSettings["ida:SignUpPolicyId"];
public static string SignInPolicyId = ConfigurationManager.AppSettings["ida:SignInPolicyId"];
public static string ProfilePolicyId = ConfigurationManager.AppSettings["ida:UserProfilePolicyId"];

public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
}

public void ConfigureAuth(IAppBuilder app)
{
//Authetication type: cookies
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);

//Default cookie configuration
app.UseCookieAuthentication(new CookieAuthenticationOptions());

// Configure OpenID Connect middleware for each policy
app.UseOpenIdConnectAuthentication(CreateOptionsFromPolicy(SignUpPolicyId));
app.UseOpenIdConnectAuthentication(CreateOptionsFromPolicy(ProfilePolicyId));
app.UseOpenIdConnectAuthentication(CreateOptionsFromPolicy(SignInPolicyId));
}

private Task AuthenticationFailed(AuthenticationFailedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)
{
notification.HandleResponse();
if (notification.Exception.Message == "access_denied")
{
notification.Response.Redirect("/");
}
else
{
notification.Response.Redirect("/Home/Error?message=" + notification.Exception.Message);
}

return Task.FromResult(0);
}

private OpenIdConnectAuthenticationOptions CreateOptionsFromPolicy(string policy)
{
return new OpenIdConnectAuthenticationOptions
{
// For each policy, give OWIN the policy-specific metadata address, and
// set the authentication type to the id of the policy
MetadataAddress = String.Format(aadInstance, tenant, policy),
AuthenticationType = policy,

// These are standard OpenID Connect parameters, with values pulled from web.config
ClientId = clientId,
RedirectUri = redirectUri,
PostLogoutRedirectUri = redirectUri,
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = AuthenticationFailed
},
Scope = "openid",
ResponseType = "id_token",

// It is used for displaying the user’s name in the navigation bar.
TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "name",
SaveSigninToken = true
}
};
}
}
}[/csharp]

Como puedes ver, se recuperan los valores que definimos en el web.config y se configura el tipo de autenticación, las políticas y el comportamiento que debe tener si la autenticación falla.

Controlador Account Controller

Por último vas a agregar un controlador que te permita llamar a cada una de las políticas de Azure Active Directory B2C con el fin de que tus usuarios puedan registrarse, iniciar sesión, modificar su perfil y cerrar sesión.

[csharp]using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace B2CWebApp.Controllers
{
public class AccountController : Controller
{
public void SignIn()
{
if (!Request.IsAuthenticated)
{
HttpContext.GetOwinContext().Authentication.Challenge(
new Microsoft.Owin.Security.AuthenticationProperties() { RedirectUri = "/" }, Startup.SignInPolicyId);
}
}

public void SignUp()
{
if (!Request.IsAuthenticated)
{
HttpContext.GetOwinContext().Authentication.Challenge(
new Microsoft.Owin.Security.AuthenticationProperties() { RedirectUri = "/" }, Startup.SignUpPolicyId);
}
}

public void Profile()
{
if (Request.IsAuthenticated)
{
HttpContext.GetOwinContext().Authentication.Challenge(
new Microsoft.Owin.Security.AuthenticationProperties() { RedirectUri = "/" }, Startup.ProfilePolicyId);
}
}

public void SignOut()
{
//To Sign out the user, you should issue an OpenIDConnect sign out request.
if (Request.IsAuthenticated)
{
var authTypes = HttpContext.GetOwinContext().Authentication.GetAuthenticationTypes();
HttpContext.GetOwinContext().Authentication.SignOut(authTypes.Select(t => t.AuthenticationType).ToArray());
}
}
}
}[/csharp]

Enlazamos estas acciones en el Layout de nuestra aplicación:

[html highlight=»27-47″]<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>@ViewBag.Title – My ASP.NET Application</title>
@Styles.Render("~/Content/css")
@Scripts.Render("~/bundles/modernizr")
</head>
<body>
<div class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
@Html.ActionLink("Application name", "Index", "Home", new { area = "" }, new { @class = "navbar-brand" })
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li>@Html.ActionLink("Home", "Index", "Home")</li>
<li>@Html.ActionLink("About", "About", "Home")</li>
<li>@Html.ActionLink("Contact", "Contact", "Home")</li>
</ul>
@if (Request.IsAuthenticated)
{
<text>
<ul class="nav navbar-nav navbar-right">
<li>
<a>@User.Identity.Name</a>
</li>
<li>@Html.ActionLink("Edit Profile", "Profile", "Account")</li>
<li>
@Html.ActionLink("Sign out", "SignOut", "Account")
</li>
</ul>
</text>
}
else
{
<ul class="nav navbar-nav navbar-right">
<li>@Html.ActionLink("Sign up", "SignUp", "Account")</li>
<li>@Html.ActionLink("Sign in", "SignIn", "Account")</li>
</ul>
}
</div>
</div>
</div>
<div class="container body-content">
@RenderBody()
<hr />
<footer>
<p>&copy; @DateTime.Now.Year – My ASP.NET Application</p>
</footer>
</div>

@Scripts.Render("~/bundles/jquery")
@Scripts.Render("~/bundles/bootstrap")
@RenderSection("scripts", required: false)
</body>
</html>
[/html]

Si no lo habías hecho antes, añade la URL del proyecto dentro de la sección Reply URL de la aplicación creada dentro de Azure Active Directory B2C:

B2CWebApp – Reply URL

Por último, ejecuta la aplicación e intenta registrar un nuevo usuario con uno de los proveedores que hayas elegido:

B2CWebApp – Logged in

¡Saludos!

1 Comment

  1. Domingo Antonio Gonzalez mayo 12, 2020 at 4:02 pm

    Hola Gisela, muchas gracias por compartir soy novato con MVC en net, este tutorial me ha sido de mucha ayuda, aunque el post tiene ya algo de tiempo tengo que implementar algo similar.

    Tengo varias dudas

    tengo una aplicación ya existente en c# net, pero que no es MVC y se requiere que implemente un login contra el AAD, tengo ya todo listo del lado del AAD, sin embargo en la parte complicada del aplicativo se me ha complicado, como hago para tener un login separado de app web que me permita logearme y utilizar la cookie de sesion creada en mi app web, básicamente lo que necesito son los datos del usuario en AD, implemente una solucion a través de una api y funciona, sin embargo desean que la sesión permita ingresar a por ejemplo office 365.

    se que es un mar de cosas pero me gustaría que me ayudes con orientación, esto es posible

    mi flujo en resumen seria el siguiente.

    LOGIN CON ESTA IMPLEMENTAN – > si correcto redirect a mi web app y crear una cookie o utilizar algún dato para hacer llamadas a ADD dentro de mi web actual.

    Reply ↓

Deja un comentario

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.