Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Sebagai penyempurnaan dari Access Token Proof-of-Possession, MSAL Browser menyediakan cara untuk menyisipkan stempel waktu bertanda tangan yang dihasilkan server (alias server nonce) ke dalam Signed HTTP Request, yang juga dikenal sebagai PoP Token. Nonce yang dihasilkan server ini dapat ditambahkan ke permintaan token apa pun yang menggunakan skema autentikasi POP.
Mengingat bahwa MSAL tidak menyimpan cacheSigned HTTP Requests, nonce server juga tidak akan di-cache. Ini berarti bahwa nonce server harus diteruskan ke setiap panggilan acquireTokenSilent agar ditambahkan ke objek SignedHttpRequest yang dihasilkan.
Signed HTTP Request Setelah dikirim ke server sumber daya sebagai PoP Token, server sumber daya bertanggung jawab untuk memvalidasi payload yang ditandatangani serta mengekstrak dan memvalidasi shrNonce.
Penggunaan
Setelah diperoleh, nonce yang dihasilkan server untuk SHR dapat diteruskan ke objek permintaan token apa pun sebagai shrNonce. Penting untuk dicatat bahwa nonce server SHR adalah ekstensi dari skema Bukti Kepemilikan Token Akses, sehingga hanya akan digunakan ketika permintaan authenticationScheme token diatur ke POP.
Contoh Permintaan untuk Mendapatkan Token
const popTokenRequest = {
scopes: ["User.Read"],
authenticationScheme: msal.AuthenticationScheme.POP,
resourceRequestMethod: "POST",
resourceRequestUri: "YOUR_RESOURCE_ENDPOINT",
shrNonce: "eyJhbGciOiJIUzI1NiIsImtpZCI6IktJRCIsInR5cCI6IkpXVCJ9.eyJ0cyI6IjE2MjU2NzI1MjkifQ.rA5ho63Lbdwo8eqZ_gUtQxY3HaseL0InIVwdgf7L_fc" // Sample Base64URL encoded server nonce value
}
Setelah permintaan dikonfigurasi dan POP ditetapkan sebagai authenticationScheme, permintaan dapat diteruskan ke api loginXXXX atau acquireTokenXXX:
const response = await myMSALObj.acquireTokenSilent(popTokenRequest);
Respons akan berisi properti yang disebut accessToken, yang akan berisi Signed HTTP Request. Saat diverifikasi menggunakan kunci publik, payload JWT SHR akan terlihat seperti berikut:
{
at: ...,
ts: ...,
m: "POST",
u: "YOUR_RESOURCE_ENDPOINT",
nonce: "eyJhbGciOiJIUzI1NiIsImtpZCI6IktJRCIsInR5cCI6IkpXVCJ9.eyJ0cyI6IjE2MjU2NzI1MjkifQ.rA5ho63Lbdwo8eqZ_gUtQxY3HaseL0InIVwdgf7L_fc",
p: ...,
q: ...,
client_claims: "{\"nonce\": \"AQAA123456\",\"local_nonce\": \"AQAA7890\"}"
}
Atribut Nonce DitandatanganiHttpRequest
Nilai shrNonce yang dapat dikonfigurasi dalam permintaan token PoP akan ditetapkan ke atribut nonce dalam SignedHttpRequest yang dikembalikan dalam AuthenticationResult. Namun, atribut nonce di SHR tidak akan kosong jika tidak dikonfigurasi secara manual melalui nilai shrNonce. Daftar berikut menjelaskan logika (berdasarkan urutan prioritas) yang digunakan untuk menetapkan nilai nonce pada SignedHttpRequest:
- Jika
shrNoncedalam permintaan autentikasi bukannullatauundefined, MSAL menetapkan nilai tersebut kenonceproperti di SHR - Jika
shrNonceadalahnullatauundefined, MSAL menghasilkan string GUID acak dan menetapkannya kenonceproperti di SHR
Mendapatkan nilai nonce server
Metode di mana aplikasi klien awalnya memperoleh dan kemudian memperbarui nonce yang dihasilkan server mungkin berbeda tergantung pada server sumber daya mana yang berinteraksi dengan aplikasi klien. Berikut adalah gambaran umum alur perolehan dan pembaruan nonce server yang dioptimalkan untuk MSAL Browser. Perlu diingat bahwa bahkan jika alur akuisisi nonce berbeda, nonce server masih selalu ditambahkan secara manual ke dalam permintaan token oleh aplikasi klien seperti yang dijelaskan di bagian Penggunaan di bawah ini.
Memperoleh nonce awal
- Langkah pertama untuk memperoleh nonce yang dihasilkan oleh server adalah membuat permintaan yang diautorisasi ke sumber daya. Dalam permintaan yang diotorisasi ini,
PoP Tokenakan ditambahkan ke headerAuthorization, tetapiPoP Tokentersebut tidak akan menyertakan nonce yang valid.
let shrNonce = null; // Globally scoped variable
// 1. Configure PoP Token Request without a valid SHR Nonce
const popTokenRequest = {
scopes: ["User.Read"],
authenticationScheme: msal.AuthenticationScheme.POP,
resourceRequestMethod: "POST",
resourceRequestUri: "YOUR_RESOURCE_ENDPOINT"
shrNonce: shrNonce // SHR Nonce is invalid as null string at this point
};
// Get PoP token to make authenticated request
const shr = await publicClientApplication.acquireTokenSilent(popTokenRequest);
// Set up PoP Resource request
const reqHeaders = new Headers();
const authorizationHeader = `PoP ${shr}`;
headers.append("Authorization", authorizationHeader);
const options = {
method: method,
headers: headers
};
- Setelah permintaan disiapkan dan
SHRtelah ditambahkan ke header otorisasi, permintaanPOSTdikirim ke sumber daya. Pada tahap ini, tanpa nonce server yang valid dalamPoP Token/SHR, resource harus merespons dengan kesalahan HTTP401 Unauthorized, yang akan memiliki headerWWW-Authenticateyang berisi nonce server valid pertama sebagai salah satu challenge-nya.
// Make call to resource with SHR
return fetch(resourceEndpointData.endpoint, options)
.then(response => response.json())
.then(response => {
// At this point, the response will be a 401 Error, so ignore the success case for now
})
.catch(error => {
// This error will be a `401 Unauthorized` error, containing a WWW-Authenticate header with an error message such as "nonce_missing" or "nonce_malformed"
// The correct way to handle this scenario is shown in the following step.
});
- Untuk mengekstrak server nonce dari header
WWW-Authenticatetersebut, MSAL menyediakan kelasAuthenticationHeaderParser, yang mencakup APIgetShrNonceyang akan menguraikan server nonce dari header autentikasi yang memuatnya:
import { PublicClientApplication, AuthenticationHeaderParser } from "@azure/msal-browser";
...
// Make call to resource with SHR
return fetch(resourceEndpointData.endpoint, options)
.then(response => response.json())
.then(response => {
if (response.status === 200 && response.headers.get("Authentication-Info")) {
// At this point, the response will be a 401 Error, so ignore the success case for now
}
// Check if error is 401 unauthorized and WWW-Authenticate header is included
else if (response.status === 401 && response.headers.get("WWW-Authenticate")) {
lastResponseHeaders = response.headers;
const authHeaderParser = new AuthenticationHeaderParser(response.headers);
shrNonce = authHeaderParser.getShrNonce(); // Null is replaced with valid nonce from WWW-Authenticate header
} else {
// Deal with other errors as necessary
}
});
});
Menggunakan dan memperbarui nonce yang valid
- Sekarang, setelah
shrNoncediperoleh untuk pertama kalinya,PoP Tokendapat diminta kembali dengan menyertakan nonce yang valid, dan permintaan sumber daya yang diotorisasi dapat berhasil diselesaikan. Respons200 OKyang berhasil kini akan menyertakanAuthentication-Infoheader yang berisinextnoncechallenge, yang dapat diuraikan oleh MSAL dengan cara yang sama sepertiWWW-Authenticatenonce:
import { PublicClientApplication, AuthenticationHeaderParser } from "@azure/msal-browser";
// 1. Configure PoP Token Request without a valid SHR Nonce
const popTokenRequest = {
scopes: ["User.Read"],
authenticationScheme: msal.AuthenticationScheme.POP,
resourceRequestMethod: "POST",
resourceRequestUri: "YOUR_RESOURCE_ENDPOINT"
shrNonce: shrNonce // SHR Nonce is now a valid server-generated nonce
};
// Get PoP token to make authenticated request
const shr = await publicClientApplication.acquireTokenSilent(popTokenRequest);
// Set up PoP Resource request
const reqHeaders = new Headers();
const authorizationHeader = `PoP ${shr}`;
headers.append("Authorization", authorizationHeader);
const options = {
method: method,
headers: headers
};
// Make call to resource with SHR
return fetch(resourceEndpointData.endpoint, options)
.then(response => response.json())
.then(response => {
if (response.status === 200 && response.headers.get("Authentication-Info")) {
/** NEW **/
// 200 OK if nonce was valid
lastResponseHeaders = response.headers;
const authHeaderParser = new AuthenticationHeaderParser(response.headers);
shrNonce = authHeaderParser.getShrNonce(); // Previous nonce (possibly expired) is replaced with the nextnonce generated by the server
}
// Check if error is 401 unauthorized and WWW-Authenticate header is included
else if (response.status === 401 && response.headers.get("WWW-Authenticate")) {
/** SAME AS BEFORE **/
lastResponseHeaders = response.headers;
const authHeaderParser = new AuthenticationHeaderParser(response.headers);
shrNonce = authHeaderParser.getShrNonce(); // Null is replaced with valid nonce from WWW-Authenticate header
} else {
// Deal with other errors as necessary
}
});
});
Siklus Akuisisi Nonce Terintegrasi
Skrip berikut mengusulkan cara yang direkomendasikan untuk menangani PoP Token Permintaan yang mengharuskan server nonce diperoleh dan diperbarui terus menerus:
/**
* Application script
*/
import { PublicClientApplication, AuthenticationHeaderParser } from "@azure/msal-browser";
const publicClientApplication = new PublicClientApplication(msalConfig);
// Initialize header map to keep track of the "last" response's headers.
let lastResponseHeaders: HttpHeaders = null;
// Call the PoP API endpoint
const { responseBody, lastResponseHeaders } = await callPopResource(publicClientApplication, resourceEndpointData, lastResponseHeaders);
/**
* End Application script
*/
/**
* Source Code:
* This method is responsible for getting data from a PoP-protected API. It is called at the bottom of the
* demo code in the application script.
*/
const async callPopResource(
publicClientApplication: PublicClientApplication,
resourceEndpointData: ResourceEndpointData,
lastResponseHeaders: HttpHeaders): ResponseBody {
// Get headers from last response's headers
const headerParser = new AuthenticationHeaderParser(lastResponseHeaders);
let shrNonce: string | null;
try {
shrNonce = headerParser.getShrNonce(); // Will return
} catch (e) {
// If the lastResponse headers are null, .getShrNonce will throw (by design)
shrNonce = null;
}
// Build PoP request as usual, adding the server nonce
const popTokenRequest = {
account: CURRENT_ACCOUNT,
scopes: resourceEndpointData.POP_RESOURCE_SCOPES,
authenticationScheme: AuthenticationScheme.POP,
resourceRequestUri: resourceEndpointData.RESOURCE_URI,
resourceRequestMethod: resourceEndpointData.METHOD,
shrClaims: resourceEndpointData.CUSTOM_CLAIMS_STRING,
shrNonce: shrNonce || undefined // Will be undefined on the first call, shrNonce should be valid on subsequent calls
}
// Get pop token to make authenticated request
const shr = await publicClientApplication.acquireTokenSilent(popTokenRequest);
// PoP Resource request
const reqHeaders = new Headers();
const authorizationHeader = `PoP ${shr}`; //Create Authorization header
headers.append("Authorization", authorizationHeader); // Add Authorization header to request headers
const options = {
method: method,
headers: headers
};
// Make call to resource with SHR
return fetch(resourceEndpointData.endpoint, options)
.then(response => response.json())
.then(response => {
if (response.status === 200 && response.headers.get("Authentication-Info")) {
lastResponseHeaders = response.headers;
const authHeaderParser = new AuthenticationHeaderParser(response.headers);
shrNonce = authHeaderParser.getShrNonce(); // Previous nonce (possibly expired) is replaced with the nextnonce generated by the server
}
// Check if error is 401 unauthorized and WWW-Authenticate header is included
else if (response.status === 401 && response.headers.get("WWW-Authenticate")) {
/** SAME AS BEFORE **/
lastResponseHeaders = response.headers;
const authHeaderParser = new AuthenticationHeaderParser(response.headers);
shrNonce = authHeaderParser.getShrNonce(); // Null is replaced with valid nonce from WWW-Authenticate header
} else {
// Deal with other errors as necessary
}
});
});
}