asp.net mvc - microsoft - Autorizar por grupo en Azure Active Directory B2C
azure portal4 (3)
Esto funcionará, sin embargo, debe escribir un par de líneas de código en su lógica de autenticación para lograr lo que busca.
En primer lugar, debe distinguir entre
Roles
y
Groups
en Azure AD (B2C).
User Role
es muy específico y solo es válido dentro de Azure AD (B2C).
El Rol define qué permisos tiene
Azure Azure AD dentro
del usuario.
Group
(o
Security Group
) define la pertenencia al grupo de usuarios, que puede exponerse a las aplicaciones externas.
Las aplicaciones externas pueden modelar el
control de acceso basado en roles
sobre los
grupos
de
seguridad
.
Sí, sé que puede sonar un poco confuso, pero eso es lo que es.
Por lo tanto, su primer paso es modelar sus
Groups
en Azure AD B2C: debe crear los grupos y asignar usuarios manualmente a esos grupos.
Puede hacerlo en el Portal de Azure (
https://portal.azure.com/
):
Luego, de vuelta a su aplicación, deberá codificar un poco y solicitar a la API de Azure AD B2C Graph API para membresías de usuarios una vez que el usuario se haya autenticado correctamente. Puede usar esta muestra para inspirarse en cómo obtener membresías de grupos de usuarios. Es mejor ejecutar este código en una de las notificaciones de OpenID (es decir, SecurityTokenValidated ) y agregar el rol de usuario a ClaimsPrincipal.
Una vez que cambie ClaimsPrincipal para que tenga los grupos de seguridad de Azure AD y los valores de "Reclamación de roles", podrá usar el atributo Authrize con la función Roles. Esto es realmente 5-6 líneas de código.
Finalmente, puede dar su voto para la función aquí: https://feedback.azure.com/forums/169401-azure-active-directory/suggestions/10123836-get-user-membership-groups-in-the-claims-with-ad-b para obtener un reclamo de membresía grupal sin tener que consultar Graph API para eso.
Estoy tratando de descubrir cómo autorizar el uso de grupos en Azure Active Directory B2C. Puedo autorizar a través del usuario, por ejemplo:
[Authorize(Users="Bill")]
Sin embargo, esto no es muy efectivo y veo muy pocos casos de uso para esto. Una solución alternativa sería Autorizar por rol. Sin embargo, por alguna razón, eso no parece sorprender. Si le doy a un usuario la función "Administrador global", por ejemplo, e intento:
[Authorize(Roles="Global Admin")]
No funciona. ¿Hay alguna forma de autorizar a través de Grupos o Roles?
Obtener membresías grupales para un usuario de Azure AD requiere mucho más que solo "un par de líneas de código", por lo que pensé en compartir lo que finalmente funcionó para mí para salvar a otros unos días de tirones de cabello y cabeza. golpeando
Comencemos agregando las siguientes dependencias a project.json:
"dependencies": {
...
"Microsoft.IdentityModel.Clients.ActiveDirectory": "3.13.8",
"Microsoft.Azure.ActiveDirectory.GraphClient": "2.0.2"
}
La primera es necesaria ya que necesitamos autenticar nuestra aplicación para que pueda acceder a la API de gráficos AAD. La segunda es la biblioteca de cliente Graph API que usaremos para consultar membresías de usuarios. No hace falta decir que las versiones solo son válidas en el momento de este escrito y pueden cambiar en el futuro.
A continuación, en el método Configure () de la clase Startup, quizás justo antes de configurar la autenticación OpenID Connect, creamos el cliente Graph API de la siguiente manera:
var authContext = new AuthenticationContext("https://login.microsoftonline.com/<your_directory_name>.onmicrosoft.com");
var clientCredential = new ClientCredential("<your_b2c_app_id>", "<your_b2c_secret_app_key>");
const string AAD_GRAPH_URI = "https://graph.windows.net";
var graphUri = new Uri(AAD_GRAPH_URI);
var serviceRoot = new Uri(graphUri, "<your_directory_name>.onmicrosoft.com");
this.aadClient = new ActiveDirectoryClient(serviceRoot, async () => await AcquireGraphAPIAccessToken(AAD_GRAPH_URI, authContext, clientCredential));
ADVERTENCIA: NO codifique la clave secreta de su aplicación, sino guárdela en un lugar seguro. Bueno, ya lo sabías, ¿verdad? :)
El método asincrónico AcquireGraphAPIAccessToken () que entregamos al constructor del cliente AD se llamará cuando sea necesario cuando el cliente necesite obtener el token de autenticación. Así es como se ve el método:
private async Task<string> AcquireGraphAPIAccessToken(string graphAPIUrl, AuthenticationContext authContext, ClientCredential clientCredential)
{
AuthenticationResult result = null;
var retryCount = 0;
var retry = false;
do
{
retry = false;
try
{
// ADAL includes an in-memory cache, so this will only send a request if the cached token has expired
result = await authContext.AcquireTokenAsync(graphAPIUrl, clientCredential);
}
catch (AdalException ex)
{
if (ex.ErrorCode == "temporarily_unavailable")
{
retry = true;
retryCount++;
await Task.Delay(3000);
}
}
} while (retry && (retryCount < 3));
if (result != null)
{
return result.AccessToken;
}
return null;
}
Tenga en cuenta que tiene un mecanismo de reintento incorporado para manejar condiciones transitorias, que puede adaptar a las necesidades de su aplicación.
Ahora que nos hemos ocupado de la autenticación de la aplicación y la configuración del cliente AD, podemos seguir adelante y aprovechar los eventos de OpenIdConnect para finalmente usarlo.
De vuelta en el método Configure () donde normalmente llamaríamos a
app.UseOpenIdConnectAuthentication()
y creamos una instancia de OpenIdConnectOptions, agregamos un controlador de eventos para el evento OnTokenValidated:
new OpenIdConnectOptions()
{
...
Events = new OpenIdConnectEvents()
{
...
OnTokenValidated = SecurityTokenValidated
},
};
El evento se activa cuando se ha obtenido, validado y establecido la identidad del usuario, el token de acceso para el usuario de inicio de sesión. (¡No debe confundirse con el token de acceso propio de la aplicación requerido para llamar a AAD Graph API!) Parece un buen lugar para consultar Graph API para membresías de grupos de usuarios y agregar esos grupos a la identidad, en forma de reclamos adicionales:
private Task SecurityTokenValidated(TokenValidatedContext context)
{
return Task.Run(async () =>
{
var oidClaim = context.SecurityToken.Claims.FirstOrDefault(c => c.Type == "oid");
if (!string.IsNullOrWhiteSpace(oidClaim?.Value))
{
var pagedCollection = await this.aadClient.Users.GetByObjectId(oidClaim.Value).MemberOf.ExecuteAsync();
do
{
var directoryObjects = pagedCollection.CurrentPage.ToList();
foreach (var directoryObject in directoryObjects)
{
var group = directoryObject as Group;
if (group != null)
{
((ClaimsIdentity)context.Ticket.Principal.Identity).AddClaim(new Claim(ClaimTypes.Role, group.DisplayName, ClaimValueTypes.String));
}
}
pagedCollection = pagedCollection.MorePagesAvailable ? await pagedCollection.GetNextPageAsync() : null;
}
while (pagedCollection != null);
}
});
}
Aquí se usa el tipo de reclamo de rol, sin embargo, podría usar uno personalizado.
Una vez hecho lo anterior, si está utilizando ClaimType.Role, todo lo que necesita hacer es decorar su clase o método de controlador de la siguiente manera:
[Authorize(Role = "Administrators")]
Eso es, por supuesto, siempre que tenga un grupo designado configurado en B2C con un nombre para mostrar de "Administradores".
Sin embargo, si elige usar un tipo de reclamo personalizado, necesitaría definir una política de autorización basada en el tipo de reclamo agregando algo como esto en el método ConfigureServices (), por ejemplo:
services.AddAuthorization(options => options.AddPolicy("ADMIN_ONLY", policy => policy.RequireClaim("<your_custom_claim_type>", "Administrators")));
y luego decorar una clase o método de controlador privilegiado de la siguiente manera:
[Authorize(Policy = "ADMIN_ONLY")]
Ok, ya hemos terminado? - Bueno no exactamente.
Si ejecutó su aplicación e intentó iniciar sesión, obtendría una excepción de Graph API que afirma "Privilegios insuficientes para completar la operación". Puede que no sea obvio, pero aunque su aplicación se autentica con éxito con AD usando su app_id y app_key, no tiene los privilegios necesarios para leer los detalles de los usuarios de su AD. Para otorgarle a la aplicación dicho acceso, decidí usar el Módulo Azure Active Directory para PowerShell
El siguiente guión me sirvió:
$tenantGuid = "<your_tenant_GUID>"
$appID = "<your_app_id>"
$userVal = "<admin_user>@<your_AD>.onmicrosoft.com"
$pass = "<admin password in clear text>"
$Creds = New-Object System.Management.Automation.PsCredential($userVal, (ConvertTo-SecureString $pass -AsPlainText -Force))
Connect-MSOLSERVICE -Credential $Creds
$msSP = Get-MsolServicePrincipal -AppPrincipalId $appID -TenantID $tenantGuid
$objectId = $msSP.ObjectId
Add-MsolRoleMember -RoleName "Company Administrator" -RoleMemberType ServicePrincipal -RoleMemberObjectId $objectId
¡Y ahora finalmente hemos terminado! ¿Cómo es eso de "un par de líneas de código"? :)
impliqué esto como está escrito, pero a partir de mayo de 2017 la línea
((ClaimsIdentity)context.Ticket.Principal.Identity).AddClaim(new Claim(ClaimTypes.Role, group.DisplayName, ClaimValueTypes.String));
necesita ser cambiado a
((ClaimsIdentity)context.Ticket.Principal.Identity).AddClaim(new Claim(ClaimTypes.Role, group.DisplayName));
Para que funcione con las últimas bibliotecas
Gran trabajo para el autor.
Además, si tiene un problema con Connect-MsolService que proporciona una actualización incorrecta de nombre de usuario y contraseña a la última lib