Αυτό το άρθρο δείχνει πώς μπορείτε να εφαρμόσετε την ανατεθειμένη ροή OAUTH 2.0 Token Exchange RFC 8693 μεταξύ δύο API, το ένα χρησιμοποιεί το Azure AD για να εξουσιοδοτήσει τα αιτήματα HTTP και ένα δεύτερο API που προστατεύεται με το OpenIddict. Το προστατευμένο API του Azure AD χρησιμοποιεί την εκχωρημένη ροή OAUTH 2.0 Token Exchange RFC 8693 για να αποκτήσει ένα νέο διακριτικό πρόσβασης με ανάθεση του OpenIddict χρησιμοποιώντας το διακριτικό ανάθεσης πρόσβασης AAD. Μια εφαρμογή σελίδας ASP.NET Core Razor που χρησιμοποιεί έναν εμπιστευτικό πελάτη χρησιμοποιείται για τη λήψη του διακριτικού πρόσβασης Azure AD με ένα πρόσβαση_ως_χρήστη πεδίο εφαρμογής. Με τη χρήση της ροής ανταλλαγής διακριτικών OAUTH 2.0, μπορεί να αποφευχθεί η ανάμειξη εξουσιοδότησης εκχώρησης και εφαρμογής και μπορεί να μειωθεί η εμπιστοσύνη μεταξύ των συστημάτων.
Κώδικας: https://github.com/damienbod/OAuthGrantExchangeOidcDownstreamApi
Ρυθμίστε το OAUTH 2.0 Token Exchange RFC 8693 για ανατεθειμένες ροές
Μια εφαρμογή διεπαφής χρήστη σελίδας Razor υλοποιείται χρησιμοποιώντας το Azure AD ως πάροχο ταυτότητας. Αυτή η εφαρμογή ελέγχει την ταυτότητα χρησιμοποιώντας έναν εμπιστευτικό πελάτη έναντι του Azure AD. Το UI χρησιμοποιεί Microsoft.Identity.Web για την εφαρμογή της λογικής ελέγχου ταυτότητας πελάτη. Η εφαρμογή ζητά ένα διακριτικό πρόσβασης με ανάθεση του Azure AD για να χρησιμοποιήσει το API το οποίο προστατεύεται επίσης με το Azure AD. Αυτή η εφαρμογή API πρέπει να χρησιμοποιεί ένα μεταγενέστερο API το οποίο προστατεύεται χρησιμοποιώντας ξεχωριστό πάροχο ταυτότητας και προστατεύεται χρησιμοποιώντας OpenIddict. Το API χρησιμοποιεί το διακριτικό πρόσβασης Azure AD για να αποκτήσει ένα άλλο διακριτικό πρόσβασης το οποίο αποδέχεται το προστατευμένο API του OpenIddict. Η ανταλλαγή διακριτικών OAuth 2.0 RFC 8693 χρησιμοποιείται για την υλοποίηση αυτού χρησιμοποιώντας την εκχωρημένη ροή. Μπορούν να χρησιμοποιηθούν μόνο γνωστά διακριτικά ανάθεσης πρόσβασης Azure AD. Ο πάροχος ταυτότητας που χρησιμοποιείται για τη φιλοξενία του OpenIddict υλοποιεί τη λογική του διακομιστή της ροής ανταλλαγής διακριτικών. Το κράτησα χωριστά, αλλά υποθέτω ότι θα μπορούσε να ενσωματωθεί και στο OpenIddict. Είναι σημαντικό να επικυρωθεί σωστά η ροή και όχι μόνο η ροή, αλλά η λογική αντιστοίχισης μεταξύ των διαφορετικών ταυτοτήτων που χρησιμοποιούνται στο διακριτικό πρόσβασης με ανάθεση. Δεν εφάρμοσα την πλήρη προδιαγραφή σε αυτήν την επίδειξη, μόνο τα bit που απαιτούνται για την εκχωρημένη ροή. Η πλαστοπροσωπία και άλλες τέτοιες περιπτώσεις χρήσης για το RFC 8693 δεν υποστηρίζονται προς το παρόν. Ίσως το εφαρμόσω αργότερα.

Εφαρμόστε τον πελάτη OAUTH 2.0 Token Exchange
ο GetApiDataAsync Η μέθοδος χρησιμοποιείται για να λάβετε ένα διακριτικό πρόσβασης για το OpenIddict downstream API και να το χρησιμοποιήσετε για να λάβετε τα δεδομένα. Χρησιμοποιεί το GetApiTokenOauthGrantTokenExchange να λάβει το διακριτικό πρόσβασης χρησιμοποιώντας τη ροή ανταλλαγής διακριτικών και, στη συνέχεια, να το χρησιμοποιεί για να καλέσει το επιχειρηματικό API. Οι τιμές διαμόρφωσης χρησιμοποιούνται καθώς και το μυστικό πελάτη για την απόκτηση του νέου διακριτικού.
public async Task<List<string>> GetApiDataAsync(string aadAccessToken)
{
try
{
var client = _clientFactory.CreateClient();
client.BaseAddress = new Uri(
_downstreamApi.Value.ApiBaseAddress);
var access_token = await _apiTokenClient
.GetApiTokenOauthGrantTokenExchange
(
_downstreamApi.Value.ClientId,
_downstreamApi.Value.Audience,
_downstreamApi.Value.ScopeForAccessToken,
_downstreamApi.Value.ClientSecret,
aadAccessToken
);
client.SetBearerToken(access_token);
var response = await client.GetAsync("api/values");
if (response.IsSuccessStatusCode)
{
var data = await JsonSerializer
.DeserializeAsync<List<string>>(
await response.Content.ReadAsStreamAsync());
if(data != null)
return data;
return new List<string>();
}
throw new ApplicationException($"Status code: {response.StatusCode},
Error: {response.ReasonPhrase}");
}
catch (Exception e)
{
throw new ApplicationException($"Exception {e}");
}
}
ο GetApiTokenOauthGrantTokenExchangeAad είναι μια εσωτερική μέθοδος που χρησιμοποιείται για να καλέσετε τον πάροχο ταυτότητας OpenIddict για να λάβετε το σωστό διακριτικό πρόσβασης. Αυτή η μέθοδος καλείται μόνο μία φορά ανά περίοδο λειτουργίας ή για όσο διάστημα το διακριτικό είναι έγκυρο. Αυτό συνήθως αποθηκεύεται στην προσωρινή μνήμη μόλις αποκτηθεί. Η μέθοδος μεταβιβάζει τις απαιτούμενες παραμέτρους που ταιριάζουν με τις ρυθμίσεις διακομιστή.
private async Task<AccessTokenItem> GetApiTokenOauthGrantTokenExchangeAad(
string clientId,
string audience,
string scope,
string clientSecret,
string aadAccessToken)
{
var tokenExchangeHttpClient = _httpClientFactory.CreateClient();
tokenExchangeHttpClient.BaseAddress = new Uri(
_downstreamApiConfigurations.Value.IdentityProviderUrl);
var tokenExchangeSuccessResponse = await RequestDelegatedAccessToken
.GetDelegatedApiTokenTokenExchange(
new GetDelegatedApiTokenOAuthTokenExchangeModel
{
Scope = scope,
AccessToken = aadAccessToken,
ClientSecret = clientSecret,
Audience = audience,
ClientId = clientId,
EndpointUrl = "/connect/oauthTokenExchangetoken",
GrantExchangeHttpClient = tokenExchangeHttpClient
}, _logger);
if (tokenExchangeSuccessResponse != null)
{
return new AccessTokenItem
{
ExpiresIn = DateTime.UtcNow
.AddSeconds(tokenExchangeSuccessResponse.expires_in),
AccessToken = tokenExchangeSuccessResponse.access_token
};
}
_logger.LogError(
"no success response from oauth token exchange access token request");
throw new ApplicationException(
"no success response from oauth token exchange access token request");
}
ο GetDelegatedApiTokenTokenExchange μέθοδος υλοποιεί την επιχείρηση πελάτη της ροής OAuth. Αυτό δημιουργεί μια κεφαλίδα ελέγχου ταυτότητας χρησιμοποιώντας βασικό έλεγχο ταυτότητας, καθώς θέλουμε να χρησιμοποιήσουμε μόνο έναν εμπιστευτικό πελάτη για αυτό. Οι παράμετροι μεταβιβάζονται ως α KeyValuePair και ταιριάζει με τις καθορισμένες προδιαγραφές στο RFC 8693 για το σώμα POST. Εάν τα δεδομένα επιστραφούν σωστά, επιστρέφεται μια απάντηση επιτυχίας, διαφορετικά η απόκριση σφάλματος όπως στον ορισμό RFC με μερικές επιπλέον παραμέτρους. ο OauthTokenExchangeSuccessResponse χρησιμοποιείται για τη λήψη της επιτυχημένης απόκρισης HTTP από το αίτημα POST.
public static async Task<OauthTokenExchangeSuccessResponse?> GetDelegatedApiTokenTokenExchange(
GetDelegatedApiTokenOAuthTokenExchangeModel reqData, ILogger logger)
{
if (reqData.GrantExchangeHttpClient == null)
throw new ArgumentException("Httpclient missing, is null");
string credentials = CreateBasicAuthenticationHeader(reqData);
reqData.GrantExchangeHttpClient.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Basic", credentials);
KeyValuePair<string, string>[] oauthTokenExchangeBody = CreateTokenExchangeBody(reqData);
var response = await reqData.GrantExchangeHttpClient.PostAsync(reqData.EndpointUrl,
new FormUrlEncodedContent(oauthTokenExchangeBody));
if (response.IsSuccessStatusCode)
{
var tokenResponse = await JsonSerializer.DeserializeAsync<OauthTokenExchangeSuccessResponse>(
await response.Content.ReadAsStreamAsync());
return tokenResponse;
}
if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
{
// Unauthorized error
var errorResult = await JsonSerializer.DeserializeAsync<OauthTokenExchangeErrorResponse>(
await response.Content.ReadAsStreamAsync());
if (errorResult != null)
{
logger.LogInformation("{error} {error_description} {correlation_id} {trace_id}",
errorResult.error,
errorResult.error_description,
errorResult.correlation_id,
errorResult.trace_id);
}
else
{
logger.LogInformation("RequestDelegatedAccessToken Error, Unauthorized unknown reason");
}
}
else
{
// unknown error, log
logger.LogInformation("RequestDelegatedAccessToken Error unknown reason");
}
return null;
}
ο CreateTokenExchangeBody δημιουργεί το σώμα. Αυτό υλοποιείται για την εκχωρημένη ροή που ζητά ένα διακριτικό πρόσβασης. ο υποκείμενο_κουπόνι Η παράμετρος χρησιμοποιείται για τη μετάδοση του διακριτικού πρόσβασης Azure AD.
private static KeyValuePair<string, string>[] CreateTokenExchangeBody(
GetDelegatedApiTokenOAuthTokenExchangeModel reqData)
{
// Content-Type: application/x-www-form-urlencoded
var oauthTokenExchangeBody = new[]
{
new KeyValuePair<string, string>("grant_type",
OAuthGrantExchangeConsts.GRANT_TYPE),
new KeyValuePair<string, string>("audience", reqData.Audience),
new KeyValuePair<string, string>("subject_token_type",
OAuthGrantExchangeConsts.TOKEN_TYPE_ACCESS_TOKEN),
new KeyValuePair<string, string>("subject_token", reqData.AccessToken),
new KeyValuePair<string, string>("scope", reqData.Scope)
// new KeyValuePair<string, string>("resource", "--optional--")
// new KeyValuePair<string, string>("requested_token_type", "--optional--")
// new KeyValuePair<string, string>("actor_token", "--optional--")
// new KeyValuePair<string, string>("actor_token_type", "--optional--")
};
return oauthTokenExchangeBody;
}
Δημιούργησα μια κλάση conss για την υλοποίηση της προδιαγραφής ανά καθορισμένους τύπους συμβολοσειράς.
public class OAuthGrantExchangeConsts
{
public const string TOKEN_TYPE_ACCESS_TOKEN = "urn:ietf:params:oauth:token-type:access_token";
public const string TOKEN_TYPE_REFRESH_TOKEN = "urn:ietf:params:oauth:token-type:refresh_token";
public const string TOKEN_TYPE_ID_TOKEN = "urn:ietf:params:oauth:token-type:id_token";
public const string TOKEN_TYPE_SAML1 = "urn:ietf:params:oauth:token-type:saml1";
public const string TOKEN_TYPE_SAML2 = "urn:ietf:params:oauth:token-type:saml2";
public const string GRANT_TYPE = "urn:ietf:params:oauth:grant-type:token-exchange";
public const string ERROR_INVALID_REQUEST = "invalid_request";
public const string ERROR_INVALID_CLIENT = "invalid_client";
public const string ERROR_INVALID_GRANT = "invalid_grant";
public const string ERROR_UNAUTHORIZED_CLIENT = "unauthorized_client";
public const string ERROR_UNSUPPORTED_GRANT_TYPE = "unsupported_grant_type";
public const string ERROR_INVALID_SCOPE = "invalid_scope";
// ... more consts, see the code for the full definitions
}
Αυτό είναι το μόνο που απαιτείται για την υλοποίηση της πλευράς πελάτη της ροής ανάθεσης ανταλλαγής OAuth Token. Εάν χρειάζεστε άλλους τύπους ροής από αυτήν την προδιαγραφή, τότε αυτό πρέπει να εφαρμοστεί. Δείτε τα έγγραφα RFC για λεπτομέρειες (Στους παρακάτω συνδέσμους)
Υλοποιήστε τον διακομιστή OAUTH 2.0 Token Exchange
Το τμήμα διακομιστή της ροής πρέπει να επικυρώσει μερικά διαφορετικά πράγματα. Ο πάροχος ταυτότητας επικυρώνει το αίτημα POST χρησιμοποιώντας τον έλεγχο ταυτότητας BASIC και, στη συνέχεια, επικυρώνει το σώμα του αιτήματος HTTP POST. Ο διακομιστής πρέπει να επικυρώσει πλήρως το διακριτικό πρόσβασης Azure AD συμπεριλαμβανομένου του υπογραφή, aud και iss σύμφωνα με το πρότυπο. Μόλις επικυρωθεί το διακριτικό Azure AD, οι αξιώσεις μπορούν να χρησιμοποιηθούν για την εξουσιοδότηση της ταυτότητας που εκχωρείται στο διακριτικό πρόσβασης. Μόνο τα διακριτικά πρόσβασης με ανάθεση θα πρέπει να γίνονται δεκτά και έτσι σε ένα διακριτικό Azure AD V2, μπορείτε να το κάνετε ελέγχοντας για οειδ αξίωση και α scp απαίτηση. Αυτές οι αξιώσεις ενδέχεται να μετονομαστούν εάν χρησιμοποιηθούν οι προεπιλεγμένοι χώροι ονομάτων της Microsoft. Ο διακομιστής πρέπει να αντιστοιχίσει τους χρήστες του με τους χρήστες του Azure AD. Πρέπει να είστε προσεκτικοί όταν χρησιμοποιείτε email για αυτό. Το Azure OID είναι μια καλή αξίωση για χρήση για αυτό.
Ο διακομιστής πρέπει να κάνει τα εξής:
- Επικυρώστε τον Βασικό έλεγχο ταυτότητας
- Επικυρώστε το σώμα του αιτήματος POST σύμφωνα με το πρότυπο
- Επικυρώστε πλήρως το διακριτικό πρόσβασης
- Επικυρώστε τις αξιώσεις, κάντε την εξουσιοδότηση
- Δημιουργήστε το νέο διακριτικό πρόσβασης σύμφωνα με το πρότυπο
Επικύρωση βασικού ελέγχου ταυτότητας
Ο βασικός έλεγχος ταυτότητας χρησιμοποιείται έτσι ώστε μόνο εμπιστευτικοί πελάτες να μπορούν να χρησιμοποιούν το API. Αυτή δεν είναι η ισχυρότερη μέθοδος ελέγχου ταυτότητας, αλλά είναι ο τρόπος με τον οποίο η προδιαγραφή συνιστά την αποστολή της ταυτότητα πελάτη και clientSecret. Ο χρησιμοποιούμενος έλεγχος ταυτότητας επικυρώνεται χρησιμοποιώντας ένα χαρακτηριστικό Authorize και το σωστό σχήμα.
[Authorize(AuthenticationSchemes = BasicAuthenticationDefaults.AuthenticationScheme)]
[HttpPost("~/connect/oauthTokenExchangetoken"), Produces("application/json")]
public async Task<IActionResult> Exchange([FromForm] OauthTokenExchangePayload oauthTokenExchangePayload)
{
// Implement validate and create AT logic
}
Μόλις επαληθευτεί η ταυτότητα, μπορεί να ξεκινήσει η επικύρωση.
Επικύρωση ωφέλιμου φορτίου του αιτήματος POST
Το ωφέλιμο φορτίο του αιτήματος HTTP POST έχει επικυρωθεί. Αυτό ελέγχει ότι το σώμα έχει τις αναμενόμενες τιμές και αυτές που επιτρέπονται. Εάν κάποια είναι λανθασμένα, επιστρέφεται η παράμετρος σφάλματος του μη εξουσιοδοτημένου αιτήματος όπως ορίζεται στην προδιαγραφή.
var (Valid, Reason, Error) = ValidateOauthTokenExchangeRequestPayload
.IsValid(oauthTokenExchangePayload,
_oauthTokenExchangeConfigurationConfiguration);
if(!Valid)
{
return UnauthorizedValidationParametersFailed(
oauthTokenExchangePayload, Reason, Error);
}
Επικύρωση διακριτικού πρόσβασης και υπογραφής
Εάν το ωφέλιμο φορτίο είναι επικυρωμένο, τότε το διακριτικό πρόσβασης αποστέλλεται χρησιμοποιώντας το υποκείμενο_κουπόνι επικυρώνεται η παράμετρος. Αυτό πρέπει να είναι πλήρως επικυρωμένο συμπεριλαμβανομένης της υπογραφής. Τα γνωστά τελικά σημεία του παρόχου ταυτότητας Azure AD χρησιμοποιούνται για τη λήψη των δημόσιων κλειδιών του πιστοποιητικού που χρησιμοποιούνται για τη δημιουργία του διακριτικού JWT. Αυτό χρησιμοποιείται για την επικύρωση της υπογραφής διακριτικού. ο iss και το aud επικυρώνονται και ελέγχονται σε σχέση με τις αναμενόμενες τιμές.
// get well known endpoints and validate access token sent in the assertion
var configurationManager = new ConfigurationManager<OpenIdConnectConfiguration>(
_oauthTokenExchangeConfigurationConfiguration.AccessTokenMetadataAddress,
new OpenIdConnectConfigurationRetriever());
var wellKnownEndpoints = await configurationManager
.GetConfigurationAsync();
var accessTokenValidationResult =
ValidateOauthTokenExchangeRequestPayload.ValidateTokenAndSignature(
oauthTokenExchangePayload.subject_token,
_oauthTokenExchangeConfigurationConfiguration,
wellKnownEndpoints.SigningKeys);
if(!accessTokenValidationResult.Valid)
{
return UnauthorizedValidationTokenAndSignatureFailed(
oauthTokenExchangePayload, accessTokenValidationResult);
}
ο ValidateTokenAndSignature η μέθοδος ελέγχει και επικυρώνει το διακριτικό.
public static (bool Valid, string Reason, ClaimsPrincipal?
ClaimsPrincipal) ValidateTokenAndSignature(
string jwtToken,
OauthTokenExchangeConfiguration oboConfiguration,
ICollection<SecurityKey> signingKeys)
{
try
{
var validationParameters = new TokenValidationParameters
{
RequireExpirationTime = true,
ValidateLifetime = true,
ClockSkew = TimeSpan.FromMinutes(1),
RequireSignedTokens = true,
ValidateIssuerSigningKey = true,
IssuerSigningKeys = signingKeys,
ValidateIssuer = true,
ValidIssuer = oboConfiguration.AccessTokenAuthority,
ValidateAudience = true,
ValidAudience = oboConfiguration.AccessTokenAudience
};
ISecurityTokenValidator tokenValidator = new JwtSecurityTokenHandler();
var claimsPrincipal = tokenValidator
.ValidateToken(jwtToken, validationParameters, out var _);
return (true, string.Empty, claimsPrincipal);
}
catch (Exception ex)
{
return (false, $"Access Token Authorization failed {ex.Message}", null);
}
}
Επικυρώστε τις αξιώσεις και εξουσιοδοτήστε το διακριτικό πρόσβασης
Τώρα που το διακριτικό έχει επικυρωθεί, επιστρέφεται αξιώσεις Κύριος μπορεί να χρησιμοποιηθεί για τον έλεγχο και την εξουσιοδότηση της ταυτότητας από το διακριτικό πρόσβασης. Το διακριτικό πρέπει να επικυρωθεί ότι είναι ένα διακριτικό ανάθεσης και πρέπει να περιέχει α scp αξίωση και μια οειδ απαίτηση. ο scp είναι αυτό που προσθέσαμε για να χρησιμοποιήσουμε την υπηρεσία. Προσθέσαμε ένα πρόσβαση_ως_χρήστη απαίτηση. Θα απέφευγα τους ρόλους καθώς οι ρόλοι μπορούν να χρησιμοποιηθούν και για διακριτικά εφαρμογής. Ταίριαξα την αξίωση ονόματος με το email από την ταυτότητα στο δεύτερο σύστημα IAM. Η χρήση της αξίωσης OID θα ήταν ένας πιο αξιόπιστος τρόπος για να γίνει αυτό.
// get claims from aad token and re use in OpenIddict token
var claimsPrincipal = accessTokenValidationResult.ClaimsPrincipal;
var isDelegatedToken = ValidateOauthTokenExchangeRequestPayload
.IsDelegatedAadAccessToken(claimsPrincipal);
if (!isDelegatedToken)
{
return UnauthorizedValidationRequireDelegatedTokenFailed();
}
var name = ValidateOauthTokenExchangeRequestPayload
.GetPreferredUserName(claimsPrincipal);
var isNameAndEmail = ValidateOauthTokenExchangeRequestPayload
.IsEmailValid(name);
if(!isNameAndEmail)
{
return UnauthorizedValidationPrefferedUserNameFailed();
}
// validate user exists
var user = await _userManager.FindByNameAsync(name);
if (user == null)
{
return UnauthorizedValidationNoUserExistsFailed();
}
Ο έλεγχος διακριτικού πρόσβασης με ανάθεση επικυρώνεται χρησιμοποιώντας το oid και τις αξιώσεις scp. Μερικές φορές οι αξιώσεις αλλάζουν χρησιμοποιώντας τους χώρους ονομάτων της Microsoft. Πρόσθεσα έναν εναλλακτικό έλεγχο για να επικυρώσω και τα δύο.
public static bool IsDelegatedAadAccessToken(ClaimsPrincipal claimsPrincipal)
{
// oid if magic MS namespaces not user
var oid = claimsPrincipal.Claims.FirstOrDefault(t => t.Type ==
"http://schemas.microsoft.com/identity/claims/objectidentifier");
// scp if magic MS namespaces not added
var scp = claimsPrincipal.Claims.FirstOrDefault(t => t.Type
== "http://schemas.microsoft.com/identity/claims/scope");
if (oid != null && scp != null)
{
return true;
}
oid = claimsPrincipal.Claims.FirstOrDefault(t => t.Type == "oid");
scp = claimsPrincipal.Claims.FirstOrDefault(t => t.Type == "scp");
if (oid != null && scp != null)
{
return true;
}
return false;
}
Δημιουργία νέου διακριτικού πρόσβασης
Δημιουργείται ένα νέο διακριτικό πρόσβασης χρησιμοποιώντας το ίδιο πιστοποιητικό με το προεπιλεγμένο που χρησιμοποιείται από το OpenIddict. Αυτό καθιστά δυνατή την επικύρωση του διακριτικού χρησιμοποιώντας τα γνωστά τελικά σημεία.
// use data and return new access token
var (ActiveCertificate, _) = await Startup.GetCertificates(_environment, _configuration);
var tokenData = new CreateDelegatedAccessTokenPayloadModel
{
Sub = Guid.NewGuid().ToString(),
ClaimsPrincipal = claimsPrincipal,
SigningCredentials = ActiveCertificate,
Scope = _oauthTokenExchangeConfigurationConfiguration.ScopeForNewAccessToken,
Audience = _oauthTokenExchangeConfigurationConfiguration.AudienceForNewAccessToken,
Issuer = _oauthTokenExchangeConfigurationConfiguration.IssuerForNewAccessToken,
OriginalClientId = _oauthTokenExchangeConfigurationConfiguration.AccessTokenAudience
};
var accessToken = CreateDelegatedAccessTokenPayload.GenerateJwtTokenAsync(tokenData);
_logger.LogInformation("OBO new access token returned sub {sub}", tokenData.Sub);
if(IdentityModelEventSource.ShowPII)
{
_logger.LogDebug("OBO new access token returned for sub {sub} for user {Username}", tokenData.Sub,
ValidateOauthTokenExchangeRequestPayload.GetPreferredUserName(claimsPrincipal));
}
return Ok(new OauthTokenExchangeSuccessResponse
{
expires_in = 60 * 60,
access_token = accessToken,
scope = oauthTokenExchangePayload.scope
});
Οι αξιώσεις προστίθενται όπως στην προδιαγραφή RFC.
public static string GenerateJwtTokenAsync(CreateDelegatedAccessTokenPayloadModel payload)
{
SigningCredentials signingCredentials = new X509SigningCredentials(payload.SigningCredentials);
var alg = signingCredentials.Algorithm;
//{
// "alg": "RS256",
// "kid": "....",
// "typ": "at+jwt",
//}
var subject = new ClaimsIdentity(new[] {
new Claim("sub", payload.Sub),
new Claim("scope", payload.Scope),
new Claim("act", $"{{ \"sub\": \"{payload.OriginalClientId}\" }}", JsonClaimValueTypes.Json )
});
if(payload.ClaimsPrincipal != null)
{
var name = ValidateOauthTokenExchangeRequestPayload.GetPreferredUserName(payload.ClaimsPrincipal);
var azp = ValidateOauthTokenExchangeRequestPayload.GetAzp(payload.ClaimsPrincipal);
var azpacr = ValidateOauthTokenExchangeRequestPayload.GetAzpacr(payload.ClaimsPrincipal);
if(!string.IsNullOrEmpty(name))
subject.AddClaim(new Claim("name", name));
if (!string.IsNullOrEmpty(name))
subject.AddClaim(new Claim("azp", azp));
if (!string.IsNullOrEmpty(name))
subject.AddClaim(new Claim("azpacr", azpacr));
}
var tokenHandler = new JwtSecurityTokenHandler();
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = subject,
Expires = DateTime.UtcNow.AddHours(1),
IssuedAt = DateTime.UtcNow,
Issuer = "https://localhost:44318/",
Audience = payload.Audience,
SigningCredentials = signingCredentials,
TokenType = "at+jwt"
};
tokenDescriptor.AdditionalHeaderClaims ??= new Dictionary<string, object>();
if (!tokenDescriptor.AdditionalHeaderClaims.ContainsKey("alg"))
{
tokenDescriptor.AdditionalHeaderClaims.Add("alg", alg);
}
var token = tokenHandler.CreateToken(tokenDescriptor);
return tokenHandler.WriteToken(token);
}
Ξεκινήστε όλες τις εφαρμογές και εάν όλα έχουν ρυθμιστεί σωστά με τον μισθωτή σας Azure AD, τα δεδομένα από το προστατευμένο API του OpenIddict μπορούν να χρησιμοποιηθούν και να εμφανιστούν στη διεπαφή χρήστη Azure AD.
Συνδέσεις
https://documentation.openiddict.com/configuration/application-permissions.html
https://datatracker.ietf.org/doc/html/rfc8693
https://www.youtube.com/watch?v=Ue8HKBGkIJY&t=
https://github.com/damienbod/OnBehalfFlowOidcDownstreamApi
https://www.rfc-editor.org/rfc/rfc6749#section-5.2
https://github.com/blowdart/idunno.Authentication/tree/dev/src/idunno.Authentication.Basic