对于集成测试,我有一个授权的.NET Core 2.2控制器,该控制器正在调用另一个授权的控制器(不同的项目)或外部api(例如Microsoft Graph)。
两个api均针对Azure AD进行了身份验证。在所有控制器操作中,我们都需要经过身份验证的用户。 我们可以通过基于用户名和密码(grant_type = password)获取令牌来获取第一个api。当调用继续到第二个api时,由于交互式登录提示(我们使用ADAL)而中断。
通常,用户使用open id connect进行身份验证,然后获得身份验证代码,并使用身份验证代码获取accesstoken +刷新令牌。使用刷新令牌,我们可以获取第二个api的访问令牌。
我们创建了一个带有默认值控制器的小样本项目来解释我们的问题。
在使用本地应用程序注册调用第一个api之前获取访问令牌:
public static async Task<string> AcquireTokenAsync(string username, string password)
{
var aadInstance = "https://login.windows.net/{0}";
var tenantId = "put id here";
var authority = string.Format(aadInstance, tenantId);
var clientId = "clientid here";
var resource = "put resource here";
var client = new HttpClient();
var tokenEndpoint = $"https://login.microsoftonline.com/{tenantId}/oauth2/token";
var body = $"resource={resource}&client_id={clientId}&grant_type=password&username={username}&password={password}";
var stringContent = new StringContent(body, Encoding.UTF8, "application/x-www-form-urlencoded");
var result = await client.PostAsync(tokenEndpoint, stringContent).ContinueWith((response) =>
{
return response.Result.Content.ReadAsStringAsync().Result;
});
JObject jobject = JObject.Parse(result);
var token = jobject["access_token"].Value<string>();
return token;
}
第一个API:
[Authorize]
[HttpGet]
public async Task<IActionResult> Get()
{
string name = User.Identity.Name;
var result = await AcquireTokenSilentWithImpersonationAsync();
string BaseUrl = "https://localhost:44356/";
var client = new HttpClient
{
BaseAddress = new Uri(BaseUrl)
};
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
var url = "api/values";
HttpResponseMessage response = await client.GetAsync(url);
switch (response.StatusCode)
{
case HttpStatusCode.OK:
int x = 1;
break;
default:
throw new HttpRequestException($"Error - {response.StatusCode} in response with message '{response.RequestMessage}'");
}
return Ok();
}
private const string BackendResource = "Second api resource here";
/// <summary>
/// For more information: https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-devhowto-adal-error-handling
/// </summary>
/// <returns></returns>
public async Task<AuthenticationResult> AcquireTokenSilentWithImpersonationAsync()
{
const string ClientId = "client id of first api here";
const string ClientSecret = "secret of first api here";
ClientCredential credential = new ClientCredential(ClientId, ClientSecret);
string userObjectId = _httpContextAccessor.HttpContext.User.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier")?.Value;
var authContext = GetAuthenticationContext(userObjectId);
AuthenticationResult authResult = null;
try
{
authResult = await authContext.AcquireTokenSilentAsync(BackendResource, credential, new UserIdentifier(userObjectId, UserIdentifierType.UniqueId));
}
catch (AdalSilentTokenAcquisitionException ex)
{
// Exception: AdalSilentTokenAcquisitionException
// Caused when there are no tokens in the cache or a required refresh failed.
// Action: Case 1, resolvable with an interactive request.
try
{
authResult = await authContext.AcquireTokenAsync(BackendResource, ClientId, new Uri("https://backurl.org"), new PlatformParameters(), new UserIdentifier(userObjectId, UserIdentifierType.UniqueId));
}
catch (Exception exs)
{
throw;
}
}
catch (AdalException e)
{
// Exception: AdalException
// Represents a library exception generated by ADAL .NET.
// e.ErrorCode contains the error code.
// Action: Case 2, not resolvable with an interactive request.
// Attempt retry after a timed interval or user action.
// Example Error: network_not_available, default case.
throw;
}
return authResult;
}
第二个api:
[Authorize]
[HttpGet]
public ActionResult<IEnumerable<string>> Get()
{
string name = User.Identity.Name;
return new string[] { "value1", "value2" };
}
答案 0 :(得分:0)
您需要在Web API中使用“代表流”(不是必需的交互式令牌获取) 如果要使用ADAL.NET,可以找到一个示例:https://github.com/azure-samples/active-directory-dotnet-webapi-onbehalfof
但是我现在建议您使用MSAL.NET。示例为:active-directory-dotnet-native-aspnetcore-v2/2. Web API now calls Microsoft Graph,文档为:https://aka.ms/msal-net-on-behalf-of
还要注意,对于Web API,我们不使用OIDC(这是用于登录用户),而是使用JWT承载中间件