Угловое приложение, которое вызывает защищенное B2C API приложения-функции, получает 500, Функция получает 404 - PullRequest
1 голос
/ 02 июня 2019

Угловой сайт, размещенный на учетной записи хранения Azure как статический веб-сайт получает 500 при запуске Azure B2C-защищенного функционального приложения функция.Функция получает 404.


Обновление

Первоначальный заголовок для этого вопроса был «Приложение Angular, которое вызывает защищенное B2C приложение функций, получает ответ 401 Unauthorized».Решением было, как предложил @Alex AIT (ниже), заменить https://<b2c_tenant_name>.b2clogin.com/<b2c_tenant_name>.onmicrosoft.com/v2.0/.well-known/openid-configuration?p=<SignUpAndSignInPolicyName> в URL-адресе эмитента приложения функций на https://<b2c_tenant_name>.b2clogin.com/<b2c_tenant_name>.onmicrosoft.com/v2.0/.Т.е. удалить конечные .well-known/openid-configuration?p=<SignUpAndSignInPolicyName> сегменты.В последующем сеансе чата Алекс указал, что политика является частью пути, такого как https://<tenantname>.b2clogin.com/<tenantname>.onmicrosoft.c‌​om/<policyname>/v2.0 или https://<tenantname>.b2clogin.com/<tenantguid>/<policyname>/v2.0.Однако любой из этих путей для URL-адреса издателя приложения-функции возвращается к ответу 401.

После решения проблемы 401 приложение Angular SPA теперь получает 500. Однако вызываемая функция APIполучение 404. Поток журнала приложения функций показывает Failed to download OpenID configuration from 'https://<b2c_tenant_name>.b2clogin.com/<b2c_tenant_name>.onmicrosoft.com/v2.0/.well-known/openid-configuration': The remote server returned an error: (404) Not Found., поэтому политика не подключается.


Моя цель - создать безопасное безсерверное веб-приложение Angular, которое статически размещается в Azure.Веб-сайт хранения (т. Е. В контейнере $web учетной записи хранения).Существует два проекта: публичный SPA Angular 7 + проект и защищенный API Function App проект.Поскольку статические веб-сайты учетной записи хранения Azure разрешают только публичный анонимный доступ ко всем файлам, файлы контейнера больших двоичных объектов Angular app-hosting (файлы веб-сайта) не защищены.Но вызовы приложения Azure Functions API Angular защищены.Проект Function App API защищен с помощью аутентификации Azure AD B2C.

С этой целью я попытался адаптировать метод, описанный в Одностраничное приложение, построенное на MSAL..js с Azure AD B2C и Node.js Web API с Azure AD B2C .Я смог заставить эти образцы работать.Более того, мне удалось изменить их настройки для проверки подлинности на моем собственном клиенте Azure B2C (а не на клиенте Microsoft B2C) и запустить их локально.Но я не пытался развернуть эти образцы проектов в Azure и выяснить, какие настройки нужны.Я пропустил упражнение по развертыванию, потому что я не разработчик Node.js.

Но моя последующая адаптация кода в этих (Node.js) примерах проектов для моего статически размещенного проекта Angular SPA и моего AzureПроект API функций дает 401 Unauthorized всякий раз, когда API вызывается из SPA.Поэтому я хотел бы понять, как решить эту проблему.

Настройка

Допущения / Предпосылки

  1. Azure B2C Арендатор былсоздано
  2. Поставщики удостоверений настроены для B2C Арендатора
  3. Настроена политика Sign-up and Sign-in потока пользователя для арендатора B2C
    • Запишите его имя.Мы будем называть его имя ниже <SignUpAndSignInPolicyName>
  4. Создана учетная запись хранилища Azure с включенной функцией Статический веб-сайт
  5. Создано приложение Angular

    1. Установлен пакет @azure/msal-angular
    2. In app-routing.module.ts,

      • была установлена ​​опция useHash: imports: [RouterModule.forRoot(routes, { useHash: true })],
        • Хеш-маршрутизация необходима для размещения статического хостинга
      • Был создан защищенный компонент иустановлен защищенный маршрут

             const routes: Routes = [
                 { path: 'secure', component: SecureComponent, canActivate: [MsalGuard] },
                 { path: 'state', redirectTo: 'secure' }, // HACK/TODO
                 { path: 'error', redirectTo: 'secure' }, // HACK/TODO
                 { path: '', redirectTo: '', pathMatch: 'full' },
             ];
      
  6. Создано функциональное приложение Azure

    • Запишите функциональное приложение URL-адрес
  7. Следующая функция была создана в функции Приложение для тестирования.И оно было опубликовано в Azure:

    using System;
    using System.IO;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.Azure.WebJobs;
    using Microsoft.Azure.WebJobs.Extensions.Http;
    using Microsoft.AspNetCore.Http;
    using Microsoft.Extensions.Logging;
    using Newtonsoft.Json;
    
    namespace SomeCompany.Functions
    {
        public static class HttpTriggerCSharp
        {
            [FunctionName("HttpTriggerCSharp")]
            public static async Task<IActionResult> Run(
                [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
                ILogger log)
            {
                log.LogInformation("C# HTTP trigger function processed a request.");
    
                string name = req.Query["name"];
    
                string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
                dynamic data = JsonConvert.DeserializeObject(requestBody);
                name = name ?? data?.name;
    
                return name != null
                    ? (ActionResult)new OkObjectResult($"Hello, {name}")
                    : new BadRequestObjectResult("Please pass a name on the query string or in the request body");
            }
        }
    }
    

B2C Tenant

Приложение API
  1. Создайте приложение API (т.е. назовите его « API »)
  2. Запишите идентификатор приложения
    • Идентификатор приложения будет использоваться позже в настройках AAD Auth приложения функции
  3. Установить Включить веб-приложение / веб-API до Да
  4. Установите Разрешить неявный поток на Да
  5. Установите Ответный URL на https://<functionappname>.azurewebsites.net/.auth/login/aad/callback
    • Суффикс URL-адреса приложения функции с/.auth/login/aad/callback
  6. Установите для URI идентификатора приложения сегмент «API»
    • Выход: https://<b2c_tenant_name>.onmicrosoft.com/API
Приложение SPA
  1. Создание приложения SPA (т. Е. Имя « SPA »)
  2. Установить Включить веб-приложение / веб-API в Да
  3. Установить Разрешить неявный поток до Да
  4. Установите Ответный URL на http://localhost:4200
  5. На вкладке API access добавьте APIAPI приложения
    • Единственная доступная область действия «Доступ к этому приложению от имени вошедшего в систему пользователя (user_impersonation)» будет предварительно выбран

Основной (не B2C) арендатор

Функция приложения
  1. В Аутентификация / Авторизация blade-сервер,
    • Set Аутентификация службы приложений в Вкл.
    • Установить Действие, которое необходимо выполнить, если запрос не аутентифицирован - Войдите в систему с Azure Active Directory
    • В разделе Поставщики проверки подлинности настройте поставщика Azure Active Directory следующим образом:
      • Установить Режим управления до Дополнительно
      • Установите Идентификатор клиента для приложения приложения B2C API * Идентификатор приложения
      • Установить URL эмитента до https://<b2c_tenant_name>.b2clogin.com/<b2c_tenant_name>.onmicrosoft.com/v2.0/.well-known/openid-configuration?p=<SignUpAndSignInPolicyName>
  2. Сохранить эти Аутентификация / Авторизация настройки
Приложение Azure
  1. В свойстве Angular приложения app.module.ts NgModule import , установите:

    MsalModule.forRoot({
        clientID: '<B2C Tenant |> SPA Application |> Application ID>',
    
        // Note, for authority, the following doesn't work:
        //    B2C Tenant |> User flows (policies) |> <SignUpAndSignInPolicyName> |> Run user flow |> URL at top of the `Run user flow` blade
        //    I.e., `https://<b2c_tenant_name>.b2clogin.com/<b2c_tenant_name>.onmicrosoft.com/v2.0/.well-known/openid-configuration?p=<SignUpAndSignInPolicyName>`
        // Supposedly (according to various blog posts), that URL should be used as the `authority`. So, why doesn't it work?.
        // The following URL works. However, the B2C portal indicates that `login.microsoftonline.com` is to be deprecated soon
        authority: 'https://login.microsoftonline.com/tfp/<b2c_tenant_name>.onmicrosoft.com/<SignUpAndSignInPolicyName>',
    
        // B2C Tenant |> Applications |> API |> Published Scopes |> `user_impersonation` | FULL SCOPE VALUE
        consentScopes: ['https://<b2c_tenant_name>.onmicrosoft.com/API/user_impersonation'],
    })
    
  2. Создать компонент с именем Secure

    • ng g c Secure -s --skipTests

    • secure.component.ts

      import { Component } from '@angular/core';
      import { HttpClient, HttpHeaders } from '@angular/common/http';
      import { Subscription } from 'rxjs';
      import { MsalService } from '@azure/msal-angular';
      
      @Component({
          selector: 'app-secure',
          templateUrl: './secure.component.html',
      })
      export class SecureComponent  {
      
          constructor(private http: HttpClient, private msalService: MsalService) { }
      
          azureTestFunctionResponse: string;
      
          callApiWithAccessToken(accessToken: string) {
              const url = 'https://<function_app_name>.azurewebsites.net/api/HttpTriggerCSharp?name=HelloFromAzureFunction';
              const httpHeaders = new HttpHeaders({ Authorization: `Bearer ${accessToken}` });
              const subscription: Subscription = this.http.get(url, { headers: httpHeaders , responseType: 'text'}).subscribe(_ => {
                  this.azureTestFunctionResponse = _;
                  subscription.unsubscribe();
              });
          }
      
          invokeB2cSecuredAzureFunction() {
              // B2C Tenant |> `API` Application |> Published Scopes |> `user_impersonation` scope |> Full Scope Value
              const tokenRequest: string[] = ['https://<b2c_tenant_name>.onmicrosoft.com/API/user_impersonation'];
              this.msalService.acquireTokenSilent(tokenRequest)
                  .then(tokenResponse => {
                      this.callApiWithAccessToken(tokenResponse);
                  })
                  .catch(error1 => {
                      this.msalService.acquireTokenPopup(tokenRequest)
                          .then(tokenResponse => {
                              this.callApiWithAccessToken(tokenResponse);
                          })
                          .catch(error => {
                              console.log('Error acquiring the access token to call the Web api:\n' + error);
                          });
                  });
          }
      
      }
      
    • secure.component.html

      <h4>Secure Component</h4>
      
      <button (click)="invokeB2cSecuredAzureFunction()">Fetch data from B2C-secured Azure functions</button>
      <hr />
      <div>{{azureTestFunctionResponse}}</div>
      
  3. app.component.html

    <div style="text-align:center">
        <h4> {{ title }} </h4>
    </div>
    <mat-card style="float: left;">
        This site is a configuration demonstration of a secure, serverless Angular web application. The site is statically hosted on an
        <em>Azure Storage</em> website (<code>$web</code> container). The site's backend is secured
        by Azure <em>Business-to-Consumer</em>&nbsp;<span class="acronym">(B2C)</span> authentication. The site interacts with a secure
        <em>Azure Functions</em>&nbsp;<span class="acronym">API</span>.
    </mat-card>
    
    <p style="text-align: center;"><a routerLink="/" routerLinkActive="active">Home</a>&nbsp;&nbsp;<a routerLink="/secure" routerLinkActive="active">Secure</a></p>
    
    <p style="text-align: center;"><router-outlet></router-outlet></p>
    
  4. Служите приложению локально: ng serve

  5. Нажмите на ссылку Secure
    • , которая ведет к /secure route
    • Что побуждает пользователя аутентифицироваться
  6. Нажмите кнопку Fetch data from B2C-secured Azure function
  7. Сервер возвращает 401 Not Authorized ответ
  8. Если Reply URL приложения SPA обновлено до статического URL SPA веб-сайта и опубликованы файлы SPA, 401 также получает ответВызывается, когда вызывается функция API.

Так что я не уверен, что настроено неправильно.Есть идеи?

Ответы [ 3 ]

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

У меня была такая же проблема, как вы описали, хотя опубликованные ответы я смог решить, изменив полномочия на:

https://<b2c_tenant_name>.b2clogin.com/tfp/<b2c_tenant_name>.onmicrosoft.com/<SignUpAndSignInPolicyName>

стандартный (https://<b2c_tenant_name>.microsoftonline.com/tfp/<b2c_tenant_name>.onmicrosoft.com/<SignUpAndSignInPolicyName>) заставлял меня получать 401, когда я пытался использовать токен в своем приложении Function

Редактировать: добавление образца кода

Пока мойкод использует переменные реагирующей среды, все это просто JS и должно работать одинаково в угловом приложении.

import * as Msal from 'msal';

/** @type {import('msal').Configuration} */
const msalConfig = {
    auth: {
        clientId: process.env.REACT_APP_CLIENT_ID,
        authority: 'https://<b2c_tenant_name>.b2clogin.com/tfp/<b2c_tenant_name>.onmicrosoft.com/<SignUpAndSignInPolicyName>',
        validateAuthority: false,
        navigateToLoginRequestUrl: false,
    },
    cache: {
        cacheLocation: 'localStorage',
        storeAuthStateInCookie: true,
    },
};
/** @type {import('msal').AuthenticationParameters} */
const reqParams = {
    scopes: [process.env.REACT_APP_SCOPE],
};
const clientApplication = new Msal.UserAgentApplication(msalConfig);
clientApplication.handleRedirectCallback((error, response) => {
    if (error) {
        if (error.message.indexOf('AADB2C90118') >= 0) {
            //User clicked forgot password
            clientApplication.authority = 'https://<b2c_tenant_name>.b2clogin.com/tfp/<b2c_tenant_name>.onmicrosoft.com/<ResetPasswordPolicyName>';
            clientApplication.loginRedirect(reqParams);
            return;
        }
        return console.error(error);
    }
});
0 голосов
/ 18 июня 2019

Решением для меня было изменить защиту для функции azure в стандартных настройках на Anonymous (из Function) ... Кажется, он ожидал также код функции в дополнение к токену на предъявителя ... Взял меня 5+ часы, чтобы выяснить, так как все мое внимание было сосредоточено на том, что может быть не так с токеном доступа JWT или конфигами AADB2C и т. д. *

401 на самом деле ...

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

Это не эмитент вашего арендатора:

https://<b2c_tenant_name>.b2clogin.com/<b2c_tenant_name>.onmicrosoft.com/v2.0/.well-known/openid-configuration?p=<SignUpAndSignInPolicyName>

Но если вы откроете этот URL-адрес в своем браузере, он покажет ваш эмитент, которого вы ищете.

Это должно быть примерно так:

https://<b2c_tenant_name>.b2clogin.com/<b2c_tenant_name>.onmicrosoft.com/v2.0
https://<b2c_tenant_name>.b2clogin.com/<b2c_tenant_guid>.onmicrosoft.com/v2.0
https://<b2c_tenant_name>.b2clogin.com/<b2c_tenant_name>.onmicrosoft.com/SignUpAndSignInPolicyName/v2.0
https://login.microsoftonline.com/<b2c_tenant_name>.onmicrosoft.com/v2.0

Также было бы неплохо выбрать либо b2clogin.com, либо login.microsoftonline.com как для функции Azure, так и для приложения Angular. Я не думаю, что вы можете смешать их так.

Если у вас все еще есть проблемы, вы можете попробовать это как область вместо /user_impersonation:

https://<b2c_tenant_name>.onmicrosoft.com/API/.default

Или попробуйте добавить https://<b2c_tenant_name>.onmicrosoft.com/API/user_impersonation к разрешенным аудиториям в функции Azure.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...