Adquisición de tokens de acceso protegidos con prueba de posesión

Para aumentar la protección de los tokens de acceso de OAuth 2.0 almacenados en el navegador frente a ataques de reutilización de tokens, MSAL proporciona un esquema de autenticación Access Token Proof-of-Posession. Access Token Proof-of-Possession, o AT PoP, es un esquema de autenticación que enlaza criptográficamente los tokens de acceso a la aplicación cliente y del explorador desde la que se solicitan, lo que significa que no se pueden usar desde otra aplicación o dispositivo.

Es importante comprender que para AT PoP que funcione de un extremo a otro y proporcionar la actualización de seguridad prevista, tanto el servicio de autorización que emite los tokens de acceso como el servidor de recursos al que proporcionan acceso deben admitir AT PoP.

Token de acceso de portador (Bearer) vs. token de acceso vinculado (PoP).

Token de acceso del portador

El resultado de autenticación estándar devuelto por las API de MSAL v2 incluye una accessToken propiedad . Cuando se usa con el esquema de autenticación predeterminado Bearer , el valor de la accessToken propiedad es el secreto del token de acceso proporcionado por el servidor de autorización. MSAL almacena en caché este artefacto y debe añadirse a las solicitudes de recursos como un token de tipo Bearer en el encabezado Authorization de la solicitud.

Ejemplo de uso de token de acceso de portador:

// Using the Bearer scheme (default), acquireTokenRedirect returns an AuthenticationResult object containing the Bearer access token secret
const { accessToken } = await myMSALObj.acquireTokenRedirect(popTokenRequest);

// The bearer token secret is appended to the Authorization header
const headers = new Headers();
const authHeader = `Bearer ${accessToken}`; // The Bearer label is used in this header
headers.append("Authorization", authHeader);

Token de acceso vinculado

A.K.A PoP Token o Signed HTTP Request. Cuando se habilita el esquema de autorización POP en una solicitud de token de MSAL, el servidor de autorización seguirá proporcionando un secreto de token de acceso JSON Web Token que parece un token de acceso Bearer, que MSAL también almacenará en caché. La principal diferencia es que, al usar el esquema POP, ese secreto del token de acceso quedará vinculado al navegador del usuario mediante un par de claves criptográficas asimétricas.

El secreto del token de acceso se encapsula en un nuevo token web JSON, que se firmará mediante el algoritmo HMAC de código de autenticación de mensajes basado en hash y la clave privada del par de claves que MSAL genera, almacena y gestiona. A continuación, el JWT firmado se agrega al objeto AuthorizationResult bajo la propiedad accessToken y se devuelve desde la API MSAL v2 llamada.

Una vez que la aplicación cliente recibe el resultado de autenticación devuelto, puede extraer el accessToken valor del resultado de autenticación y agregarlo al Authorization encabezado de una solicitud de recurso protegido por poP, mediante la PoP etiqueta en lugar de la Bearer etiqueta.

Nota: MSAL nunca almacena en caché el JWT firmado (denominado solicitud HTTP firmada o SHR). Cada vez que se llama a una API de MSAL v2, MSAL recuperará un secreto de token de acceso sin procesar válido de la memoria caché o solicitará un nuevo token de acceso desde el servidor de autorización. A continuación, MSAL firmará dicho token de acceso y lo devolverá en el resultado de la autenticación.

Ejemplo de uso de un token de acceso enlazado (PoP):

// Using the POP scheme (default), acquireTokenRedirect returns an AuthenticationResult object containing the Signed HTTP Request (PoP Token)
const { accessToken } = await myMSALObj.acquireTokenRedirect(popTokenRequest);

// The SHR is appended to the Authorization header
const headers = new Headers();
const authHeader = `PoP ${accessToken}`; // The PoP label is used in this header
headers.append("Authorization", authHeader);

Realizar una solicitud de token PoP

Una vez que haya comprobado que el servicio de autorización y el servidor de recursos admiten la vinculación del token de acceso, puede configurar los objetos de solicitud de autenticación y de autorización de MSAL para obtener tokens de acceso vinculados mediante la creación de un objeto de solicitud de token que contenga los atributos específicos de PoP para el token de acceso. Sin embargo, todos los atributos siguientes son opcionales en el objeto de solicitud; sin embargo, authorizationScheme debe establecerse manualmente en "pop" para habilitar la prueba de posesión.

Parámetros de la solicitud PoP de AT

Nombre Description Obligatorio
authenticationScheme Indica si MSAL debe adquirir un token de Bearer o un token de PoP. El valor predeterminado es Bearer. Obligatorio
resourceRequestMethod El nombre all-caps del método HTTP de la solicitud que usará el token firmado (GET, POST, PUT, etc.) Obligatorio
resourceRequestUri Dirección URL del recurso protegido para el que se emite el token de acceso. Obligatorio
shrClaims Un objeto JSON serializado como cadena que contiene declaraciones personalizadas del cliente que se añadirán a SignedHTTPRequest. Consulte la documentación de reclamaciones SHR personalizadas para obtener más información. Opcional
shrNonce Marca de tiempo firmada generada por el servidor que está codificada en Base64URL como una cadena. Este nonce se utiliza para mitigar los desajustes del reloj y los ataques de viaje en el tiempo destinados a permitir la generación anticipada del token PoP. Consulte la documentación de SHR Server Nonce para obtener más información. Opcional

Nota: Aunque este documento muestra cómo añadir un elemento shrNonce al SignedHttpRequest, el patrón de adquisición del nonce del servidor queda fuera del alcance. Revise la documentación de SHR Server Nonce para obtener más información sobre cómo obtener nonces generados por el servidor.

Ejemplo de solicitud de redirección para adquirir un token

const popTokenRequest = {
    scopes: ["User.Read"],
    authenticationScheme: msal.AuthenticationScheme.POP,
    resourceRequestMethod: "POST",
    resourceRequestUri: "YOUR_RESOURCE_ENDPOINT",
    shrClaims: "{\"shrClaim1\": \"claimValue\"}",
    shrNonce: "NONCE_ACQUIRED_FROM_RESOURCE_SERVER"
}

Una vez configurada la solicitud y POP establecida como authenticationScheme, se puede enviar a la acquireTokenRedirect API de MSAL v2.

const response = await myMSALObj.acquireTokenRedirect(popTokenRequest);

// Once a Pop Token has been acquired, it can be added on the authorization header of a resource request
const headers = new Headers();
const authHeader = `${response.tokenType} ${response.accessToken}`;

headers.append("Authorization", authHeader);

const options = {
    method: popTokenRequest.resourceRequestMethod,
    headers: headers
};

// After the request has been built and the POP access token has bee appended, the request can be executed using an API like "fetch"
fetch(endpoint, options)
    .then(response => response.json())
    .then(response => callback(response, endpoint))
    .catch(error => console.log(error));
});

Ejemplo de solicitud silenciosa de adquisición de tokens

La adquisición silenciosa de tokens de acceso PoP requiere los mismos cambios en la configuración de la solicitud de tokens que en las API interactivas acquireToken:

const silentPopTokenRequest = {
    scopes: ["User.Read"],
    authenticationScheme: msal.AuthenticationScheme.POP, // Default is "BEARER"
    resourceRequestMethod: "POST",
    resourceRequestUri: "YOUR_RESOURCE_ENDPOINT",
    shrClaims: "{\"shrClaim1\": \"claimValue\"}",
    shrNonce: "NONCE_ACQUIRED_FROM_RESOURCE_SERVER"
}

// Try to acquire token silently
const { accessToken } = await myMSALObj.acquireTokenSilent(silentPopTokenRequest).catch(async (error) => {
        console.log("Silent token acquisition failed.");
        if (error instanceof msal.InteractionRequiredAuthError) {
            // Fallback to interaction if silent call fails
            console.log("Acquiring token using redirect");
            myMSALObj.acquireTokenRedirect(silentPopTokenRequest);
        } else {
            console.error(error);
        }
    });

// Once a Pop Token has been acquired, it can be added on the authorization header of a resource request
const headers = new Headers();
const authHeader = `PoP ${accessToken}`;

headers.append("Authorization", authHeader);

const options = {
    method: popTokenRequest.resourceRequestMethod,
    headers: headers
};

// After the request has been built and the POP access token has bee appended, the request can be executed using an API like "fetch"
fetch(endpoint, options)
    .then(response => response.json())
    .then(response => callback(response, endpoint))
    .catch(error => console.log(error));
});

Gestión de claves PoP

El esquema de autenticación de prueba de posesión se basa en una clave criptográfica asimétrica para enlazar el token de acceso al explorador del usuario. MSAL Browser genera este par de claves al solicitar inicialmente un token de acceso desde el servicio de autorización y lo almacena mediante IndexedDB. Este par de claves criptográficas se utiliza entonces para firmar el SHR cada vez que se solicita de manera silenciosa el token de acceso enlazado.

En caso de actualizar un token de acceso enlazado, MSAL eliminará el par de claves criptográficas que se generó al solicitar el token de acceso enlazado expirado, generará un nuevo par de claves criptográficas para el nuevo token de acceso y almacenará el nuevo par de claves en el almacén de claves.

Característica avanzada: par de claves criptográficas gestionado por la aplicación

Warning

No se recomienda usar esta característica a menos que esté familiarizado con el protocolo Prueba de posesión y tenga un requisito específico para generar su propia clave criptográfica. En la mayoría de los casos, se recomienda el uso de PoP, tal como se describe en el resto de este documento.

Si decide generar su propio par de claves criptográficas, esta función permite que la aplicación proporcione el popKid como parámetro de solicitud. MSAL JS garantiza que el emisor del token inserte el cnf en el token, pero devuelve el token emitido sin firmar. La responsabilidad de firmar el token de acceso antes de que se reenvíe al recurso previsto estará en la aplicación.

Tenga en cuenta también que debe asegurarse de que los parámetros PoP restantes, excepto el authenticationScheme, no estén configurados si decide aprovechar este comportamiento.

¿Por qué los tokens de acceso se guardan de forma asincrónica?

La mayoría de las credenciales de MSAL y los elementos de caché, como ID Tokens por ejemplo, se pueden almacenar y quitar sincrónicamente. Esto se debe a que estos elementos de caché se almacenan en localStorage o sessionStorage (que se pueden manipular de forma sincrónica) y no tienen dependencias en otros elementos almacenados que tienen restricciones de acceso asincrónicas.

A diferencia de otros elementos de caché, Access Tokens se guardan en la memoria caché de forma asincrónica. La razón de esto es que, en el caso de que un token de acceso esté enlazado a un keypair criptográfico, que se almacena en IndexedDB, reemplazar el token de acceso también implica reemplazar el keypair criptográfico. Dado que quitar y escribir claves en IndexedDB son operaciones asincrónicas, el proceso para guardar un token de acceso inevitablemente se vuelve asincrónico por extensión.

Ejemplos de código