Skip to content

Entra ID ​

Entra ID gestiona tanto usuarios y grupos como privilegios de acceso según roles. Es decir, proporciona un servicio IAM (Identity and Access Management) que incluye identidad, autenticación y autorización. Resulta especialmente robusto para entornos corporativos por su relación estrecha con el Active Directory (directorios heredados) y las cuentas de Microsoft 365. De hecho, si se compara con los servicios de AWS, necesitarías AWS IAM + Cognito para cubrir las mismas funciones.

Conceptos clave: Usuarios, Grupos y Roles... ​

Antes de empezar a administrar Entra ID (por ejemplo, con Azure CLI), es importante entender los aspectos principales:

Usuarios ​

  • Representan personas (empleados, contratistas, socios, etc.) que se autentican
  • Cada usuario tiene una identidad única (correo electrónico, nombre de usuario, etc.)
  • Los usuarios pueden tener asignaciones de roles (permisos de acceso) y pertenecer a grupos
  • Los tres tipos de usuarios que se gestionan son: usuarios, invitados y usuarios sincronizados (creados en el Active Directory)

Grupos ​

  • Contenedores lógicos para agrupar usuarios (y a veces recursos)
  • Simplifican la administración de permisos, de este modo, al asignar roles a un grupo afecta a todos sus miembros
  • Ejemplos típicos: "Equipo de Marketing", "Desarrolladores", "Gerentes"

Roles ​

  • La autorización se realiza mediante roles, los cuales se asignan a usuarios o grupos para otorgar acceso
  • Estos definen permisos específicos (qué acciones un usuario puede realizar). Como principio se suelen asignar permisos mínimos
  • Por ejemplo: "Administrador Global" (acceso completo), "Lector" (solo consultar), "Colaborador" (administrar recursos)

Permisos ​

  • Los permisos son las acciones individuales permitidas (ej. read, write, delete)
  • Entra ID permite un control granular: se pueden restringir permisos por recurso, aplicación o alcance
  • Los permisos se heredan: un usuario en un grupo con un rol hereda automáticamente los permisos de ese rol
  • Para ver los grupos (y, por tanto, los roles que heredan) de un usuario: az ad user get-member-groups --id "correo-o-id-del-usuario"

Usando Azure CLI con Entra ID ​

La línea de comandos es una buena opción para administrar los servicios Azure. Basándonos en el sitio de documentación de Azure CLI, podemos instalarlo en Windows del modo acostumbrado (descargando y siguiendo el instalador).

Para MacOS se puede instalar ejecutando:

bash
brew install azure-cli

Desde una terminal (o PowerShell en Windows) estaremos habilitados para ejecutar la primera sentencia de login, así:

bash
az login

Esto abre el navegador para ingresar tu usuario y al conectarse te preguntará el número de tenant, por defecto 1.
Personalmente me resulta más sencillo que con AWS

Una vez autenticado, los comandos az ad son la forma principal de interactuar con Entra ID. A continuación expongo algunos ejemplos básicos...

Listar usuarios ​

bash
az ad user list --filter "displayName eq 'Juan Pérez'"

Crear un usuario básico ​

bash
az ad user create \
  --user-principal-name [email protected] \
  --display-name "Juan Pérez" \
  --password 'Password123!' \
  --force-change-password-next-login true

Listar grupos ​

bash
az ad group list

Asignar un usuario a un grupo ​

bash
az ad group member add \
  --group "NombreDelGrupo" \
  --member-id "id-del-usuario"

Asignar un rol de directorio a un usuario ​

Los roles corporativos como Administrador Global son roles de directorio; az ad no tiene un comando propio para asignarlos, así que se invoca Microsoft Graph con az rest:

bash
# 1. Obtener el object id del usuario
USER_ID=$(az ad user show --id [email protected] --query id -o tsv)

# 2. Asignar el rol de directorio (roleDefinitionId de "Administrador Global")
az rest --method POST \
  --url "https://graph.microsoft.com/v1.0/roleManagement/directory/roleAssignments" \
  --body "{\"principalId\": \"$USER_ID\", \"roleDefinitionId\": \"62e90394-69f5-4237-9190-012177145e10\", \"directoryScopeId\": \"/\"}"

Nota: El roleDefinitionId mostrado corresponde al rol Administrador Global. Los roles se asocian por defecto a todo el directorio (directoryScopeId: "/"). Para otros roles se puede consultar el catálogo con az rest ... roleDefinitions. Son asignaciones de máximo privilegio: conviene usarlas con moderación y preferir PIM (Privileged Identity Management) para elevaciones temporales.

Nota: Los comandos az ad permiten gestionar prácticamente cualquier aspecto de Entra ID: usuarios, grupos, aplicaciones, permisos y roles. Puedes ver la ayuda disponible con az ad --help o az ad <comando> --help.

Ejemplo de OAuth 2.0 con PKCE ​

OAuth 2.0 con PKCE (Proof Key for Code Exchange) es el flujo recomendado para aplicaciones SPA y móviles. Añade una capa de seguridad adicional usando códigos verificadores.

1. Registrar la aplicación ​

bash
az ad app create \
  --display-name "Mi App SPA" \
  --spa-redirect-uris "http://localhost:3000"

Anota el Application (client) ID que devuelve el comando: lo usarás como client_id en los ejemplos siguientes. Para una SPA el redirect URI se registra bajo la plataforma Single-Page App con --spa-redirect-uris (y por seguridad debe ser https, salvo al probar en local con http://localhost). Como la app usa PKCE (cliente público), no se genera client secret.

2. Flujo con PKCE (ejemplo en JavaScript) ​

javascript
// Generar un code_verifier aleatorio (43-128 caracteres)
const generateCodeVerifier = () => {
  const array = new Uint8Array(32);
  crypto.getRandomValues(array);
  return btoa(String.fromCharCode(...array))
    .replace(/\+/g, '-')
    .replace(/\//g, '_')
    .replace(/=/g, '');
};

// Generar un code_challenge a partir del verifier (S256)
const generateCodeChallenge = async (verifier) => {
  const encoder = new TextEncoder();
  const data = encoder.encode(verifier);
  const digest = await crypto.subtle.digest('SHA-256', data);
  return btoa(String.fromCharCode(...new Uint8Array(digest)))
    .replace(/\+/g, '-')
    .replace(/\//g, '_')
    .replace(/=/g, '');
};

// Paso 1: Redirigir al usuario a autorizar
const authorize = async () => {
  const codeVerifier = generateCodeVerifier();
  const codeChallenge = await generateCodeChallenge(codeVerifier);
  
  sessionStorage.setItem('code_verifier', codeVerifier);

  const params = new URLSearchParams({
    client_id: 'tu-client-id',
    response_type: 'code',
    redirect_uri: 'http://localhost:3000',
    scope: 'openid profile email offline_access https://graph.microsoft.com/user.read',
    state: 'random-state-value',
    code_challenge: codeChallenge,
    code_challenge_method: 'S256'
  });

  window.location.href = 
    `https://login.microsoftonline.com/tu-tenant-id/oauth2/v2.0/authorize?${params}`;
};

// Paso 2: Intercambiar el código por tokens
const callback = async () => {
  const urlParams = new URLSearchParams(window.location.search);
  const code = urlParams.get('code');
  const codeVerifier = sessionStorage.getItem('code_verifier');

  const response = await fetch(
    'https://login.microsoftonline.com/tu-tenant-id/oauth2/v2.0/token',
    {
      method: 'POST',
      headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
      body: new URLSearchParams({
        client_id: 'tu-client-id',
        grant_type: 'authorization_code',
        code: code,
        redirect_uri: 'http://localhost:3000',
        code_verifier: codeVerifier
      })
    }
  );

  const { access_token, id_token, refresh_token } = await response.json();
  console.log('Access Token:', access_token);
};

3. Verbos curl equivalentes ​

bash
# Paso 1: Redirigir al navegador a autorizar
open "https://login.microsoftonline.com/{tenant-id}/oauth2/v2.0/authorize?\
client_id={client-id}\
&response_type=code\
&redirect_uri=http://localhost:3000\
&scope=openid%20profile%20email%20offline_access%20https://graph.microsoft.com/user.read\
&code_challenge={code-challenge}\
&code_challenge_method=S256"

# Paso 2: Intercambiar código por tokens
curl -X POST https://login.microsoftonline.com/{tenant-id}/oauth2/v2.0/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "client_id={client-id}" \
  -d "grant_type=authorization_code" \
  -d "code={authorization-code}" \
  -d "redirect_uri=http://localhost:3000" \
  -d "code_verifier={code-verifier}"

Resumen del flujo PKCE:

  1. La app genera un code_verifier y su code_challenge (hash SHA-256)
  2. Se redirige al usuario a autorizar
  3. Se devuelve un code temporal
  4. La app intercambia code + code_verifier por access_token
  5. El servidor valida que el code_verifier coincida con el code_challenge original

Probar PKCE con GitHub (alternativa práctica) ​

Los ejemplos anteriores muestran el flujo sobre Entra ID, pero para probar los mismos conceptos sin depender de un tenant corporativo puedes usar GitHub OAuth Apps, que sigue el mismo authorization_code (aquí GitHub no exige PKCE porque el cliente usa client secret, pero el mecanismo del code es idéntico).

1. Crear una OAuth App en GitHub ​

Ve a Settings → Developer settings → OAuth Apps → New OAuth App y configura:

  • Homepage URL: http://localhost:3000
  • Authorization callback URL: http://localhost:3000/callback (GitHub sí requiere esta ruta)
  • Al crearla, copia el Client ID y el Client secret.

2. Redirigir al usuario a autorizar (curl) ​

bash
open "https://github.com/login/oauth/authorize?\
client_id={client-id}\
&scope=read:user\
&redirect_uri=http://localhost:3000/callback"

El usuario autoriza y GitHub responde redirigiendo a tu app con ?code={authorization-code}&state=... en la URL.

3. Intercambiar el código por un token ​

bash
curl -X POST https://github.com/login/oauth/access_token \
  -H "Accept: application/json" \
  -d "client_id={client-id}" \
  -d "client_secret={client-secret}" \
  -d "code={authorization-code}" \
  -d "redirect_uri=http://localhost:3000/callback"

Nota: GitHub devuelve {"access_token": "...", "token_type": "bearer"} en JSON. Ese access_token es el equivalente al de los ejemplos con Entra ID y puedes usarlo para llamar a la API de GitHub (curl -H "Authorization: Bearer {access_token}" https://api.github.com/user).