Como o risco e a política são avaliados em tempo real, o tempo de vida do token de algumas APIs de recursos pode aumentar em até 28 horas. Esses tokens de longa duração são atualizados proativamente pela Microsoft Authentication Library (MSAL), aumentando a resiliência de seus aplicativos.
Para usar o CAE, seu aplicativo e a API de recurso que ele está acessando devem estar habilitados para CAE. Se uma API de recurso implementar CAE e seu aplicativo declarar que pode lidar com CAE, seu aplicativo receberá tokens CAE para esse recurso. Por esse motivo, se você declarar seu aplicativo pronto para CAE, seu aplicativo deverá lidar com o desafio de declaração CAE para todas as APIs de recursos que aceitam tokens de acesso do Microsoft Identity.
No entanto, preparar seu código para oferecer suporte a recursos habilitados para CAE não limita sua capacidade de trabalhar com APIs que não suportam CAE. Se seu aplicativo não manipular respostas CAE corretamente, ele poderá repetir repetidamente uma chamada de API usando um token que é tecnicamente válido, mas é revogado devido ao CAE.
Comece adicionando código para manipular respostas da API de recurso rejeitando a chamada devido ao CAE. Com o CAE, as APIs retornam um status 401 e um WWW-Authenticate
cabeçalho quando o token de acesso é revogado ou a API deteta uma alteração no endereço IP usado. O WWW-Authenticate
cabeçalho contém um Desafio de Declarações que o aplicativo pode usar para adquirir um novo token de acesso.
Quando essas condições são atendidas, o aplicativo pode extrair e decodificar o desafio de declarações usando a classe MSAL.NETWwwAuthenticateParameters
.
if (APIresponse.IsSuccessStatusCode)
{
// ...
}
else
{
if (APIresponse.StatusCode == System.Net.HttpStatusCode.Unauthorized
&& APIresponse.Headers.WwwAuthenticate.Any())
{
string claimChallenge = WwwAuthenticateParameters.GetClaimChallengeFromResponseHeaders(APIresponse.Headers);
Em seguida, seu aplicativo usa o desafio de declarações para adquirir um novo token de acesso para o recurso.
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 seu aplicativo estiver pronto para lidar com o desafio de declaração retornado por um recurso habilitado para CAE, você poderá dizer ao Microsoft Identity que seu aplicativo está pronto para CAE. Para fazer isso em seu aplicativo MSAL, crie seu cliente público usando os recursos de cliente do "cp1"
.
_clientApp = PublicClientApplicationBuilder.Create(App.ClientId)
.WithDefaultRedirectUri()
.WithAuthority(authority)
.WithClientCapabilities(new [] {"cp1"})
.Build();
Quando essas condições são atendidas, o aplicativo pode extrair o desafio de declarações do cabeçalho de resposta da API da seguinte maneira:
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;
}
Em seguida, seu aplicativo usa o desafio de declarações para adquirir um novo token de acesso para o recurso.
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);
}
}
Quando seu aplicativo estiver pronto para lidar com o desafio de declaração retornado por um recurso habilitado para CAE, você poderá dizer ao Microsoft Identity que seu aplicativo está pronto para CAE adicionando uma clientCapabilities
propriedade na configuração MSAL.
const msalConfig = {
auth: {
clientId: 'Enter_the_Application_Id_Here',
clientCapabilities: ["CP1"]
// remaining settings...
}
}
const msalInstance = new PublicClientApplication(msalConfig);
Quando essas condições são atendidas, o aplicativo pode extrair o desafio de declarações do cabeçalho de resposta da API da seguinte maneira:
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)
Declarar suporte para a capacidade do cliente CP1
Na configuração do aplicativo, você deve declarar que o aplicativo suporta CAE incluindo o recurso do CP1
cliente. Isso é especificado usando a client_capabilities
propriedade 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"
}
}
]
}
Responda aos desafios do CAE em tempo de execução
Faça uma solicitação a um recurso, se a resposta contiver um desafio de declarações, extraia-o e alimente-o de volta para o MSAL para uso na próxima solicitação.
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
Os trechos de código a seguir descrevem o fluxo de aquisição de um token silenciosamente, fazendo uma chamada http para o provedor de recursos e, em seguida, manipulando um caso CAE. Uma chamada de interação extra pode ser necessária se a chamada silenciosa falhar com declarações.
Declarar suporte para a capacidade do cliente CP1
Na configuração do aplicativo, você deve declarar que o aplicativo suporta CAE incluindo o recurso do CP1
cliente. Isso é especificado usando a clientCapabilities
propriedade.
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)
Implemente uma função auxiliar para analisar desafios de declarações.
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 / reclamações contestações.
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 essas condições são atendidas, o aplicativo pode extrair o desafio de declarações do cabeçalho de resposta da API da seguinte maneira:
Anuncie as capacidades do cliente.
client, err := New("client-id", WithAuthority(authority), WithClientCapabilities([]string{"cp1"}))
analise o WWW-Authenticate
cabeçalho e passe o desafio resultante para o MSAL-Go.
// No snippet provided at this time
Tente adquirir um token silenciosamente com a contestação de reivindicações.
var ar AuthResult;
ar, err := client.AcquireTokenSilent(ctx, tokenScope, public.WithClaims(claims))
Você pode testar seu aplicativo entrando em um usuário e, em seguida, usando o portal do Azure para revogar a sessão do usuário. Na próxima vez que o aplicativo chamar a API habilitada para CAE, o usuário será solicitado a se autenticar novamente.