Como o risco e a política são avaliados em tempo real, o tempo de vida do token de algumas APIs de recurso pode aumentar em até 28 horas. Esses tokens de longa duração são atualizados proativamente pela Biblioteca de Autenticação da Microsoft (MSAL), aumentando a resiliência dos seus aplicativos.
Para usar a CAE, o aplicativo e a API do recurso que ele acessa precisam estar habilitados para CAE. Se uma API de recurso implementar a CAE e o aplicativo declarar que pode manipular a CAE, o aplicativo obterá tokens da CAE para esse recurso. Por esse motivo, se você declarar que seu aplicativo está pronto para CAE, seu aplicativo deverá lidar com o desafio da declaração de CAE para todas as APIs de recurso que aceitam tokens de acesso do Microsoft Identity.
No entanto, preparar seu código para dar suporte a recursos habilitados para CAE não limita sua capacidade de trabalhar com APIs que não dão suporte para CAE. Se seu aplicativo não lidar corretamente com as respostas do CAE, ele poderá tentar repetidamente uma chamada à API usando um token que é tecnicamente válido, mas que foi revogado devido ao CAE.
Comece adicionando código para lidar com 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 detecta uma alteração no endereço IP usado. O cabeçalho WWW-Authenticate
contém um Desafio de Declarações que p 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.NET WwwAuthenticateParameters
.
if (APIresponse.IsSuccessStatusCode)
{
// ...
}
else
{
if (APIresponse.StatusCode == System.Net.HttpStatusCode.Unauthorized
&& APIresponse.Headers.WwwAuthenticate.Any())
{
string claimChallenge = WwwAuthenticateParameters.GetClaimChallengeFromResponseHeaders(APIresponse.Headers);
Seu aplicativo então usará 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á informar ao Microsoft Identity que seu aplicativo está pronto para CAE. Para fazer isso no seu aplicativo MSAL, crie seu Cliente Público usando as Funcionalidades do 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;
}
Seu aplicativo então usará 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 à Identidade da Microsoft que seu aplicativo está pronto para a CAE adicionando uma propriedade clientCapabilities
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)
Declare suporte para a Capacidade do Cliente CP1
Na configuração do seu aplicativo, você deve declarar que seu aplicativo dá suporte para CAE incluindo a capacidade do cliente CP1
. Isso é especificado usando a propriedade 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"
}
}
]
}
Responder aos Desafios do CAE em Responder
Faça uma solicitação a um recurso, se a resposta contiver um desafio de declarações, extraia-o e alimente-o novamente na 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 de forma silenciosa, fazendo uma chamada HTTP para o provedor de recursos e, em seguida, lidando com um caso de CAE. Uma chamada de interação extra pode ser necessária se a chamada silenciosa falhar com as declarações.
Declarar suporte para a capacidade do cliente CP1
Na configuração do seu aplicativo, você deve declarar que seu aplicativo dá suporte para CAE incluindo a capacidade do cliente CP1
. Isso é especificado usando a propriedade 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)
Implementar 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)
}
Capturar e analisar desafios de 401 / declaraçõ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:
Anunciar as capacidades do cliente.
client, err := New("client-id", WithAuthority(authority), WithClientCapabilities([]string{"cp1"}))
analisar o cabeçalho WWW-Authenticate
e passar o desafio resultante para a MSAL-Go.
// No snippet provided at this time
Tentar adquirir um token de forma silenciosa com o desafio de declarações.
var ar AuthResult;
ar, err := client.AcquireTokenSilent(ctx, tokenScope, public.WithClaims(claims))
Você pode testar seu aplicativo conectando um usuário no aplicativo e usando o portal do Azure para revogar as sessões do usuário. Na próxima vez que o aplicativo chamar a API habilitada para CAE, o usuário deverá se autenticar.