Так как риск и политика оцениваются в режиме реального времени, некоторые API-интерфейсы ресурсов могут увеличиваться до 28 часов. Эти долгосрочные маркеры заранее обновляются библиотекой проверки подлинности Майкрософт (MSAL), повышая устойчивость ваших приложений.
Чтобы использовать ЦС, приложение и API ресурсов, к нему доступ, должны быть включены ЦС. Если API ресурсов реализует CAE и приложение объявляет, что оно может обрабатывать ЦС, ваше приложение получает маркеры CAE для этого ресурса. По этой причине, если вы объявляете приложение caE-ready, приложение должно обработать запрос утверждения CAE для всех API ресурсов, которые принимают маркеры доступа к удостоверениям Майкрософт.
Однако подготовка кода для поддержки ресурсов САЭ не ограничивает возможность работы с API, которые не поддерживают ЦС. Если приложение не обрабатывает ответы CAE правильно, оно может повторно повторить вызов API с помощью маркера, который технически допустим, но отозван из-за ЦС.
Сначала добавьте код для обработки ответов из API ресурсов, отклоняя вызов из-за ЦС. При использовании ЦС API возвращают состояние 401 и WWW-Authenticate
заголовок при отмене маркера доступа или API обнаруживает изменение используемого IP-адреса. Заголовок WWW-Authenticate
содержит запрос утверждений, который приложение может использовать для получения нового маркера доступа.
При выполнении этих условий приложение может извлекать и декодировать вызов утверждений с помощью класса MSAL.NETWwwAuthenticateParameters
.
if (APIresponse.IsSuccessStatusCode)
{
// ...
}
else
{
if (APIresponse.StatusCode == System.Net.HttpStatusCode.Unauthorized
&& APIresponse.Headers.WwwAuthenticate.Any())
{
string claimChallenge = WwwAuthenticateParameters.GetClaimChallengeFromResponseHeaders(APIresponse.Headers);
Затем приложение использует вызов утверждений для получения нового маркера доступа для ресурса.
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);
}
// ...
Когда приложение будет готово к обработке запроса, возвращаемого ресурсом ЦС, вы можете сообщить Microsoft Identity, что ваше приложение готово к сертификации. Для этого в приложении MSAL создайте общедоступный клиент с помощью возможностей "cp1"
клиента.
_clientApp = PublicClientApplicationBuilder.Create(App.ClientId)
.WithDefaultRedirectUri()
.WithAuthority(authority)
.WithClientCapabilities(new [] {"cp1"})
.Build();
При соблюдении этих условий приложение может извлечь запрос утверждений из заголовка ответа API следующим образом:
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;
}
Затем приложение использует вызов утверждений для получения нового маркера доступа для ресурса.
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);
}
}
Когда ваше приложение будет готово к обработке запроса с требованием на утверждение, возвращенного ресурсом с включенным CAE, вы сможете сообщить Microsoft Identity о готовности вашего приложения принимать CAE, добавив свойство clientCapabilities
в конфигурации MSAL.
const msalConfig = {
auth: {
clientId: 'Enter_the_Application_Id_Here',
clientCapabilities: ["CP1"]
// remaining settings...
}
}
const msalInstance = new PublicClientApplication(msalConfig);
При соблюдении этих условий приложение может извлечь запрос утверждений из заголовка ответа API следующим образом:
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)
Объявление поддержки возможности клиента CP1
В конфигурации приложения необходимо объявить, что приложение поддерживает ЦС, включив CP1
возможность клиента. Это указано с помощью client_capabilities
свойства 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"
}
}
]
}
Реагирование на вызовы CAE во время выполнения
Выполните запрос к ресурсу, если ответ содержит запрос утверждений, извлеките его и отправьте его обратно в MSAL для использования в следующем запросе.
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
В следующих фрагментах кода описывается поток получения токена автоматически, вызов http к поставщику ресурсов, а затем обработка регистра ЦС. При сбое автоматического вызова с утверждениями может потребоваться дополнительный вызов взаимодействия.
Объявление поддержки возможностей клиента CP1
В конфигурации приложения необходимо объявить, что приложение поддерживает ЦС, включив CP1
возможность клиента. Это указано с помощью 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)
Реализуйте вспомогающую функцию для синтаксического анализа проблем утверждений.
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)
}
Перехват и анализ 401 / вызовов утверждений.
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
}
При соблюдении этих условий приложение может извлечь запрос утверждений из заголовка ответа API следующим образом:
Объявление возможностей клиента.
client, err := New("client-id", WithAuthority(authority), WithClientCapabilities([]string{"cp1"}))
проанализировать WWW-Authenticate
заголовок и передать результирующий вызов в MSAL-Go.
// No snippet provided at this time
Попытайтесь автоматически получить маркер с вызовом утверждений.
var ar AuthResult;
ar, err := client.AcquireTokenSilent(ctx, tokenScope, public.WithClaims(claims))
Вы можете протестировать приложение, выполнив вход в него, а затем отозвав сеанс пользователя на портале Azure. В следующий раз, когда приложение вызовет API с поддержкой CAE, пользователю будет предложено повторно пройти проверку подлинности.