Creación de un token de SAS de delegación de usuarios con Azure Blob Storage y JavaScript

En este artículo se muestra cómo crear un token de SAS de delegación de usuarios en la biblioteca cliente de Azure Blob Storage v12 para JavaScript. Una SAS de delegación de usuarios, introducida con la versión 2018-11-09, protegida con credenciales de Azure AD, solo se admite para Blob service para:

  • Conceder acceso a un contenedor existente.
  • Conceder acceso para crear, usar y eliminar blobs.

Para crear una firma de acceso compartido de delegación de usuarios, el cliente debe tener permisos para llamar a la operación blobServiceClient.getUserDelegationKey. La clave devuelta por esta operación se usa para firmar la firma de acceso compartido de delegación de usuarios. La entidad de seguridad que llama a esta operación debe tener asignado un rol RBAC que incluya la acción Microsoft.Storage/storageAccounts/blobServices/generateUserDelegationKey/.

Los permisos concedidos a un cliente que posea la firma de acceso compartido son la intersección de los permisos concedidos a la entidad de seguridad que solicitó la clave de delegación de usuarios y los concedidos al recurso en el token de SAS en el campo permisos firmados (sp). Si un permiso concedido a la entidad de seguridad a través de RBAC no se concede además en el token de SAS, no se concederá al cliente que intenta usar la firma de acceso compartido para acceder al recurso.

Los fragmentos de código de ejemplo están disponibles en GitHub como archivos de Node.js ejecutables.

Paquete (npm) | Ejemplos | Referencia de la API | Código fuente de la biblioteca | Hacer comentarios

Procedimientos recomendados para los tokens de SAS de delegación de usuarios

Dado que cualquier persona con el token de SAS puede usarlo para acceder al contenedor y los blobs, debe definirlo con los permisos más restrictivos que permitan que el token complete las tareas necesarias.

Procedimientos recomendados para los tokens de SAS

Uso de DefaultAzureCredential en la nube de Azure

Para autenticarse en Azure, sin secretos, configure la identidad administrada. Esto permite que el código use DefaultAzureCredential.

Para configurar la identidad administrada para la nube de Azure:

  • Creación de una entidad administrada
  • Establecimiento de los roles de almacenamiento adecuados para la identidad
  • Configuración del entorno de Azure para que funcione con la identidad administrada

Cuando se completen estas dos tareas, use DefaultAzureCredential en lugar de una cadena de conexión o una clave de cuenta. Esto permite que todos los entornos usen exactamente el mismo código fuente sin el problema de los secretos del código fuente.

Uso de DefaultAzureCredential para el entorno de desarrollo local

En el entorno de desarrollo local, la identidad de Azure (su cuenta de desarrollo personal o de desarrollo que use para iniciar sesión en Azure Portal) debe autenticarse en Azure para usar el mismo código en los entornos de ejecución locales y en la nube.

Contenedor: incorporación de las dependencias necesarias a la aplicación

Incluya las dependencias necesarias para crear un token de SAS de contenedor.

const {
    DefaultAzureCredential
} = require('@azure/identity');
const {
    ContainerClient,
    BlobServiceClient,
    ContainerSASPermissions,
    generateBlobSASQueryParameters,
    SASProtocol
} = require('@azure/storage-blob');

// used for local environment variables
require('dotenv').config();

Contenedor: obtención de las variables de entorno

El nombre de la cuenta de Blob Storage y el nombre del contenedor son los valores mínimos necesarios para crear un token de SAS de contenedor:

// Get environment variables for DefaultAzureCredential
const accountName = process.env.AZURE_STORAGE_ACCOUNT_NAME;
const containerName = process.env.AZURE_STORAGE_BLOB_CONTAINER_NAME;

Creación de una SAS con DefaultAzureCredential

Los pasos conceptuales siguientes son necesarios para crear un token de SAS con DefaultAzureCredential:

  • Configuración de DefaultAzureCredential
    • Desarrollo local: uso de la identidad personal y establecimiento de roles para el almacenamiento
    • Nube de Azure: creación de una identidad administrada
  • Uso de DefaultAzureCredential para obtener la clave de delegación de usuarios con UserDelegationKey
  • Uso de la clave de delegación de usuarios para construir el token de SAS con los campos adecuados con generateBlobSASQueryParameters

Contenedor: creación de un token de SAS con DefaultAzureCredential

Con la identidad configurada, use el código siguiente para crear el token de SAS de delegación de usuarios para una cuenta y un contenedor existentes:

// Server creates User Delegation SAS Token for container
async function createContainerSas() {

    // Get environment variables
    const accountName = process.env.AZURE_STORAGE_ACCOUNT_NAME;
    const containerName = process.env.AZURE_STORAGE_BLOB_CONTAINER_NAME;

    // Best practice: create time limits
    const TEN_MINUTES = 10 * 60 * 1000;
    const NOW = new Date();

    // Best practice: set start time a little before current time to 
    // make sure any clock issues are avoided
    const TEN_MINUTES_BEFORE_NOW = new Date(NOW.valueOf() - TEN_MINUTES);
    const TEN_MINUTES_AFTER_NOW = new Date(NOW.valueOf() + TEN_MINUTES);

    // Best practice: use managed identity - DefaultAzureCredential
    const blobServiceClient = new BlobServiceClient(
        `https://${accountName}.blob.core.windows.net`,
        new DefaultAzureCredential()
      );

    // Best practice: delegation key is time-limited  
    // When using a user delegation key, container must already exist 
    const userDelegationKey = await blobServiceClient.getUserDelegationKey(
        TEN_MINUTES_BEFORE_NOW, 
        TEN_MINUTES_AFTER_NOW
    );

    // Need only list permission to list blobs 
    const containerPermissionsForAnonymousUser = "l";

    // Best practice: SAS options are time-limited
    const sasOptions = {
        containerName,                                           
        permissions: ContainerSASPermissions.parse(containerPermissionsForAnonymousUser), 
        protocol: SASProtocol.HttpsAndHttp,
        startsOn: TEN_MINUTES_BEFORE_NOW,
        expiresOn: TEN_MINUTES_AFTER_NOW
    };
 
    const sasToken = generateBlobSASQueryParameters(
        sasOptions,
        userDelegationKey,
        accountName 
    ).toString();

    return sasToken;
}

El código de servidor anterior crea un flujo de valores para crear el token de SAS del contenedor:

Una vez creado el token de SAS del contenedor, puede proporcionárselo al cliente que consumirá el token. Después, este puede usarlo para enumerar los blobs de un contenedor. En un ejemplo de código de cliente se muestra cómo probar la SAS como consumidor.

Contenedor: uso del token de SAS

Una vez creado el token de SAS del contenedor, úselo. Como ejemplo del uso del token de SAS, puede hacer lo siguiente:

  • Construya una dirección URL completa, incluido el nombre del contenedor y la cadena de consulta. La cadena de consulta es el token de SAS.
  • Cree un ContainerClient con la dirección URL del contenedor.
  • Use el cliente: en este ejemplo, enumere los blobs del contenedor con listBlobsFlat.
// Client or another process uses SAS token to use container
async function listBlobs(sasToken){

    // Get environment variables
    const accountName = process.env.AZURE_STORAGE_ACCOUNT_NAME;
    const containerName = process.env.AZURE_STORAGE_BLOB_CONTAINER_NAME;
    
    // Create Url
    // SAS token is the query string with typical `?` delimiter
    const sasUrl = `https://${accountName}.blob.core.windows.net/${containerName}?${sasToken}`;
    console.log(`\nContainerUrl = ${sasUrl}\n`);

    // Create container client from SAS token url
    const containerClient = new ContainerClient(sasUrl);

    let i = 1;

    // List blobs in container
    for await (const blob of containerClient.listBlobsFlat()) {
        console.log(`Blob ${i++}: ${blob.name}`);
    }    
}

Blob: incorporación de las dependencias necesarias a la aplicación

Incluya las dependencias necesarias para crear un token de SAS de n blobs.

const {
    DefaultAzureCredential
} = require('@azure/identity');
const {
    BlockBlobClient,
    BlobServiceClient,
    BlobSASPermissions,
    generateBlobSASQueryParameters,
    SASProtocol
} = require('@azure/storage-blob');

// used for local environment variables
require('dotenv').config();

Blob: obtención de las variables de entorno

El nombre de la cuenta de Blob Storage y el nombre del contenedor son los valores mínimos necesarios para crear un token de SAS de blob:

const accountName = process.env.AZURE_STORAGE_ACCOUNT_NAME;
const containerName = process.env.AZURE_STORAGE_BLOB_CONTAINER_NAME;

Cuando necesite crear un token de SAS de blob, debe tener el nombre del blob para crear el token de SAS. Esto será predeterminado, como un nombre de blob aleatorio, un nombre de blob enviado por el usuario o un nombre generado a partir de la aplicación.

// Create random blob name for text file
const blobName = `${(0|Math.random()*9e6).toString(36)}.txt`;

Blob: creación de un token de SAS con DefaultAzureCredential

Con la identidad configurada, use el código siguiente para crear el token de SAS de delegación de usuarios para una cuenta y un contenedor existentes:

// Server creates User Delegation SAS Token for blob
async function createBlobSas(blobName) {

    // Get environment variables
    const accountName = process.env.AZURE_STORAGE_ACCOUNT_NAME;
    const containerName = process.env.AZURE_STORAGE_BLOB_CONTAINER_NAME;

    // Best practice: create time limits
    const TEN_MINUTES = 10 * 60 * 1000;
    const NOW = new Date();

    // Best practice: set start time a little before current time to 
    // make sure any clock issues are avoided
    const TEN_MINUTES_BEFORE_NOW = new Date(NOW.valueOf() - TEN_MINUTES);
    const TEN_MINUTES_AFTER_NOW = new Date(NOW.valueOf() + TEN_MINUTES);

    // Best practice: use managed identity - DefaultAzureCredential
    const blobServiceClient = new BlobServiceClient(
        `https://${accountName}.blob.core.windows.net`,
        new DefaultAzureCredential()
      );

    // Best practice: delegation key is time-limited  
    // When using a user delegation key, container must already exist 
    const userDelegationKey = await blobServiceClient.getUserDelegationKey(
        TEN_MINUTES_BEFORE_NOW, 
        TEN_MINUTES_AFTER_NOW
    );

    // Need only create/write permission to upload file
    const blobPermissionsForAnonymousUser = "cw"

    // Best practice: SAS options are time-limited
    const sasOptions = {
        blobName,
        containerName,                                           
        permissions: BlobSASPermissions.parse(blobPermissionsForAnonymousUser), 
        protocol: SASProtocol.HttpsAndHttp,
        startsOn: TEN_MINUTES_BEFORE_NOW,
        expiresOn: TEN_MINUTES_AFTER_NOW
    };
 
    const sasToken = generateBlobSASQueryParameters(
        sasOptions,
        userDelegationKey,
        accountName 
    ).toString();

    return sasToken;
}

El código anterior crea un flujo de valores para crear el token de SAS del contenedor:

Una vez creado el token de SAS del blob, puede proporcionárselo al cliente que consumirá el token. Después, el cliente podrá usarlo para cargar un blob. En un ejemplo de código de cliente se muestra cómo probar la SAS como consumidor.

Blob: uso del token de SAS

Una vez creado el token de SAS de blob, úselo. Como ejemplo del uso del token de SAS, puede hacer lo siguiente:

  • Construya una dirección URL completa, incluido el nombre del contenedor, el nombre del blob y la cadena de consulta. La cadena de consulta es el token de SAS.
  • Cree un BlockBlobClient con la dirección URL del contenedor.
  • Use el cliente: en este ejemplo, cargue el blob con cargar.
// Client or another process uses SAS token to upload content to blob
async function uploadStringToBlob(blobName, sasToken, textAsString){

    // Get environment variables
    const accountName = process.env.AZURE_STORAGE_ACCOUNT_NAME;
    const containerName = process.env.AZURE_STORAGE_BLOB_CONTAINER_NAME;

    // Create Url SAS token as query string with typical `?` delimiter
    const sasUrl = `https://${accountName}.blob.core.windows.net/${containerName}/${blobName}?${sasToken}`;
    console.log(`\nBlobUrl = ${sasUrl}\n`);

    // Create blob client from SAS token url
    const blockBlobClient = new BlockBlobClient(sasUrl);

    // Upload string
    await blockBlobClient.upload(textAsString, textAsString.length, undefined);    
}

Vea también