Poiché il rischio e i criteri vengono valutati in tempo reale, la durata dei token delle API delle risorse può aumentare fino a 28 ore. Questi token di lunga durata vengono aggiornati in modo proattivo da Microsoft Authentication Library (MSAL), aumentando la resilienza delle applicazioni.
Per usare CAE, sia l'app che l'API delle risorse a cui accede devono essere abilitate per CAE. Se un'API della risorsa implementa CAE e l'applicazione lo dichiara in grado di gestire l'ambiente di certificazione, l'app riceve i token CAE per tale risorsa. Per questo motivo, se dichiari l'app pronta per CAE, l'applicazione deve gestire la richiesta di attestazione CAE per tutte le API di risorsa che accettano token di accesso a Microsoft Identity.
Tuttavia, la preparazione del codice per supportare le risorse abilitate per CAE non limita la possibilità di usare le API che non supportano CAE. Se l'app non gestisce correttamente le risposte CAE, potrebbe ripetere ripetutamente una chiamata API usando un token tecnicamente valido ma revocato a causa di CAE.
Per iniziare, aggiungere codice per gestire le risposte dall'API della risorsa che rifiuta la chiamata a causa di CAE. Con CAE, le API restituiscono uno stato 401 e un'intestazione WWW-Authenticate
quando il token di accesso viene revocato o l'API rileva una modifica nell'indirizzo IP usato. L'intestazione WWW-Authenticate
contiene una richiesta di attestazioni che l'applicazione può usare per acquisire un nuovo token di accesso.
Quando queste condizioni vengono soddisfatte, l'app può estrarre e decodificare la richiesta di attestazioni usando la classe MSAL.NETWwwAuthenticateParameters
.
if (APIresponse.IsSuccessStatusCode)
{
// ...
}
else
{
if (APIresponse.StatusCode == System.Net.HttpStatusCode.Unauthorized
&& APIresponse.Headers.WwwAuthenticate.Any())
{
string claimChallenge = WwwAuthenticateParameters.GetClaimChallengeFromResponseHeaders(APIresponse.Headers);
L'app usa quindi la richiesta di attestazioni per acquisire un nuovo token di accesso per la risorsa.
try
{
authResult = await _clientApp.AcquireTokenSilent(scopes, firstAccount)
.WithClaims(claimChallenge)
.ExecuteAsync()
.ConfigureAwait(false);
}
catch (MsalUiRequiredException)
{
try
{
authResult = await _clientApp.AcquireTokenInteractive(scopes)
.WithClaims(claimChallenge)
.WithAccount(firstAccount)
.ExecuteAsync()
.ConfigureAwait(false);
}
// ...
Quando l'applicazione è pronta per gestire la richiesta di attestazione restituita da una risorsa abilitata per CAE, è possibile indicare a Microsoft Identity che l'app è pronta per l'autenticazione condizionale. Per eseguire questa operazione nell'applicazione MSAL, compilare il client pubblico usando le funzionalità client di "cp1"
.
_clientApp = PublicClientApplicationBuilder.Create(App.ClientId)
.WithDefaultRedirectUri()
.WithAuthority(authority)
.WithClientCapabilities(new [] {"cp1"})
.Build();
Quando vengono soddisfatte queste condizioni, l'app può estrarre la richiesta di attestazioni dall'intestazione della risposta API come indicato di seguito:
try {
const response = await fetch(apiEndpoint, options);
if (response.status === 401 && response.headers.get('www-authenticate')) {
const authenticateHeader = response.headers.get('www-authenticate');
const claimsChallenge = parseChallenges(authenticateHeader).claims;
// use the claims challenge to acquire a new access token...
}
} catch(error) {
// ...
}
// helper function to parse the www-authenticate header
function parseChallenges(header) {
const schemeSeparator = header.indexOf(' ');
const challenges = header.substring(schemeSeparator + 1).split(',');
const challengeMap = {};
challenges.forEach((challenge) => {
const [key, value] = challenge.split('=');
challengeMap[key.trim()] = window.decodeURI(value.replace(/['"]+/g, ''));
});
return challengeMap;
}
L'app usa quindi la richiesta di attestazioni per acquisire un nuovo token di accesso per la risorsa.
const tokenRequest = {
claims: window.atob(claimsChallenge), // decode the base64 string
scopes: ['User.Read'],
account: msalInstance.getActiveAccount()
};
let tokenResponse;
try {
tokenResponse = await msalInstance.acquireTokenSilent(tokenRequest);
} catch (error) {
if (error instanceof InteractionRequiredAuthError) {
tokenResponse = await msalInstance.acquireTokenPopup(tokenRequest);
}
}
Una volta che l'applicazione è pronta per gestire la richiesta di attestazione restituita da una risorsa abilitata per CAE, è possibile indicare a Microsoft Identity che l'app è pronta per l'aggiunta di una clientCapabilities
proprietà nella configurazione MSAL.
const msalConfig = {
auth: {
clientId: 'Enter_the_Application_Id_Here',
clientCapabilities: ["CP1"]
// remaining settings...
}
}
const msalInstance = new PublicClientApplication(msalConfig);
Quando vengono soddisfatte queste condizioni, l'app può estrarre la richiesta di attestazioni dall'intestazione della risposta API come indicato di seguito:
import msal # pip install msal
import requests # pip install requests
import www_authenticate # pip install www-authenticate==0.9.2
# Once your application is ready to handle the claim challenge returned by a CAE-enabled resource, you can tell Microsoft Identity your app is CAE-ready. To do this in your MSAL application, build your Public Client using the Client Capabilities of "cp1".
app = msal.PublicClientApplication("your_client_id", client_capabilities=["cp1"])
...
# When these conditions are met, the app can extract the claims challenge from the API response header as follows:
response = requests.get("<your_resource_uri_here>")
if response.status_code == 401 and response.headers.get('WWW-Authenticate'):
parsed = www_authenticate.parse(response.headers['WWW-Authenticate'])
claims = parsed.get("bearer", {}).get("claims")
# Your app would then use the claims challenge to acquire a new access token for the resource.
if claims:
auth_result = app.acquire_token_interactive(["scope"], claims_challenge=claims)
Dichiarare il supporto per la funzionalità client CP1
Nella configurazione dell'applicazione è necessario dichiarare che l'applicazione supporta CAE includendo la CP1
funzionalità client. Questo valore viene specificato usando la client_capabilities
proprietà JSON.
{
"client_id" : "<your_client_id>",
"authorization_user_agent" : "DEFAULT",
"redirect_uri" : "msauth://<pkg>/<cert_hash>",
"multiple_clouds_supported":true,
"broker_redirect_uri_registered": true,
"account_mode": "MULTIPLE",
"client_capabilities": "CP1",
"authorities" : [
{
"type": "AAD",
"audience": {
"type": "AzureADandPersonalMicrosoftAccount"
}
}
]
}
Rispondere alle sfide CAE in fase di esecuzione
Effettuare una richiesta a una risorsa, se la risposta contiene una richiesta di attestazioni, estrarla e rimetterla in MSAL per l'uso nella richiesta successiva.
final HttpURLConnection connection = ...;
final int responseCode = connection.getResponseCode();
// Check the response code...
if (200 == responseCode) {
// ...
} else if (401 == responseCode) {
final String authHeader = connection.getHeaderField("WWW-Authenticate");
if (null != authHeader) {
final ClaimsRequest claimsRequest = WWWAuthenticateHeader
.getClaimsRequestFromWWWAuthenticateHeaderValue(authHeader);
// Feed the challenge back into MSAL, first silently, then interactively if required
final AcquireTokenSilentParameters silentParameters = new AcquireTokenSilentParameters.Builder()
.fromAuthority(authority)
.forAccount(account)
.withScopes(scope)
.withClaims(claimsRequest)
.build();
try {
final IAuthenticationResult silentRequestResult = mPublicClientApplication.acquireTokenSilent(silentParameters);
// If successful - your business logic goes here...
} catch (final Exception e) {
if (e instanceof MsalUiRequiredException) {
// Retry the request interactively, passing in any claims challenge...
}
}
}
} else {
// ...
}
// Don't forget to close your connection
I frammenti di codice seguenti descrivono il flusso di acquisizione invisibile all'utente di un token, l'esecuzione di una chiamata HTTP al provider di risorse e la gestione di un caso CAE. Potrebbe essere necessaria una chiamata di interazione aggiuntiva se la chiamata invisibile all'utente non è riuscita con attestazioni.
Dichiarare il supporto per la funzionalità client CP1
Nella configurazione dell'applicazione è necessario dichiarare che l'applicazione supporta CAE includendo la CP1
funzionalità client. Questa proprietà viene specificata utilizzando la clientCapabilities
proprietà .
let clientConfigurations = MSALPublicClientApplicationConfig(clientId: "contoso-app-ABCDE-12345",
redirectUri: "msauth.com.contoso.appbundle://auth",
authority: try MSALAuthority(url: URL(string: "https://login.microsoftonline.com/organizations")!))
clientConfigurations.clientApplicationCapabilities = ["CP1"]
let applicationContext = try MSALPublicClientApplication(configuration: clientConfigurations)
Implementare una funzione helper per l'analisi delle richieste di attestazioni.
func parsewwwAuthenticateHeader(headers:Dictionary<AnyHashable, Any>) -> String? {
// !! This is a sample code and is not validated, please provide your own implementation or fully test the sample code provided here.
// Can also refer here for our internal implementation: https://github.com/AzureAD/microsoft-authentication-library-common-for-objc/blob/dev/IdentityCore/src/webview/embeddedWebview/challangeHandlers/MSIDPKeyAuthHandler.m#L112
guard let wwwAuthenticateHeader = headers["WWW-Authenticate"] as? String else {
// did not find the header, handle gracefully
return nil
}
var parameters = [String: String]()
// regex mapping
let regex = try! NSRegularExpression(pattern: #"(\w+)="([^"]*)""#)
let matches = regex.matches(in: wwwAuthenticateHeader, range: NSRange(wwwAuthenticateHeader.startIndex..., in: wwwAuthenticateHeader))
for match in matches {
if let keyRange = Range(match.range(at: 1), in: wwwAuthenticateHeader),
let valueRange = Range(match.range(at: 2), in: wwwAuthenticateHeader) {
let key = String(wwwAuthenticateHeader[keyRange])
let value = String(wwwAuthenticateHeader[valueRange])
parameters[key] = value
}
}
guard let jsonData = try? JSONSerialization.data(withJSONObject: parameters, options: .prettyPrinted) else {
// cannot convert params into json date, end gracefully
return nil
}
return String(data: jsonData, encoding: .utf8)
}
Catch & parse 401 / richieste di attestazioni.
let response = .... // HTTPURLResponse object from 401'd service response
switch response.statusCode {
case 200:
// ...succeeded!
break
case 401:
let headers = response.allHeaderFields
// Parse header fields
guard let wwwAuthenticateHeaderString = self.parsewwwAuthenticateHeader(headers: headers) else {
// 3.7 no valid wwwAuthenticateHeaderString is returned from header, end gracefully
return
}
let claimsRequest = MSALClaimsRequest(jsonString: wwwAuthenticateHeaderString, error: nil)
// Create claims request
let parameters = MSALSilentTokenParameters(scopes: "Enter_the_Protected_API_Scopes_Here", account: account)
parameters.claimsRequest = claimsRequest
// Acquire token silently again with the claims challenge
applicationContext.acquireTokenSilent(with: parameters) { (result, error) in
if let error = error {
// error happened end flow gracefully, and handle error. (e.g. interaction required)
return
}
guard let result = result else {
// no result end flow gracefully
return
}
// Success - You got a token!
}
break
default:
break
}
Quando vengono soddisfatte queste condizioni, l'app può estrarre la richiesta di attestazioni dall'intestazione della risposta API come indicato di seguito:
Annunciare le funzionalità client.
client, err := New("client-id", WithAuthority(authority), WithClientCapabilities([]string{"cp1"}))
analizzare l'intestazione WWW-Authenticate
e passare la richiesta di verifica risultante in MSAL-Go.
// No snippet provided at this time
Tentare di acquisire un token in modo invisibile all'utente con la richiesta di verifica delle attestazioni.
var ar AuthResult;
ar, err := client.AcquireTokenSilent(ctx, tokenScope, public.WithClaims(claims))
È possibile testare l'applicazione accedendo a un utente e quindi usando il portale di Azure per revocare la sessione dell'utente. La volta successiva che l'app chiama l'API abilitata per CAE, all'utente verrà chiesto di ripetere l'autenticazione.