Ponieważ ryzyko i zasady są oceniane w czasie rzeczywistym, niektóre interfejsy API zasobów mogą wzrosnąć o maksymalnie 28 godzin. Te długotrwałe tokeny są aktywnie odświeżane przez bibliotekę Microsoft Authentication Library (MSAL), zwiększając odporność aplikacji.
Aby korzystać ze środowiska CAE, zarówno aplikacja, jak i interfejs API zasobów, do których uzyskuje dostęp, musi być włączona funkcja CAE. Jeśli interfejs API zasobów implementuje interfejs CAE, a aplikacja deklaruje, że może obsługiwać interfejs CAE, aplikacja otrzymuje tokeny CAE dla tego zasobu. Z tego powodu, jeśli zadeklarowasz gotowość caE aplikacji, aplikacja musi obsługiwać wyzwanie oświadczenia CAE dla wszystkich interfejsów API zasobów, które akceptują tokeny dostępu tożsamości firmy Microsoft.
Jednak przygotowanie kodu do obsługi zasobów z obsługą caE nie ogranicza możliwości pracy z interfejsami API, które nie obsługują interfejsów CAE. Jeśli aplikacja nie obsługuje poprawnie odpowiedzi CAE, może wielokrotnie ponowić próbę wywołania interfejsu API przy użyciu tokenu, który jest technicznie prawidłowy, ale jest odwołany ze względu na caE.
Zacznij od dodania kodu do obsługi odpowiedzi z interfejsu API zasobów odrzucającego wywołanie z powodu caE. W przypadku środowiska CAE interfejsy API zwracają stan 401 i WWW-Authenticate
nagłówek po odwołaniu tokenu dostępu lub interfejs API wykryje zmianę używanego adresu IP. Nagłówek WWW-Authenticate
zawiera wyzwanie oświadczeń, którego aplikacja może użyć do uzyskania nowego tokenu dostępu.
Po spełnieniu tych warunków aplikacja może wyodrębnić i zdekodować wyzwanie dotyczące oświadczeń przy użyciu klasy MSAL.NETWwwAuthenticateParameters
.
if (APIresponse.IsSuccessStatusCode)
{
// ...
}
else
{
if (APIresponse.StatusCode == System.Net.HttpStatusCode.Unauthorized
&& APIresponse.Headers.WwwAuthenticate.Any())
{
string claimChallenge = WwwAuthenticateParameters.GetClaimChallengeFromResponseHeaders(APIresponse.Headers);
Następnie aplikacja używa wyzwania dotyczącego oświadczeń, aby uzyskać nowy token dostępu dla zasobu.
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);
}
// ...
Gdy aplikacja będzie gotowa do obsługi żądania oświadczenia zwróconego przez zasób z obsługą caE, możesz poinformować, że usługa Microsoft Identity twoja aplikacja jest gotowa do użycia w usłudze CAE. Aby to zrobić w aplikacji MSAL, skompiluj klienta publicznego przy użyciu funkcji "cp1"
klienta programu .
_clientApp = PublicClientApplicationBuilder.Create(App.ClientId)
.WithDefaultRedirectUri()
.WithAuthority(authority)
.WithClientCapabilities(new [] {"cp1"})
.Build();
Po spełnieniu tych warunków aplikacja może wyodrębnić żądanie oświadczeń z nagłówka odpowiedzi interfejsu API w następujący sposób:
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;
}
Następnie aplikacja używa wyzwania dotyczącego oświadczeń, aby uzyskać nowy token dostępu dla zasobu.
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);
}
}
Gdy aplikacja będzie gotowa do obsługi żądania zgłoszonego przez zasób z obsługą caE, możesz poinformować, że tożsamość firmy Microsoft aplikacja jest gotowa do użycia przez dodanie clientCapabilities
właściwości w konfiguracji biblioteki MSAL.
const msalConfig = {
auth: {
clientId: 'Enter_the_Application_Id_Here',
clientCapabilities: ["CP1"]
// remaining settings...
}
}
const msalInstance = new PublicClientApplication(msalConfig);
Po spełnieniu tych warunków aplikacja może wyodrębnić żądanie oświadczeń z nagłówka odpowiedzi interfejsu API w następujący sposób:
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)
Deklarowanie obsługi możliwości klienta CP1
W konfiguracji aplikacji należy zadeklarować, że aplikacja obsługuje caE, uwzględniając CP1
możliwości klienta. Jest to określane przy użyciu client_capabilities
właściwości 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"
}
}
]
}
Reagowanie na wyzwania caE w czasie wykonywania
Prześlij żądanie do zasobu, jeśli odpowiedź zawiera żądanie oświadczeń, wyodrębnij je i przekaż go z powrotem do biblioteki MSAL do użycia w następnym żądaniu.
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
Poniższe fragmenty kodu opisują przepływ uzyskiwania tokenu w trybie dyskretnym, wykonując wywołanie http dostawcy zasobów, a następnie obsługę przypadku CAE. Jeśli wywołanie dyskretne nie powiodło się z oświadczeniami, może być wymagane dodatkowe wywołanie interakcji.
Deklarowanie obsługi możliwości klienta CP1
W konfiguracji aplikacji należy zadeklarować, że aplikacja obsługuje caE, uwzględniając CP1
możliwości klienta. Jest to określane przy użyciu clientCapabilities
właściwości .
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)
Zaimplementuj funkcję pomocnika na potrzeby analizowania wyzwań oświadczeń.
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 / roszczenia wyzwania.
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
}
Po spełnieniu tych warunków aplikacja może wyodrębnić żądanie oświadczeń z nagłówka odpowiedzi interfejsu API w następujący sposób:
Anonsuj możliwości klienta.
client, err := New("client-id", WithAuthority(authority), WithClientCapabilities([]string{"cp1"}))
WWW-Authenticate
Przeanalizuj nagłówek i przekaż wynikowe wyzwanie do biblioteki MSAL-Go.
// No snippet provided at this time
Spróbuj uzyskać token dyskretnie z wyzwaniem oświadczeń.
var ar AuthResult;
ar, err := client.AcquireTokenSilent(ctx, tokenScope, public.WithClaims(claims))
Aplikację można przetestować, logując się do użytkownika, a następnie używając witryny Azure Portal, aby odwołać sesję użytkownika. Przy następnym wywołaniu interfejsu API z obsługą caE użytkownik zostanie poproszony o ponowne uwierzytelnienie.