C# 如何获取与外部api集成测试的访问令牌

C# 如何获取与外部api集成测试的访问令牌,c#,azure,asp.net-core,azure-active-directory,adal,C#,Azure,Asp.net Core,Azure Active Directory,Adal,对于集成测试,我有一个授权的.NET Core 2.2控制器,它正在调用另一个授权控制器(不同的项目)或外部api(如Microsoft Graph) 这两个API都是针对Azure AD进行身份验证的。在所有控制器操作中,我们都需要经过身份验证的用户。 我们可以通过基于用户名和密码(grant_type=password)获得一个令牌来进入第一个api。当调用继续到第二个api时,由于交互式登录提示(我们使用ADAL),调用中断 通常,用户使用openid connect进行身份验证,然后我们

对于集成测试,我有一个授权的.NET Core 2.2控制器,它正在调用另一个授权控制器(不同的项目)或外部api(如Microsoft Graph)

这两个API都是针对Azure AD进行身份验证的。在所有控制器操作中,我们都需要经过身份验证的用户。 我们可以通过基于用户名和密码(grant_type=password)获得一个令牌来进入第一个api。当调用继续到第二个api时,由于交互式登录提示(我们使用ADAL),调用中断

通常,用户使用openid 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;
}
公共静态异步任务AcquireTokenAsync(字符串用户名、字符串密码)
{
var aadInstance=”https://login.windows.net/{0}";
var tenantId=“将id放在此处”;
var authority=string.Format(aadInstance,tenantId);
var clientId=“clientId here”;
var resource=“将资源放在此处”;
var client=新的HttpClient();
var-tokenpoint=$”https://login.microsoftonline.com/{tenantId}/oauth2/token”;
var body=$“resource={resource}&client_id={clientId}&grant_type=password&username={username}&password={password}”;
var stringContent=新的stringContent(body,Encoding.UTF8,“application/x-www-form-urlencoded”);
var result=wait client.PostAsync(令牌端点,stringContent).ContinueWith((响应)=>
{
返回response.Result.Content.ReadAsStringAsync().Result;
});
JObject=JObject.Parse(结果);
var token=jobject[“access_token”].Value();
返回令牌;
}
第一个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();
}
[授权]
[HttpGet]
公共异步任务Get()
{
字符串名称=User.Identity.name;
var result=await AcquireTokenSilentWithImpersonationAsync();
字符串BaseUrl=”https://localhost:44356/";
var client=新的HttpClient
{
BaseAddress=新Uri(BaseUrl)
};
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(新的MediaTypeWithQualityHeaderValue(“应用程序/json”);
client.DefaultRequestHeaders.Authorization=新的AuthenticationHeaderValue(“Bearer”,result.AccessToken);
var url=“api/values”;
HttpResponseMessage response=wait client.GetAsync(url);
开关(响应状态代码)
{
案例HttpStatusCode.OK:
int x=1;
打破
违约:
抛出新的HttpRequestException($“Error-{response.StatusCode}以响应消息“{response.RequestMessage}”);
}
返回Ok();
}
private const string BackendResource=“此处的第二个api资源”;
/// 
///有关更多信息:https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-devhowto-adal-error-handling
/// 
/// 
公共异步任务AcquireTokenSilentWithImpersonationAsync()
{
const string ClientId=“此处第一个api的客户端id”;
const string ClientSecret=“此处第一个api的秘密”;
ClientCredential=新的ClientCredential(ClientId,ClientSecret);
字符串userObjectId=\u httpContextAccessor.HttpContext.User.FindFirst(“http://schemas.microsoft.com/identity/claims/objectidentifier)价值;
var authContext=GetAuthenticationContext(userObjectId);
AuthenticationResult authResult=null;
尝试
{
authResult=等待authContext.AcquireTokenSilentAsync(后端资源、凭证、新用户标识符(userObjectId、UserIdentifierType.UniqueId));
}
捕获(AdalSilentTokenAcquisitionException ex)
{
//例外:AdalSilentTokenAcquisitionException
//当缓存中没有令牌或所需刷新失败时导致。
//措施:案例1,可通过交互式请求解决。
尝试
{
authResult=等待authContext.AcquireTokenAsync(后端资源,客户端ID,新Uri(“https://backurl.org)、新平台参数()、新用户标识符(userObjectId、UserIdentifierType.UniqueId));
}
捕获(异常exs)
{
投掷;
}
}
接住(二语)
{
//例外:词汇感知
//表示由ADAL.NET生成的库异常。
//e.ErrorCode包含错误代码。
//措施:案例2,无法通过交互式请求解决。
//尝试在时间间隔或用户操作后重试。
//示例错误:网络不可用,默认情况。
投掷;
}
返回结果;
}
第二个api:

[Authorize]
[HttpGet]
public ActionResult<IEnumerable<string>> Get()
{
    string name = User.Identity.Name;

    return new string[] { "value1", "value2" };
}
[授权]
[HttpGet]
公共行动结果获取()
{
字符串名称=User.Identity.name;
返回新字符串[]{“value1”,“value2”};
}

您需要在Web API中使用代表流(而不是交互式令牌获取,需要) 如果您想使用ADAL.NET,这里有一个示例:

但我现在建议您使用MSAL.NET。样本为:,文档为:


还要注意的是,对于Web api,我们不使用OIDC(这是为了登录用户),而是使用JWT承载中间件

来访问本地应用程序中的第一个api应用程序,所以第一个api应用程序的缓存中没有用户/令牌/刷新令牌。所以AcquireTokenSilentAsync在您的场景中不起作用。我知道这不起作用。这就是我们提出这个问题的原因;-)。通常我们不使用本机但开放id连接。但这是交互式的。我们使用OIDC,但是对于集成测试来说,交互式是不可能的,所以我们正在检查一些替代方案。没有OIDC(集成测试),我们就无法进行海外建筑运营管理局呼叫。我们需要用户的上下文。问微软这个问题
[Authorize]
[HttpGet]
public ActionResult<IEnumerable<string>> Get()
{
    string name = User.Identity.Name;

    return new string[] { "value1", "value2" };
}