Comme le risque et la stratégie sont évalués en temps réel, la durée de validité des jetons de certaines API de ressources peut augmenter jusqu’à 28 heures. Ces jetons de longue durée sont actualisés préalablement par la bibliothèque d’authentification Microsoft (MSAL), ce qui augmente la résilience de vos applications.
Pour utiliser l’évaluation CAE, votre application et l’API de ressource à laquelle elle accède doivent être compatibles CAE. Si une API de ressource implémente l’évaluation CAE et que votre application déclare qu’elle peut gérer la fonctionnalité CAE, votre application obtient des jetons CAE pour cette ressource. Pour cette raison, si vous déclarez que votre application est prête à utiliser l’évaluation continue de l’accès, votre application doit traiter le défi de revendications CAE pour toutes les API de ressources qui acceptent les jetons d’accès Microsoft Identity.
Toutefois, la préparation de votre code pour la prise en charge des ressources compatibles CAE ne limite pas sa capacité à travailler avec des API qui ne prennent pas en charge CAE. Si votre application ne gère pas correctement les réponses CAE, elle peut réessayer à plusieurs reprises un appel d’API à l’aide d’un jeton techniquement valide, mais révoqué en raison de la CAE.
Commencez par ajouter du code pour gérer les réponses de l’API de ressource qui rejette l’appel en raison de la CAE. Avec CAE, les API retournent un état 401 et un en-tête WWW-Authenticate
lorsque le jeton d’accès est révoqué ou que l’API détecte une modification de l’adresse IP utilisée. L’en-tête WWW-Authenticate
contient un défi de revendications que l’application peut utiliser pour obtenir un nouveau jeton d’accès.
Lorsque ces conditions sont réunies, l’application peut extraire et décoder le défi de revendications à l’aide de la classe MSAL.NET WwwAuthenticateParameters
.
if (APIresponse.IsSuccessStatusCode)
{
// ...
}
else
{
if (APIresponse.StatusCode == System.Net.HttpStatusCode.Unauthorized
&& APIresponse.Headers.WwwAuthenticate.Any())
{
string claimChallenge = WwwAuthenticateParameters.GetClaimChallengeFromResponseHeaders(APIresponse.Headers);
Votre application utilise ensuite le défi de revendications pour obtenir un nouveau jeton d’accès pour la ressource.
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);
}
// ...
Une fois que votre application est prête à traiter le défi de revendications retourné par une ressource activée avec CAE, vous pouvez indiquer à Microsoft Identity que votre application est prête à utiliser CAE. Pour effectuer cette opération dans votre application MSAL, générez votre client public à l’aide des fonctionnalités client de "cp1"
.
_clientApp = PublicClientApplicationBuilder.Create(App.ClientId)
.WithDefaultRedirectUri()
.WithAuthority(authority)
.WithClientCapabilities(new [] {"cp1"})
.Build();
Lorsque ces conditions sont remplies, l’application peut extraire le défi des revendications à partir de l’en-tête de réponse de l’API comme suit :
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;
}
Votre application utilise ensuite le défi de revendications pour obtenir un nouveau jeton d’accès pour la ressource.
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);
}
}
Une fois que votre application est prête à traiter le défi de revendications retourné par une ressource prenant en charge l’évaluation continue de l’accès, vous pouvez indiquer à Microsoft Identity que votre application est prête à utiliser l’évaluation continue de l’accès en ajoutant une propriété clientCapabilities
dans la configuration MSAL.
const msalConfig = {
auth: {
clientId: 'Enter_the_Application_Id_Here',
clientCapabilities: ["CP1"]
// remaining settings...
}
}
const msalInstance = new PublicClientApplication(msalConfig);
Lorsque ces conditions sont remplies, l’application peut extraire le défi des revendications à partir de l’en-tête de réponse de l’API comme suit :
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)
Déclarer la prise en charge de la fonctionnalité client CP1
Dans la configuration de votre application, vous devez déclarer que votre application prend en charge CAE en incluant la fonctionnalité client CP1
. Ceci est spécifié à l’aide de la propriété JSON client_capabilities
.
{
"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"
}
}
]
}
Répondre aux défis CAE au moment du runtime
Effectuez une demande à une ressource, si la réponse contient un défi de revendications, vous devez l’extraire et l’alimenter dans MSAL pour l’utiliser dans la requête suivante.
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
Les extraits de code suivants décrivent le flux d’acquisition d’un jeton en mode silencieux, qui effectue un appel http au fournisseur de ressources, puis gère un cas CAE. Un appel d’interaction supplémentaire peut être nécessaire si l’appel silencieux a échoué avec les revendications.
Déclarer la prise en charge de la fonctionnalité client CP1
Dans la configuration de votre application, vous devez déclarer que votre application prend en charge CAE en incluant la fonctionnalité client CP1
. Ceci est spécifié à l’aide de la propriété clientCapabilities
.
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)
Implémenter une fonction d’assistance pour analyser les défis liés aux revendications.
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)
}
Interception et analyse de l’état 401 / défis liés aux revendications.
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
}
Lorsque ces conditions sont remplies, l’application peut extraire le défi des revendications à partir de l’en-tête de réponse de l’API comme suit :
Publier les fonctionnalités du client.
client, err := New("client-id", WithAuthority(authority), WithClientCapabilities([]string{"cp1"}))
Analysez l’en-tête WWW-Authenticate
et transmettez le défi correspondant dans MSAL-Go.
// No snippet provided at this time
Essayez d’obtenir un jeton en mode silencieux avec la demande de revendications.
var ar AuthResult;
ar, err := client.AcquireTokenSilent(ctx, tokenScope, public.WithClaims(claims))
Vous pouvez tester votre application en connectant un utilisateur, puis en utilisant le portail Azure pour révoquer la session de l’utilisateur. La prochaine fois que l’application appellera l’API prenant en charge l’évaluation continue de l’accès, l’utilisateur sera invité à se réauthentifier.