Búsqueda de sitios web

Funciones de Azure y autorización de Azure AD


Esta publicación es parte de una serie sobre Azure Functions y PowerShell. ¡Consulta la lista de otras publicaciones de la serie!

En la última publicación sobre Azure Functions y la autenticación de Azure AD, vimos cómo requerir autenticación para nuestra aplicación de funciones. Ahora que sabemos quién accede a nuestra función, es hora de echar un vistazo a Autorización, es decir, ahora que sabemos quiénes son, ¿deberíamos dejarles entrar?

Restringir el acceso a usuarios seleccionados

Como la aplicación está configurada actualmente, ya sea que usemos el modo rápido o avanzado, cualquier aplicación registrada, usuario o usuario invitado de nuestro inquilino puede iniciar sesión en nuestra aplicación. Hasta ahora sólo hemos configurado la parte de Autenticación. Ahora echemos un vistazo a algunas autorizaciones básicas.

Para proteger aún más nuestra Function App, no solo exigiremos que un usuario se autentique, sino que también necesitaremos que ese usuario esté autorizado o, en términos simples, solo queremos admitir usuarios seleccionados.

El primer paso al configurar la autorización es simplemente restringir el acceso a un grupo seleccionado de usuarios. Azure AD tiene soporte integrado para esto y solo necesitamos activarlo configurando nuestra aplicación.

Esto se puede hacer en el portal yendo a Aplicaciones empresariales en Azure AD, localice su aplicación, vaya a propiedades y establezca la propiedad "¿Se requiere asignación de usuario?" a “Sí”.

También se puede hacer con la CLI de Azure:

$ServerAppId = 'c491d3f8-0d15-4ea5-96fd-957601d579fa'
az ad sp update --id $ServerAppId --set appRoleAssignmentRequired=true

Al intentar navegar por mi aplicación, ahora debería aparecer el mensaje "No tienes permiso para ver este directorio o página".

Para obtener acceso nuevamente, agregue cada usuario a la aplicación. Podemos hacer esto en el Portal de Azure yendo a Azure Active Directory -> Aplicaciones empresariales, busque nuestra aplicación y en "Usuarios y grupos" agregue todos los usuarios a los que se les debe permitir acceder a nuestra aplicación.

También podemos hacer esto usando la API de Microsoft Graph. Nuevamente en este ejemplo usamos la CLI de Azure para llamar a Graph desde PowerShell.

Primero guardamos el id de la aplicación (clientId) de nuestra aplicación y el nombre de usuario de nuestro usuario en variables. Luego usamos az ad sp show para obtener nuestra entidad de servicio (llamada Aplicación Empresarial en el portal). Necesitamos enviar tres cosas a Microsoft Graph, el ResourceId (ID de objeto de la entidad de servicio), el ID de principal (el ID de objeto de nuestro usuario) y el ID del rol que queremos asignar.

Espera, ¿papel? ¿No hemos creado ningún rol?

No, no hemos creado ningún rol, y si nuestra entidad de servicio/aplicación Enterprice no tiene ningún rol, tiene algo llamado "Acceso predeterminado" en el portal, podemos hacer referencia a esto usando un guid vacío (" 00000000-0000-0000-0000-000000000000").

Como última nota, una vez más estamos canalizando nuestro cuerpo a ConvertTo-Json dos veces para escapar de todas las comillas (") con barra invertida (\).

Basta de hablar, aquí está el código:

$AppId = 'c491d3f8-0d15-4ea5-96fd-957601d579fa'
$UserPrincipalName = '[email '

$App = az ad sp show --id $AppId | ConvertFrom-Json
$principalId = az ad user show --id $UserPrincipalName --query 'objectId' -o tsv

$Body = @{
    appRoleId = [Guid]::Empty.Guid
    principalId = $principalId
    resourceId = $App.objectId
} | ConvertTo-Json -Compress | ConvertTo-Json
az rest --method post --uri "https://graph.microsoft.com/v1.0/users/$UserPrincipalName/appRoleAssignments" --body $Body --headers "Content-Type=application/json"

¡Eso sí que es útil! Pero ¿qué pasa si quiero crear roles personalizados? ¿Adivina qué? ¡Puede!

Autorización mediante roles personalizados

¡Creemos algunos roles de aplicación personalizados! ¿Por qué? Estos roles estarán disponibles en el token de acceso y se pueden usar en nuestro código de función para determinar si una solicitud está permitida o no.

Si no necesita ningún rol, puede omitir esta parte y continuar usando el rol "Acceso predeterminado", que está representado por un GUID vacío 00000000-0000-0000-0000-000000000000 como se indica arriba. Si crea funciones personalizadas, la función "Acceso predeterminado" ya no estará disponible. Intentar asignar DefaultAcccess a una aplicación con roles definidos generará un mensaje de error que indica: El permiso asignado no se encontró en la aplicación

¡Es hora de crear un rol personalizado! Necesitamos especificar qué tipo de objetos pueden tener el rol, darle al rol una descripción, un nombre para mostrar, una identificación y un valor. El valor es el nombre del rol y la identificación debe ser un GUID único. Estoy usando [guid]::NewGuid().Guid que generará un nuevo GUID.

Aquí hay un ejemplo:

$AppId = 'c491d3f8-0d15-4ea5-96fd-957601d579fa'
# Add app role to an app
$AppRole = @{
    allowedMemberTypes = @('User')
    description = 'Example of custom role'
    displayName = 'CustomRole'
    id = [guid]::NewGuid().Guid
    isEnabled = $true
    value = 'Custom'
} | ConvertTo-Json -Compress | ConvertTo-Json

# Update enterprise application
az ad sp update --id $AppId --add appRoles $AppRole

Aquí se documenta cómo se define un AppRole:
https://docs.microsoft.com/graph/api/resources/approle?view=graph-rest-1.0

Una vez que tengamos un rol configurado podemos asignarlo a un usuario. Esto se hace de manera similar a la anterior. Lo único nuevo es que ahora buscamos el ID de rol de la aplicación en $App.appRoles en lugar de usar un GUID vacío.

$AppId = 'c491d3f8-0d15-4ea5-96fd-957601d579fa'
$AppRoleValue = 'Custom'
$UserPrincipalName = '[email '

$App = az ad sp show --id $AppId | ConvertFrom-Json
$principalId = az ad user show --id $UserPrincipalName --query 'objectId' -o tsv

$Body = @{
    appRoleId = $App.appRoles.where{$_.Value -eq $AppRoleValue}.id
    principalId = $principalId
    resourceId = $App.objectId
} | ConvertTo-Json -Compress | ConvertTo-Json
az rest --method post --uri "https://graph.microsoft.com/beta/users/$UserPrincipalName/appRoleAssignments" --body $Body --headers "Content-Type=application/json"

Para obtener más información sobre cómo proteger una API web, consulte los escenarios API web protegida y Una API web que llama a API web.

Validar roles en su código

Al utilizar la función de autenticación sencilla de App Services, la validación del token se realiza por nosotros y no necesitamos manejar gran parte de ella, pero aún necesitamos decodificar el token de identificación y extraer la parte de roles si queremos validar roles separados. .

Esto no es tan difícil como parece. Recibimos el token de identificación en un encabezado llamado x-ms-token-aad-id-token. La autenticación fácil se asegura de que este encabezado no pueda ser proporcionado por el usuario y solo mediante autenticación fácil para que podamos confiar en el encabezado.

Para decodificar un token JWT, estoy usando la siguiente función.

function ConvertFrom-JwtToken {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory, ValueFromPipeline)]
        [string]
        $Token
    )
    
    process {
        $DecodedToken = $Token.Replace('-', '+').Replace('_', '/')

        $Header,$Payload,$Signature = $DecodedToken.Split('.') | ForEach-Object -Process {
            $String = $_
            switch($String.Length % 4) {
                0 {$String;break}
                1 {throw 'Invalid token'}
                2 {"$String==";break}
                3 {"$String=";break}
            }
        }

        [pscustomobject][ordered]@{
            Header = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($Header)) | ConvertFrom-Json
            Payload = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($Payload)) | ConvertFrom-Json
            Signature = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($Signature))

        }

    }

}

Podemos acceder a los encabezados en Azure Functions en el objeto Solicitud, aquí hay un ejemplo de cómo acceder al token de identificación, decodificarlo e imprimir los roles de un usuario:

$IDToken = $Request.Headers.'x-ms-token-aad-id-token' | ConvertFrom-JwtToken
$Roles = $IDToken.Payload.Roles
Write-Output $Roles

Artículos relacionados