如何获取访问令牌以与外部API进行集成测试

时间:2019-02-26 09:56:56

标签: 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)而中断。

通常,用户使用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" };
}

1 个答案:

答案 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承载中间件