Ottenere token di accesso protetti con Proof-of-Possession

Per aumentare la protezione dei token di accesso OAuth 2.0 archiviati nel browser contro il riutilizzo dei token, MSAL fornisce uno schema di autenticazione Access Token Proof-of-Posession. Access Token Proof-of-Possession AT PoPo è uno schema di autenticazione che associa in modo crittografico i token di accesso al browser e all'applicazione client da cui sono richiesti, ovvero non possono essere usati da un'applicazione o un dispositivo diverso.

È importante comprendere che, affinché AT PoP funzioni end-to-end e fornisca il miglioramento della sicurezza previsto, sia il servizio di autorizzazione che rilascia i token di accesso sia il server di risorse a cui tali token forniscono accesso devono supportare AT PoP.

Token di accesso Bearer rispetto a token di accesso vincolato (PoP)

Token di accesso Bearer

Il risultato dell'autenticazione standard restituito dalle API MSAL v2 include una accessToken proprietà . Se usato con lo schema di autenticazione predefinito Bearer , il valore nella accessToken proprietà è il segreto del token di accesso fornito dal server di autorizzazione. Questo artefatto viene memorizzato nella cache da MSAL e deve essere aggiunto alle richieste alla risorsa come token bearer nell'intestazione Authorization della richiesta.

Esempio di utilizzo del token di accesso Bearer:

// 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 di accesso vincolato

A.K.A PoP Token o Signed HTTP Request. Quando è abilitato lo schema di autorizzazione POP in una richiesta di token MSAL, il server di autorizzazione fornirà comunque un segreto del token di accesso JSON Web Token che ha l’aspetto di un token di accesso Bearer, che MSAL memorizzerà anch’esso nella cache. La differenza principale è che quando si usa lo POP schema, il segreto del token di accesso verrà associato al browser dell'utente tramite una coppia di chiavi crittografiche asimmetriche.

Il segreto del token di accesso viene incapsulato in un nuovo JSON Web Token, che verrà firmato usando l'algoritmo di hashing HMAC (codice di autenticazione dei messaggi basato su hash) e la chiave privata della coppia di chiavi che MSAL genera, archivia e gestisce. Il token JWT firmato viene quindi aggiunto all'oggetto AuthorizationResult sotto la accessToken proprietà e restituito dall'API MSAL v2 chiamata.

Dopo che l'applicazione client riceve il risultato dell'autenticazione restituita, può estrarre il accessToken valore dal risultato dell'autenticazione e aggiungerlo all'intestazione Authorization di una richiesta di risorsa protetta da PoP, usando l'etichetta PoP anziché l'etichetta Bearer .

Nota: il token JWT firmato (denominato richiesta HTTP firmata o SHR) non viene mai memorizzato nella cache da MSAL. Ogni volta che viene chiamata un'API MSAL v2, MSAL recupererà un segreto di token di accesso non elaborato valido dalla cache o richiederà un nuovo token di accesso dal server di autorizzazione. MSAL firmerà quindi il token di accesso e lo restituirà nel risultato dell'autenticazione.

Esempio di utilizzo di un token di accesso vincolato (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);

Esecuzione di una richiesta di token PoP

Dopo aver stabilito che il servizio di autorizzazione e il server di risorse supportano il binding dei token di accesso, è possibile configurare gli oggetti di richiesta di autenticazione e autorizzazione di MSAL per ottenere token di accesso associati creando un oggetto di richiesta del token contenente gli attributi specifici di PoP del token di accesso. Tutti gli attributi seguenti sono facoltativi nell'oggetto richiesta, tuttavia, authorizationScheme deve essere impostato manualmente su "pop" per abilitare la verifica del possesso.

Parametri della richiesta PoP AT

Name Description Obbligatorio
authenticationScheme Indica se MSAL deve acquisire un token Bearer o PoP. Il valore predefinito è Bearer. Obbligatorio
resourceRequestMethod Nome all-caps del metodo HTTP della richiesta che userà il token firmato (GET, POST, PUTe così via) Obbligatorio
resourceRequestUri URL della risorsa protetta per cui viene emesso il token di accesso Obbligatorio
shrClaims Oggetto JSON stringato contenente attestazioni client personalizzate da aggiungere all'oggetto SignedHTTPRequest. Per altre informazioni, vedere la documentazione sulle attestazioni SHR personalizzate . Facoltativo
shrNonce Un timestamp generato dal server e firmato, codificato Base64URL sotto forma di stringa. Questo nonce viene usato per attenuare l'asimmetria del clock e gli attacchi di spostamento temporale destinati a abilitare la pre-generazione del token PoP. Per altre informazioni, vedere la documentazione relativa al server SHR Nonce . Facoltativo

Nota: mentre questo documento illustra come aggiungere un shrNonce a SignedHttpRequest, lo schema di acquisizione del nonce del server non rientra nell'ambito di questo documento. Consultare la documentazione sui nonce del server SHR per ulteriori informazioni su come ottenere nonce generati dal server.

Esempio di richiesta di reindirizzamento per l'acquisizione del 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"
}

Dopo aver configurato la richiesta ed POP è impostata come authenticationScheme, può essere inviata all'API acquireTokenRedirect 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));
});

Esempio di richiesta non interattiva per l'acquisizione di token

L'acquisizione automatica dei token di accesso PoP richiede le stesse modifiche alla configurazione della richiesta di token come con le API interattive 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));
});

Gestione delle chiavi PoP

Lo schema di autenticazione Proof-of-Possession si basa su una coppia di chiavi crittografiche asimmetriche per associare il token di accesso al browser dell'utente. MSAL Browser genera questa coppia di chiavi quando inizialmente richiede un token di accesso dal servizio di autorizzazione e lo archivia usando IndexedDB. Questa coppia di chiavi crittografiche viene quindi utilizzata per firmare il SHR ogni volta che il token di accesso vincolato viene richiesto in modo non interattivo.

In caso di aggiornamento di un token di accesso associato, MSAL eliminerà la coppia di chiavi crittografiche generata quando si richiede il token di accesso associato scaduto, genererà una nuova coppia di chiavi crittografiche per il nuovo token di accesso e archivierà la nuova coppia di chiavi nell'archivio chiavi.

Funzionalità avanzata: Coppia di chiavi crittografiche gestite dall'applicazione

Avvertimento

Non è consigliabile usare questa funzionalità a meno che non si abbia familiarità con il protocollo Proof of Possession e che sia necessario generare una coppia di chiavi crittografiche personalizzata. Per la maggior parte dei casi, è consigliabile usare il poP come descritto nel resto di questo documento.

Se si sceglie di generare la propria coppia di chiavi crittografiche, questa funzionalità consente all'applicazione di fornire popKid come parametro di richiesta. MSAL JS garantisce che l'emittente del token includa cnf nel token, ma restituisce il token emesso non firmato. L'onere di firmare il token di accesso prima che venga inoltrato alla risorsa di destinazione spetterà all'applicazione.

Si noti anche che i parametri PoP rimanenti , ad eccezione di authenticationScheme , non sono impostati se si sceglie di sfruttare questo comportamento.

Perché i token di accesso vengono salvati in modo asincrono

La maggior parte delle credenziali MSAL e degli elementi della cache, ID Tokens ad esempio, può essere archiviata e rimossa in modo sincrono. Ciò è dovuto al fatto che questi elementi della cache vengono archiviati in localStorage o sessionStorage (che possono essere modificati in modo sincrono) e non hanno dipendenze da altri elementi archiviati con restrizioni di accesso asincrone.

A differenza di altri elementi della cache, Access Tokens vengono salvati nella cache in modo asincrono. Il motivo è che nel caso di un token di accesso associato a una coppia di chiavi crittografiche, archiviata in IndexedDB, la sostituzione del token di accesso comporta anche la sostituzione della coppia di chiavi crittografiche. Dato che la rimozione e la scrittura delle chiavi in IndexedDB sono operazioni asincrone, il processo di salvataggio di un token di accesso diventa inevitabilmente asincrono per estensione.

Esempi di codice