Консольное приложение .Net, вызывающее веб-API, вызывающее другой веб-API - PullRequest
5 голосов
/ 30 мая 2019

Мне сложно понять, как повторно использовать маркер безопасности пользователя, чтобы аутентифицировать его, когда его запрос данных проходит через несколько веб-API.

  • Console App - C # / Net Framework 4.7.x консольное приложение.
  • WebAPI 1 - Приложение C # / .Net Core 2.2 MVC WebAPI.
  • WebAPI 2 - Приложение C # / .Net Core 2.2 MVC WebAPI.

В настоящее время все они настроены как автономные приложения в своих собственных решениях Visual Studio 2019, работающих на моем компьютере разработчика, но (когда они будут работать !!) каждый из них будет размещаться в Azure как отдельный объект самостоятельно.

По сути, пользователь проходит проверку подлинности в Консольном приложении, проверяя свои учетные данные из Azure Active Directory. После этого образца на GitHub у меня есть консольное приложение, которое успешно вызывает WebAPI 1 и возвращает данные.

Однако я хочу, чтобы WebAPI 1 звонил WebAPI 2 во время вызова и извлекал другие данные как часть набора данных для Консольного приложения, и это та часть, с которой я застрял.

WebAPI 2 настроен на портале Azure точно так же, как и WebAPI 1, за исключением различных идентификаторов клиентов приложений и т. Д.

Как часть примера (упомянутого выше), я могу WebAPI 1 вызвать Microsoft Graph API перед возвратом данных обратно на вызывающий Console App, так что я не думаю, что я способ от этого. Вот код для вызова Graph API:

    public async Task<string> CallGraphApiOnBehalfOfUser()
    {
        string[] scopes = { "user.read" };

        // we use MSAL.NET to get a token to call the API On Behalf Of the current user
        try
        {
            string accessToken = await _tokenAcquisition.GetAccessTokenOnBehalfOfUser(HttpContext, scopes);
            dynamic me = await CallGraphApiOnBehalfOfUser(accessToken);
            return me.userPrincipalName;
        }
        catch (MsalUiRequiredException ex)
        {
            _tokenAcquisition.ReplyForbiddenWithWwwAuthenticateHeader(HttpContext, scopes, ex);
            return string.Empty;
        }
    } 

    private static async Task<dynamic> CallGraphApiOnBehalfOfUserOriginal(string accessToken)
    {
        //
        // Call the Graph API and retrieve the user's profile.
        //
        HttpClient client = new HttpClient();
        client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
        HttpResponseMessage response = await client.GetAsync("https://graph.microsoft.com/v1.0/me");
        string content = await response.Content.ReadAsStringAsync();
        if (response.StatusCode == HttpStatusCode.OK)
        {
            dynamic me = JsonConvert.DeserializeObject(content);
            return me;
        }

        throw new Exception(content);
    }

Мой план состоял в том, чтобы изменить URL-адрес в приведенном выше коде, чтобы он указывал на адрес WebAPI 2, но во время аутентификации он не удался ЕСЛИ я удаляю атрибут класса [Authorize] на моем контроллере в WebAPI 2, он успешно устанавливает соединение и возвращает ожидаемые данные, но с включенным атрибутом он даже не достигает точки останова на Контроллер, подсказывая мне, что проблема в маркере носителя, который я пытаюсь использовать ИЛИ , что WebAPI 2 не настроен должным образом.

Получение копии токена безопасности и попытка повторно использовать этот промежуточный полет также не работает, так как я предполагаю, что токен предназначен для WebAPI 1 и поэтому недопустим для использования с WebAPI 2.

Должен ли я выполнять такую ​​аутентификацию на стороне? (Кажется грязным, когда пользователь жестко кодирует учетные данные в WebAPI 1, которые могут получить доступ к WebAPI 2, поэтому я не хочу этого делать. Плюс, если учетные данные пользователя требуют изменения, у меня есть перераспределение только для что.)

Есть ли лучший способ сделать то, что я пытаюсь сделать?

Если вам понадобится дополнительная информация, чтобы объяснить, что я сделал, я, безусловно, могу это сделать.

ОБНОВЛЕНИЕ 1: Вот Startup.cs:

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddProtectWebApiWithMicrosoftIdentityPlatformV2(Configuration)
                .AddProtectedApiCallsWebApis(Configuration, new string[] { "user.read", "offline_access" })
                .AddInMemoryTokenCaches();

        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        ...

        app.UseHttpsRedirection();
        app.UseAuthentication();
        app.UseMvc();
    }

ОБНОВЛЕНИЕ 2: Аналогичное сообщение переполнения стека С тех пор я нашел этот пост , который @ philippe-signoret описывает в своем ответе, и это именно то, что мне нужно.

ОБНОВЛЕНИЕ 3: несанкционированный ответ при вызове WebAPI 2

Вот сообщение об ошибке, которое я получаю после звонка:

{StatusCode: 401, ReasonPhrase: 'Unauthorized', Version: 1.1, Content: System.Net.Http.HttpConnection+HttpConnectionResponseContent, Headers:
{
   Server: Kestrel
   WWW-Authenticate: Bearer error="invalid_token", error_description="The signature is invalid"
   X-SourceFiles: =?UTF-8?B?*<random-looking-code>*
   X-Powered-By: ASP.NET
   Date: Fri, 31 May 2019 09:48:31 GMT
   Content-Length: 0
}}

Как я упоминал ранее, если я удалю атрибут [Authorize] из класса моего контроллера, вызов пройдет, как и ожидалось.

1 Ответ

0 голосов
/ 04 июня 2019

Образец, к которому следует обратиться: https://github.com/Azure-Samples/active-directory-dotnet-webapp-webapi-openidconnect-aspnetcore. Этот образец содержит веб-API, работающий на ASP.NET Core 2.0, защищенный Azure AD.Доступ к веб-API осуществляется через веб-приложение ASP.NET Core 2.0 от имени вошедшего в систему пользователя.

Промежуточное приложение в примере - это веб-приложение, а не веб-API, но основной принцип тот же.

Я бы посоветовал вам вначале строго следовать этому примеру, чтобы вы понимали, как вызывать web api 2 из web api 1 от имени пользователей.Обратите внимание на шаги 6, 7 раздела « Регистрация веб-приложения TodoListWebApp »:

6, From the Settings blade, select Required permissions. Select + Add, and then select Select an API. 
Type TodoListService in the textbox and press Enter. Select the web API from the list and then select the Select button. Select Select Permissions. 
Tick the checkbox next to Access TodoListService and then select the Select button. Select the Done button.

7, In the Settings blade, under API Access, select Required permissions. 
Click on the Grant Permissions and when prompted press Yes. 
Once the web app is granted access to the webapi you should see the following message: Successfully granted permissions to the application for your account. 
To grant permissions for all users, please have an admin consent to the application.

Фрагмент кода ядра для вызова веб-API в веб-приложении приведен ниже:

            // Because we signed-in already in the WebApp, the userObjectId is know
            string userObjectID = (User.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier"))?.Value;

            // Using ADAL.Net, get a bearer token to access the TodoListService
            AuthenticationContext authContext = new AuthenticationContext(AzureAdOptions.Settings.Authority, new NaiveSessionCache(userObjectID, HttpContext.Session));
            ClientCredential credential = new ClientCredential(AzureAdOptions.Settings.ClientId, AzureAdOptions.Settings.ClientSecret);
            result = await authContext.AcquireTokenSilentAsync(AzureAdOptions.Settings.TodoListResourceId, credential, new UserIdentifier(userObjectID, UserIdentifierType.UniqueId));

            // Retrieve the user's To Do List.
            HttpClient client = new HttpClient();
            HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, AzureAdOptions.Settings.TodoListBaseAddress + "/api/todolist");
            request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
            HttpResponseMessage response = await client.SendAsync(request);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...