Proteja o backend SPA com AAuth 2.0, Azure Ative Directory B2C e Azure API ManagementProtect SPA backend with OAuth 2.0, Azure Active Directory B2C and Azure API Management

Este cenário mostra-lhe como configurar o seu exemplo de Gestão API Azure para proteger uma API.This scenario shows you how to configure your Azure API Management instance to protect an API. Utilizaremos o fluxo Azure AD B2C SPA (Auth Code + PKCE) para adquirir um token, juntamente com a API Management para garantir um backend de funções Azure usando a EasyAuth.We'll use the Azure AD B2C SPA (Auth Code + PKCE) flow to acquire a token, alongside API Management to secure an Azure Functions backend using EasyAuth.

ObjetivosAims

Vamos ver como a API Management pode ser usada num cenário simplificado com As Funções Azure e Azure AD B2C.We're going to see how API Management can be used in a simplified scenario with Azure Functions and Azure AD B2C. Irá criar uma aplicação JavaScript (JS) que chama a API, que assina nos utilizadores com Azure AD B2C.You'll create a JavaScript (JS) app calling an API, that signs in users with Azure AD B2C. Em seguida, utilizará as funcionalidades de política validada da API Management, CORS e Rate Limit By Key para proteger a API de backend.Then you'll use API Management's validate-jwt, CORS, and Rate Limit By Key policy features to protect the Backend API.

Para a defesa em profundidade, utilizamos a EasyAuth para validar novamente o token dentro da API de back-end e garantir que a gestão da API é o único serviço que pode chamar o backend das Funções Azure.For defense in depth, we then use EasyAuth to validate the token again inside the back-end API and ensure that API management is the only service that can call the Azure Functions backend.

O que vai aprender?What will you learn

  • Configuração de uma App de página única e API de backend em Azure Ative Directory B2CSetup of a Single Page App and backend API in Azure Active Directory B2C
  • Criação de uma API de Funções AzureCreation of an Azure Functions Backend API
  • Importação de uma API de Funções Azure para a Gestão da API AzureImport of an Azure Functions API into Azure API Management
  • Assegurar a API na Gestão API da AzureSecuring the API in Azure API Management
  • Chamando o Azure Ative Directory B2C Autorizações Pontos finais através das Bibliotecas da Plataforma de Identidade da Microsoft (MSAL.js)Calling the Azure Active Directory B2C Authorization Endpoints via the Microsoft Identity Platform Libraries (MSAL.js)
  • Armazenar uma aplicação de página única HTML / Vanilla JS e servi-la a partir de um ponto final de armazenamento de blob Azure BlobStoring a HTML / Vanilla JS Single Page Application and serving it from an Azure Blob Storage Endpoint

Pré-requisitosPrerequisites

Para seguir os passos deste artigo, você deve ter:To follow the steps in this article, you must have:

  • Uma Conta de Armazenamento V2 de Propósito Geral Azure (StorageV2) para acolher a app de página única JS.An Azure (StorageV2) General Purpose V2 Storage Account to host the frontend JS Single Page App.
  • Uma instância de Gestão da API Azure (Qualquer nível funcionará, incluindo 'Consumo', no entanto, certas características aplicáveis ao cenário completo não estão disponíveis neste nível (taxa-limite-por-chave e IP virtual dedicado), estas restrições são chamadas abaixo no artigo, se for caso disso).An Azure API Management instance (Any tier will work, including 'Consumption', however certain features applicable to the full scenario are not available in this tier (rate-limit-by-key and dedicated Virtual IP), these restrictions are called out below in the article where appropriate).
  • Uma aplicação vazia Azure Function (executando o tempo de execução V3.1 .NET Core, em um Plano de Consumo) para acolher a chamada APIAn empty Azure Function app (running the V3.1 .NET Core runtime, on a Consumption Plan) to host the called API
  • Um inquilino Azure AD B2C, ligado a uma subscrição.An Azure AD B2C tenant, linked to a subscription.

Embora, na prática, se utilize recursos na mesma região em cargas de trabalho de produção, para este artigo como-artigo a região de implantação não é importante.Although in practice you would use resources in the same region in production workloads, for this how-to article the region of deployment isn't important.

Descrição GeralOverview

Aqui está uma ilustração dos componentes em uso e o fluxo entre eles uma vez que este processo esteja concluído.Here's an illustration of the components in use and the flow between them once this process is complete. Componentes em uso e fluxoComponents in use and flow

Aqui está uma visão geral rápida dos passos:Here's a quick overview of the steps:

  1. Criar as aplicações Azure AD B2C (Frontend, API Management) e API com âmbitos e conceder acesso à APICreate the Azure AD B2C Calling (Frontend, API Management) and API Applications with scopes and grant API Access

  2. Crie a inscrição e inscreva-se em políticas que permitam aos utilizadores iniciar sessão com Azure AD B2CCreate the sign up and sign in policies to allow users to sign in with Azure AD B2C

  3. Configure a API Management com os novos IDs e chaves do cliente Azure AD B2C e chaves para ativar a autorização do utilizador OAuth2 na Consola de DesenvolvimentoConfigure API Management with the new Azure AD B2C Client IDs and keys to Enable OAuth2 user authorization in the Developer Console

  4. Construir a Função APIBuild the Function API

  5. Configure a Função API para permitir a EasyAuth com o novo Azure AD B2C Client ID's e Chaves e bloquear para APIM VIPConfigure the Function API to enable EasyAuth with the new Azure AD B2C Client ID’s and Keys and lock down to APIM VIP

  6. Construir a Definição de API na Gestão da APIBuild the API Definition in API Management

  7. Configurar o Oauth2 para a configuração da API Management APISet up Oauth2 for the API Management API configuration

  8. Crie a política CORS e adicione a política validada-jwt para validar o token OAuth para cada pedido de entradaSet up the CORS policy and add the validate-jwt policy to validate the OAuth token for every incoming request

  9. Construir a aplicação de chamada para consumir a APIBuild the calling application to consume the API

  10. Faça o upload da amostra JS SPAUpload the JS SPA Sample

  11. Configure a App do Cliente JS sample com o novo ID do cliente Azure AD B2C E chavesConfigure the Sample JS Client App with the new Azure AD B2C Client ID’s and keys

  12. Testar a Aplicação do ClienteTest the Client Application

    Dica

    Vamos capturar algumas peças de informação e chaves, etc. enquanto caminhamos neste documento, pode achar útil ter um editor de texto aberto para armazenar temporariamente os seguintes itens de configuração.We're going to capture quite a few pieces of information and keys etc as we walk this document, you might find it handy to have a text editor open to store the following items of configuration temporarily.

    ID DO CLIENTE DE BACKEND B2C:B2C BACKEND CLIENT ID:
    CHAVE SECRETA DO CLIENTE B2C BACKEND:B2C BACKEND CLIENT SECRET KEY:
    B2C BACKEND API SCOPE URI:B2C BACKEND API SCOPE URI:
    ID DO CLIENTE DIANTEIRO B2C:B2C FRONTEND CLIENT ID:
    B2C USER FLOW ENDPOINT URI:B2C USER FLOW ENDPOINT URI:
    B2C BEM CONHECIDO PONTO FINAL OPENID:B2C WELL-KNOWN OPENID ENDPOINT:
    B2C NOME DA POLÍTICA: FRONTENDAPP_SIGNUPANDSIGNIN URL DE FUNÇÃO:B2C POLICY NAME: Frontendapp_signupandsignin FUNCTION URL:
    URL BASE APIM API: URL DE PONTO FINAL PRIMÁRIO DE ARMAZENAMENTO:APIM API BASE URL: STORAGE PRIMARY ENDPOINT URL:

Configure a aplicação backendConfigure the backend application

Abra a lâmina Azure AD B2C no portal e faça os seguintes passos.Open the Azure AD B2C blade in the portal and do the following steps.

  1. Selecione o separador Registos de AplicaçõesSelect the App Registrations tab

  2. Clique no botão 'Novo Registo'.Click the 'New Registration' button.

  3. Escolha 'Web' na caixa de seleção URI de redirecionamento.Choose 'Web' from the Redirect URI selection box.

  4. Agora desa um nome de exibição, escolha algo único e relevante para o serviço que está a ser criado.Now set the Display Name, choose something unique and relevant to the service being created. Neste exemplo, usaremos o nome "Aplicação de Backend".In this example, we will use the name "Backend Application".

  5. Use espaços reservados para os urls de resposta, como https://jwt.ms ' (um site de descoding de token da Microsoft), atualizaremos esses urls mais tarde.Use placeholders for the reply urls, like 'https://jwt.ms' (A Microsoft owned token decoding site), we’ll update those urls later.

  6. Certifique-se de que selecionou a opção "Contas em qualquer fornecedor de identidade ou diretório organizacional (para autenticar utilizadores com fluxos de utilizador)"Ensure you have selected the "Accounts in any identity provider or organizational directory (for authenticating users with user flows)" option

  7. Para esta amostra, desmarque a caixa de "Consentimento administrativo", já que não exigiremos offline_access permissões hoje.For this sample, uncheck the "Grant admin consent" box, as we won't require offline_access permissions today.

  8. Clique em “Registar”.Click 'Register'.

  9. Grave o ID do cliente de aplicação de backend para utilização posterior (apresentado em 'Aplicação (cliente) ID').Record the Backend Application Client ID for later use (shown under 'Application (client) ID').

  10. Selecione o separador Certificados e Segredos (em Gestão) e, em seguida, clique em 'Novo Segredo de Cliente' para gerar uma tecla de auth (Aceite as definições predefinidos e clique em 'Adicionar').Select the Certificates and Secrets tab (under Manage) then click 'New Client Secret' to generate an auth key (Accept the default settings and click 'Add').

  11. Ao clicar em 'Add', copie a chave (em 'valor') em algum lugar seguro para posterior utilização como o 'segredo do cliente backend' - note que este diálogo é a única hipótese que terá de copiar esta chave.Upon clicking 'Add', copy the key (under 'value') somewhere safe for later use as the 'Backend client secret' - note that this dialog is the ONLY chance you'll have to copy this key.

  12. Selecione agora o separador Expor um Separador API (Under Manage).Now select the Expose an API Tab (Under Manage).

  13. Será solicitado que estabeleça o AppID URI, selecione e grave o valor predefinido.You will be prompted to set the AppID URI, select and record the default value.

  14. Crie e nomeie o âmbito "Olá" para a sua Função API, pode utilizar a frase "Olá" para todas as opções insutilizáveis, registando o valor de alcance completo povoado URI e, em seguida, clique em 'Add Scope'.Create and name the scope "Hello" for your Function API, you can use the phrase 'Hello' for all of the enterable options, recording the populated Full Scope Value URI, then click 'Add Scope'.

  15. Volte à raiz da lâmina Azure AD B2C selecionando a migalha de pão 'Azure AD B2C' na parte superior esquerda do portal.Return to the root of the Azure AD B2C blade by selecting the 'Azure AD B2C' breadcrumb at the top left of the portal.

    Nota

    Os âmbitos AZure AD B2C são efetivamente permissões dentro da sua API a que outras aplicações podem solicitar acesso através da lâmina de acesso API a partir das suas aplicações, efetivamente acaba de criar permissões de aplicação para a sua chamada API.Azure AD B2C scopes are effectively permissions within your API that other applications can request access to via the API access blade from their applications, effectively you just created application permissions for your called API.

Configurar a aplicação frontendConfigure the frontend application

  1. Selecione o separador Registos de AplicaçõesSelect the App Registrations tab
  2. Clique no botão 'Novo Registo'.Click the 'New Registration' button.
  3. Escolha 'Aplicação de página única (SPA)' na caixa de seleção URI de redirecionamento.Choose 'Single Page Application (SPA)' from the Redirect URI selection box.
  4. Agora desajei o Nome de Exibição e AppID URI, escolha algo único e relevante para a aplicação Frontend que utilizará este registo de aplicações AAD B2C.Now set the Display Name and AppID URI, choose something unique and relevant to the Frontend application that will use this AAD B2C app registration. Neste exemplo, pode utilizar a "Aplicação frontend"In this example, you can use "Frontend Application"
  5. De acordo com o registo da primeira aplicação, deixe a seleção de tipos de conta suportada por defeito (autenticando utilizadores com fluxos de utilizador)As per the first app registration, leave the supported account types selection to default (authenticating users with user flows)
  6. Use espaços reservados para os urls de resposta, como https://jwt.ms ' (um site de descoding de token da Microsoft), atualizaremos esses urls mais tarde.Use placeholders for the reply urls, like 'https://jwt.ms' (A Microsoft owned token decoding site), we’ll update those urls later.
  7. Deixe a caixa de consentimento de administração de concessão assinaladaLeave the grant admin consent box ticked
  8. Clique em “Registar”.Click 'Register'.
  9. Grave o ID do cliente da aplicação frontal para utilização posterior (apresentado em 'ID de aplicação (cliente)").Record the Frontend Application Client ID for later use (shown under 'Application (client) ID').
  10. Mude para o separador permissões da API.Switch to the API Permissions tab.
  11. Conceder acesso à aplicação backend clicando em 'Adicionar uma permissão', em seguida'As minhas APIs', selecione a 'Aplicação de backend', selecione 'Permissões', selecione o âmbito criado na secção anterior e clique em 'Adicionar permissões'Grant access to the backend application by clicking 'Add a permission', then 'My APIs', select the 'Backend Application', select 'Permissions', select the scope you created in the previous section, and click 'Add permissions'
  12. Clique em 'Grant admin consent for {tenant} e clique em 'Sim' a partir do diálogo popup.Click 'Grant admin consent for {tenant} and click 'Yes' from the popup dialog. Este popup consente a "Aplicação Frontend" para usar a permissão "olá" definida na "Aplicação backend" criada anteriormente.This popup consents the "Frontend Application" to use the permission "hello" defined in the "Backend Application" created earlier.
  13. Todas as permissões devem agora mostrar para a app como um tique verde sob a coluna de estadoAll Permissions should now show for the app as a green tick under the status column

Criar um fluxo de utilizador "Iniciar sessão e iniciar sessão"Create a "Sign up and Sign in" user flow

  1. Volte à raiz da lâmina B2C selecionando a migalha de pão Azure AD B2C.Return to the root of the B2C blade by selecting the Azure AD B2C breadcrumb.

  2. Mude para o separador 'Fluxos de Utilizador' (Em Políticas).Switch to the 'User Flows' (Under Policies) tab.

  3. Clique em "Novo fluxo de utilizador"Click "New user flow"

  4. Escolha o tipo de fluxo de utilizador 'Iniciar e iniciar sessão' e selecione 'Recomendado' e, em seguida, 'Criar'Choose the 'Sign up and sign in' user flow type, and select 'Recommended' and then 'Create'

  5. Dê um nome à apólice e grave-o para mais tarde.Give the policy a name and record it for later. Para este exemplo, pode utilizar o "Frontendapp_signupandsignin", note que este será prefixado com "B2C_1_" para fazer "B2C_1_Frontendapp_signupandsignin"For this example, you can use "Frontendapp_signupandsignin", note that this will be prefixed with "B2C_1_" to make "B2C_1_Frontendapp_signupandsignin"

  6. Em 'Fornecedores de identidade' e "Contas locais", consulte 'Email Sign up' (ou 'User ID sign up' dependendo da config do seu inquilino B2C) e clique em OK.Under 'Identity providers' and "Local accounts", check 'Email sign up' (or 'User ID sign up' depending on the config of your B2C tenant) and click OK. Esta configuração deve-se ao registo de contas B2C locais, não adiando para outro fornecedor de identidade (como um fornecedor de identidade social) para usar a conta de um utilizador nas redes sociais.This configuration is because we'll be registering local B2C accounts, not deferring to another identity provider (like a social identity provider) to use an user's existing social media account.

  7. Deixe o MFA e as definições de acesso condicional ao seu padrão.Leave the MFA and conditional access settings at their defaults.

  8. Em 'Atributos e reclamações do utilizador', clique em 'Mostrar Mais...' em seguida, escolha as opções de reclamação que deseja que os seus utilizadores entrem e tenham devolvido no token.Under 'User Attributes and claims', click 'Show More...' then choose the claim options that you want your users to enter and have returned in the token. Verifique pelo menos 'Display Name' e 'Email Address' para recolher, com 'Display Name' e 'Email Addresss' para devolver (preste atenção ao facto de estar a recolher emailaddress, singular e pedir para devolver endereços de e-mail, múltiplos) e clicar em 'OK', e clicar em 'Criar'.Check at least 'Display Name' and 'Email Address' to collect, with 'Display Name' and 'Email Addresses' to return (pay careful attention to the fact that you are collecting emailaddress, singular, and asking to return email addresses, multiple), and click 'OK', then click 'Create'.

  9. Clique no fluxo de utilizador que criou na lista e, em seguida, clique no botão 'Executar o fluxo do utilizador'.Click on the user flow that you created in the list, then click the 'Run user flow' button.

  10. Esta ação abrirá a lâmina de fluxo do utilizador de execução, selecionará a aplicação frontend, copiará o ponto final do fluxo do utilizador e guardá-la-á para mais tarde.This action will open the run user flow blade, select the frontend application, copy the user flow endpoint and save it for later.

  11. Copie e guarde o link na parte superior, registando como o "conhecido ponto final de configuração openid" para posterior utilização.Copy and store the link at the top, recording as the 'well-known openid configuration endpoint' for later use.

    Nota

    As políticas B2C permitem-lhe expor os pontos finais de login Azure AD B2C para poder capturar diferentes componentes de dados e assinar nos utilizadores de diferentes formas.B2C Policies allow you to expose the Azure AD B2C login endpoints to be able to capture different data components and sign in users in different ways.

    Neste caso, configuramos um sinal ou assinamos no fluxo (política).In this case we configured a sign up or sign in flow (policy). Isto também expôs um ponto final de configuração bem conhecido, em ambos os casos a nossa política criada foi identificada no URL pelo parâmetro de cadeia de consulta "p=".This also exposed a well-known configuration endpoint, in both cases our created policy was identified in the URL by the "p=" query string parameter.

    Uma vez feito isto, tem agora uma plataforma de identidade funcional Business to Consumer que irá inscrever os utilizadores em várias aplicações.Once this is done, you now have a functional Business to Consumer identity platform that will sign users into multiple applications.

Construir a função APIBuild the function API

  1. Volte para o seu inquilino padrão Azure AD no portal Azure para que possamos configurar itens na sua subscrição novamente.Switch back to your standard Azure AD tenant in the Azure portal so we can configure items in your subscription again.

  2. Vá à lâmina de Apps de Função do portal Azure, abra a sua aplicação de função vazia e, em seguida, clique em 'Funções', clique em 'Adicionar'.Go to the Function Apps blade of the Azure portal, open your empty function app, then click 'Functions', click 'Add'.

  3. No flyout que aparece, escolha 'Desenvolver no portal', em 'selecione um modelo' e, em seguida, escolha 'HTTP trigger', nos detalhes do Modelo nomeá-lo 'olá' com nível de autorização 'Função', em seguida, selecione Adicionar.In the flyout that appears, choose 'Develop in portal', under 'select a template' then choose 'HTTP trigger', under Template details name it 'hello' with authorization level 'Function', then select Add.

  4. Mude para a lâmina de teste + e copie o código de amostra a partir de baixo sobre o código existente que aparece.Switch to the Code + Test blade and copy-paste the sample code from below over the existing code that appears.

  5. Selecione Guardar.Select Save.

    
    using System.Net;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.Extensions.Primitives;
    
    public static async Task<IActionResult> Run(HttpRequest req, ILogger log)
    {
       log.LogInformation("C# HTTP trigger function processed a request.");
    
       return (ActionResult)new OkObjectResult($"Hello World, time and date are {DateTime.Now.ToString()}");
    }
    
    

    Dica

    O código de função de script c# que acaba de colar simplesmente regista uma linha para os registos de funções e devolve o texto "Hello World" com alguns dados dinâmicos (a data e hora).The c# script function code you just pasted simply logs a line to the functions logs, and returns the text "Hello World" with some dynamic data (the date and time).

  6. Selecione "Integração" a partir da lâmina esquerda e, em seguida, clique na ligação http (req) dentro da caixa 'Trigger'.Select “Integration” from the left-hand blade, then click the http (req) link inside the 'Trigger' box.

  7. A partir do dropdown 'Selecionados métodos HTTP', desmarque o método http POST, deixando apenas OBT selecionado e, em seguida, clique em Guardar.From the 'Selected HTTP methods' dropdown, uncheck the http POST method, leaving only GET selected, then click Save.

  8. Volte para o separador Código + Teste, clique em 'Obter URL de função', em seguida, copie o URL que aparece e guarde-o para mais tarde.Switch back to the Code + Test tab, click 'Get Function URL', then copy the URL that appears and save it for later.

    Nota

    As encadernações que acabou de criar simplesmente dizem às Funções para responder em pedidos anónimos http GET para o URL que acabou de copiar https://yourfunctionappname.azurewebsites.net/api/hello?code=secretkey ().The bindings you just created simply tell Functions to respond on anonymous http GET requests to the URL you just copied (https://yourfunctionappname.azurewebsites.net/api/hello?code=secretkey). Agora temos uma API https sem servidor escalável, que é capaz de devolver uma carga útil muito simples.Now we have a scalable serverless https API, that is capable of returning a very simple payload.

    Agora pode testar a chamada a este API a partir de um navegador web usando a sua versão do URL acima que acabou de copiar e guardar.You can now test calling this API from a web browser using your version of the URL above that you just copied and saved. Também pode remover os parâmetros de cadeia de consulta "?code=secretkey" do URL , e testar novamente, para provar que as Funções Azure retornam um erro de 401.You can also remove the query string parameters "?code=secretkey" portion of the URL , and test again, to prove that Azure Functions will return a 401 error.

Configurar e assegurar a função APIConfigure and secure the function API

  1. Duas áreas extra na aplicação de função precisam de ser configuradas (Autorização e Restrições de Rede).Two extra areas in the function app need to be configured (Authorization and Network Restrictions).

  2. Em primeiro lugar, vamos configurar a autenticação / Autorização, por isso, volte para a lâmina de raiz da aplicação de função através da migalha de pão.Firstly Let's configure Authentication / Authorization, so navigate back to the root blade of the function app via the breadcrumb.

  3. Selecione seguinte 'Autenticação/ Autorização' (em 'Definições').Next select 'Authentication / Authorization' (under 'Settings').

  4. Ligue a função de autenticação do Serviço de Aplicações.Turn on the App Service Authentication feature.

  5. Desacorda a Ação a tomar quando o pedido não for autenticado para "Iniciar sessão com o Azure Ative Directory".Set the Action to take when request is not authenticated dropdown to "Log in with Azure Active Directory".

  6. Em 'Fornecedores de Autenticação', escolha 'Azure Ative Directory'.Under 'Authentication Providers', choose ‘Azure Active Directory’.

  7. Escolha 'Avançado' a partir do interruptor modo de gestão.Choose ‘Advanced’ from the Management Mode switch.

  8. Cole o ID do cliente da aplicação Backend (de Azure AD B2C) na caixa 'Client ID'Paste the Backend application's [Application] Client ID (from Azure AD B2C) into the ‘Client ID’ box

  9. Cole o conhecido ponto final de configuração de id aberto a partir da inscrição e inscreva-se na política na caixa URL do Emitente (registamos esta configuração anteriormente).Paste the Well-known open-id configuration endpoint from the sign up and sign in policy into the Issuer URL box (we recorded this configuration earlier).

  10. Clique em 'Mostrar Segredo' e cole o segredo do cliente da aplicação Backend na caixa apropriada.Click 'Show Secret' and paste the Backend application's client secret into the appropriate box.

  11. Selecione OK, que o leva de volta à lâmina/ecrã de seleção do fornecedor de identidade.Select OK, which takes you back to the identity provider selection blade/screen.

  12. Deixe a Token Store ativada em definições avançadas (predefinição).Leave Token Store enabled under advanced settings (default).

  13. Clique em 'Guardar' (na parte superior esquerda da lâmina).Click 'Save' (at the top left of the blade).

    Importante

    Agora a sua Função API é implantada e deve lançar 401 respostas se o JWT correto não for fornecido como Uma Autorização: Cabeçalho ao portador, e deve devolver dados quando um pedido válido for apresentado.Now your Function API is deployed and should throw 401 responses if the correct JWT is not supplied as an Authorization: Bearer header, and should return data when a valid request is presented.
    Adicionou segurança adicional em profundidade na EasyAuth, configurando a opção 'Login with Azure AD' para lidar com pedidos não autenticados.You added additional defense-in-depth security in EasyAuth by configuring the 'Login With Azure AD' option to handle unauthenticated requests. Esteja ciente de que isto irá alterar o comportamento de pedido não autorizado entre a App de Função Backend e o Frontend SPA, uma vez que a EasyAuth emitirá um redirecionamento 302 para a AAD em vez de uma resposta 401 Não Autorizada, corrigiremos isso utilizando a API Management mais tarde.Be aware that this will change the unauthorized request behavior between the Backend Function App and Frontend SPA as EasyAuth will issue a 302 redirect to AAD instead of a 401 Not Authorized response, we will correct this by using API Management later.

    Ainda não temos nenhuma segurança IP aplicada, se você tem uma chave válida e token OAuth2, qualquer um pode chamá-lo de qualquer lugar - idealmente queremos forçar todos os pedidos a vir através da API Management.We still have no IP security applied, if you have a valid key and OAuth2 token, anyone can call this from anywhere - ideally we want to force all requests to come via API Management.

    Se estiver a utilizar o nível de consumo de APIM, então não existe um IP Virtual de Gestão API dedicado para permitir a lista com as restrições de acesso às funções.If you're using APIM Consumption tier then there isn't a dedicated Azure API Management Virtual IP to allow-list with the functions access-restrictions. No Azure API Management Standard SKU e acima do VIP é inquilino único e para toda a vida do recurso.In the Azure API Management Standard SKU and above the VIP is single tenant and for the lifetime of the resource. Para o nível de Consumo de Gestão API Azure, pode bloquear as suas chamadas API através da chave de função secreta partilhada na parte do URI que copiou acima.For the Azure API Management Consumption tier, you can lock down your API calls via the shared secret function key in the portion of the URI you copied above. Além disso, para o nível de consumo - os passos 12-17 abaixo não se aplicam.Also, for the Consumption tier - steps 12-17 below do not apply.

  14. Feche a lâmina 'Autenticação / Autorização'Close the 'Authentication / Authorization' blade

  15. Abra a lâmina de Gestão API do portal e, em seguida, abra a sua instância.Open the API Management blade of the portal, then open your instance.

  16. Grave o VIP privado apresentado no separador visão geral.Record the Private VIP shown on the overview tab.

  17. Volte à lâmina Azure Functions do portal e, em seguida, abra novamente o seu caso.Return to the Azure Functions blade of the portal then open your instance again.

  18. Selecione 'Networking' e, em seguida, selecione 'Configurar restrições de acesso'Select 'Networking' and then select 'Configure access restrictions'

  19. Clique em 'Adicionar Regra', e introduza o VIP copiado no passo 3 acima no formato xx.xx.xx.xx.xx/32.Click 'Add Rule', and enter the VIP copied in step 3 above in the format xx.xx.xx.xx/32.

  20. Se pretender continuar a interagir com o portal de funções e a realizar os passos opcionais abaixo, deve adicionar também aqui o seu próprio endereço IP público ou alcance CIDR.If you want to continue to interact with the functions portal, and to carry out the optional steps below, you should add your own public IP address or CIDR range here too.

  21. Uma vez que haja uma entrada de permiti na lista, Azure adiciona uma regra implícita de negação para bloquear todos os outros endereços.Once there’s an allow entry in the list, Azure adds an implicit deny rule to block all other addresses.

Terá de adicionar blocos de endereços formatados ciDR ao painel de restrições IP.You'll need to add CIDR formatted blocks of addresses to the IP restrictions panel. Quando precisa de adicionar um único endereço, como o API Management VIP, tem de o adicionar no formato xx.xx.xx.xx.xx/32.When you need to add a single address such as the API Management VIP, you need to add it in the format xx.xx.xx.xx/32.

Nota

Agora, a sua API de função não deve ser chamada de outro lugar que não seja através da gestão da API, ou do seu endereço.Now your Function API should not be callable from anywhere other than via API management, or your address.

  1. Abra a lâmina de gestão da API e, em seguida, abra a sua instância.Open the API Management blade, then open your instance.

  2. Selecione a Lâmina apis (em APIs).Select the APIs Blade (under APIs).

  3. A partir do painel 'Adicionar uma Nova API', escolha 'App de função', e depois selecione 'Full' a partir do topo do popup.From the 'Add a New API' pane, choose 'Function App', then select 'Full' from the top of the popup.

  4. Clique em Procurar, escolha a aplicação de função que está hospedando a API no interior e clique em selecionar.Click Browse, choose the function app you're hosting the API inside, and click select. Em seguida, clique em selecionar novamente.Next, click select again.

  5. Dê à API um nome e descrição para o uso interno da API Management e adicione-o ao Produto 'ilimitado'.Give the API a name and description for API Management's internal use and add it to the ‘unlimited’ Product.

  6. Copiar e gravar o 'URL base' da API e clicar em 'criar'.Copy and record the API's 'base URL' and click 'create'.

  7. Clique no separador 'definições' e, em seguida, em subscrição - desligue a caixa de verificação 'Subscrição Necessária', uma vez que usaremos o token Oauth JWT neste caso para limitar a taxa.Click the 'settings' tab, then under subscription - switch off the 'Subscription Required' checkbox as we will use the Oauth JWT token in this case to rate limit. Note que se estiver a utilizar o nível de consumo, isso continuaria a ser necessário num ambiente de produção.Note that if you are using the consumption tier, this would still be required in a production environment.

    Dica

    Se utilizar o nível de consumo de APIM, o produto ilimitado não estará disponível como um fora da caixa.If using the consumption tier of APIM the unlimited product won't be available as an out of the box. Em vez disso, navegue para "Produtos" em "APIs" e atinja "Add".Instead, navigate to "Products" under "APIs" and hit "Add".
    Digite "Ilimitado" como nome e descrição do produto e selecione a API que acaba de adicionar a partir da chamada de APIs "+" na parte inferior esquerda do ecrã.Type "Unlimited" as the product name and description and select the API you just added from the "+" APIs callout at the bottom left of the screen. Selecione a caixa de verificação "publicada".Select the "published" checkbox. Deixe o resto como padrão.Leave the rest as default. Finalmente, acerte no botão "criar".Finally, hit the "create" button. Isto criou o produto "ilimitado" e atribuiu-o à sua API.This created the "unlimited" product and assigned it to your API. Pode personalizar o seu novo produto mais tarde.You can customize your new product later.

Configure e capture as definições corretas do ponto final de armazenamentoConfigure and capture the correct storage endpoint settings

  1. Abra a lâmina das contas de armazenamento no portal AzureOpen the storage accounts blade in the Azure portal

  2. Selecione a conta criada e selecione a lâmina 'Static Website' a partir da secção Definições (se não vir uma opção 'Site Estático', verifique se criou uma conta V2).Select the account you created and select the 'Static Website' blade from the Settings section (if you don't see a 'Static Website' option, check you created a V2 account).

  3. Deslote a função de hospedagem estática na web para 'activado', e deslote o nome do documento de índice para 'index.html', e depois clique em 'guardar'.Set the static web hosting feature to 'enabled', and set the index document name to 'index.html', then click 'save'.

  4. Note o conteúdo do 'Ponto final primário' para mais tarde, uma vez que este local é onde o site frontend será hospedado.Note down the contents of the 'Primary Endpoint' for later, as this location is where the frontend site will be hosted.

    Dica

    Você poderia usar ou Azure Blob Storage + CDN reescrita, ou Azure App Service para hospedar o SPA - mas a funcionalidade de hospedagem estática do Site da Blob Storage dá-nos um recipiente predefinido para servir conteúdo estático na Web / html / js / css da Azure Storage e irá inferir uma página padrão para nós para zero trabalho.You could use either Azure Blob Storage + CDN rewrite, or Azure App Service to host the SPA - but Blob Storage's Static Website hosting feature gives us a default container to serve static web content / html / js / css from Azure Storage and will infer a default page for us for zero work.

Configurar as políticas CORS e validar-jwtSet up the CORS and validate-jwt policies

As seguintes secções devem ser seguidas independentemente da utilização do nível APIM.The following sections should be followed regardless of the APIM tier being used. O URL da conta de armazenamento é da conta de armazenamento que terá disponibilizado a partir dos pré-requisitos no topo deste artigo.The storage account URL is from the storage account you will have made available from the prerequisites at the top of this article.

  1. Mude para a lâmina de gestão API do portal e abra o seu exemplo.Switch to the API management blade of the portal and open your instance.

  2. Selecione APIs e, em seguida, selecione "Todas as APIs".Select APIs, then select “All APIs”.

  3. Em "Processamento de entrada", clique no botão de visualização de código "</>" para mostrar ao editor de política.Under "Inbound processing", click the code view button "</>" to show the policy editor.

  4. Edite a secção de entrada e cole a xml abaixo para que se leia como se fosse o seguinte.Edit the inbound section and paste the below xml so it reads like the following.

  5. Substitua os seguintes parâmetros na PolíticaReplace the following parameters in the Policy

  6. {PrimaryStorageEndpoint} (O 'Ponto final de armazenamento primário' que copiou na secção anterior), {b2cpolicy-bem conhecido-openid} (O 'conhecido ponto final de configuração aberta' que copiou anteriormente) e {backend-api-application-client-id} (Aplicação B2C / ID do cliente para a API de backend) com os valores corretos guardados anteriormente.{PrimaryStorageEndpoint} (The 'Primary Storage Endpoint' you copied in the previous section), {b2cpolicy-well-known-openid} (The 'well-known openid configuration endpoint' you copied earlier) and {backend-api-application-client-id} (The B2C Application / Client ID for the backend API) with the correct values saved earlier.

  7. Se estiver a utilizar o nível de consumo da Gestão de API, então deve remover ambas as políticas de limite de taxa por chave, uma vez que esta política não está disponível ao utilizar o nível de consumo da Gestão API Azure.If you're using the Consumption tier of API Management, then you should remove both rate-limit-by-key policy as this policy is not available when using the Consumption tier of Azure API Management.

    <inbound>
       <cors allow-credentials="true">
             <allowed-origins>
                 <origin>{PrimaryStorageEndpoint}</origin>
             </allowed-origins>
             <allowed-methods preflight-result-max-age="120">
                 <method>GET</method>
             </allowed-methods>
             <allowed-headers>
                 <header>*</header>
             </allowed-headers>
             <expose-headers>
                 <header>*</header>
             </expose-headers>
         </cors>
       <validate-jwt header-name="Authorization" failed-validation-httpcode="401" failed-validation-error-message="Unauthorized. Access token is missing or invalid." require-expiration-time="true" require-signed-tokens="true" clock-skew="300">
          <openid-config url="{b2cpolicy-well-known-openid}" />
          <required-claims>
             <claim name="aud">
                <value>{backend-api-application-client-id}</value>
             </claim>
          </required-claims>
       </validate-jwt>
       <rate-limit-by-key calls="300" renewal-period="120" counter-key="@(context.Request.IpAddress)" />
       <rate-limit-by-key calls="15" renewal-period="60" counter-key="@(context.Request.Headers.GetValueOrDefault("Authorization","").AsJwt()?.Subject)" />
    </inbound>
    

    Nota

    Agora, a gestão da API da Azure é capaz de responder aos pedidos de origem cruzada das suas aplicações JavaScript SPA, e irá executar estrangulamento, limitação de tarifas e pré-validação do token JWT auth sendo passado antes de encaminhar o pedido para a API de função.Now Azure API management is able respond to cross origin requests from your JavaScript SPA apps, and it will perform throttling, rate-limiting and pre-validation of the JWT auth token being passed BEFORE forwarding the request on to the Function API.

    Parabéns, tem agora Azure AD B2C, API Management e Azure Functions trabalhando em conjunto para publicar, proteger e consumir uma API!Congratulations, you now have Azure AD B2C, API Management and Azure Functions working together to publish, secure AND consume an API!

    Dica

    Se estiver a utilizar o nível de consumo de Gestão API, em vez de limitar a taxa pelo assunto JWT ou pelo endereço IP (A taxa de chamada limite por política-chave não é suportada hoje para o nível "Consumo"), pode limitar por quota de taxa de chamada ver aqui.If you're using the API Management consumption tier then instead of rate limiting by the JWT subject or incoming IP Address (Limit call rate by key policy is not supported today for the "Consumption" tier), you can Limit by call rate quota see here.
    Como este exemplo é uma Aplicação de página única JavaScript, utilizamos a Chave de Gestão da API apenas para chamadas de limitação de taxas e faturação.As this example is a JavaScript Single Page Application, we use the API Management Key only for rate-limiting and billing calls. A autorização e autenticação real é tratada pelo Azure AD B2C, e é encapsulada no JWT, que é validado duas vezes, uma pela API Management, e depois pela Função Azure backend.The actual Authorization and Authentication is handled by Azure AD B2C, and is encapsulated in the JWT, which gets validated twice, once by API Management, and then by the backend Azure Function.

Faça o upload da amostra JavaScript SPA para armazenamento estáticoUpload the JavaScript SPA sample to static storage

  1. Ainda na lâmina da conta de armazenamento, selecione a lâmina 'Containers' da secção Serviço Blob e clique no recipiente $web que aparece no painel direito.Still in the storage account blade, select the 'Containers' blade from the Blob Service section and click on the $web container that appears in the right-hand pane.

  2. Guarde o código abaixo para um ficheiro localmente na sua máquina, uma vez index.html e, em seguida, faça o upload do ficheiro index.html para o recipiente $web.Save the code below to a file locally on your machine as index.html and then upload the file index.html to the $web container.

     <!doctype html>
     <html lang="en">
     <head>
          <meta charset="utf-8">
          <meta http-equiv="X-UA-Compatible" content="IE=edge">
          <meta name="viewport" content="width=device-width, initial-scale=1">
          <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-BmbxuPwQa2lc/FVzBcNJ7UAyJxM6wuqIj61tLrc4wSX0szH/Ev+nYRRuWlolflfl" crossorigin="anonymous">
          <script type="text/javascript" src="https://alcdn.msauth.net/browser/2.11.1/js/msal-browser.min.js"></script>
     </head>
     <body>
          <div class="container-fluid">
              <div class="row">
                  <div class="col-md-12">
                     <nav class="navbar navbar-expand-lg navbar-dark bg-dark">
                         <div class="container-fluid">
                             <a class="navbar-brand" href="#">Azure Active Directory B2C with Azure API Management</a>
                             <div class="navbar-nav">
                                 <button class="btn btn-success" id="signinbtn"  onClick="login()">Sign In</a>
                             </div>
                         </div>
                     </nav>
                  </div>
              </div>
              <div class="row">
                  <div class="col-md-12">
                      <div class="card" >
                         <div id="cardheader" class="card-header">
                             <div class="card-text"id="message">Please sign in to continue</div>
                         </div>
                         <div class="card-body">
                             <button class="btn btn-warning" id="callapibtn" onClick="getAPIData()">Call API</a>
                             <div id="progress" class="spinner-border" role="status">
                                 <span class="visually-hidden">Loading...</span>
                             </div>
                         </div>
                      </div>
                  </div>
              </div>
          </div>
          <script lang="javascript">
                 // Just change the values in this config object ONLY.
                 var config = {
                     msal: {
                         auth: {
                             clientId: "{CLIENTID}", // This is the client ID of your FRONTEND application that you registered with the SPA type in AAD B2C
                             authority:  "{YOURAUTHORITYB2C}", // Formatted as https://{b2ctenantname}.b2clogin.com/tfp/{b2ctenantguid or full tenant name including onmicrosoft.com}/{signuporinpolicyname}
                             redirectUri: "{StoragePrimaryEndpoint}", // The storage hosting address of the SPA, a web-enabled v2 storage account - recorded earlier as the Primary Endpoint.
                             knownAuthorities: ["{B2CTENANTDOMAIN}"] // {b2ctenantname}.b2clogin.com
                         },
                         cache: {
                             cacheLocation: "sessionStorage",
                             storeAuthStateInCookie: false 
                         }
                     },
                     api: {
                         scopes: ["{BACKENDAPISCOPE}"], // The scope that we request for the API from B2C, this should be the backend API scope, with the full URI.
                         backend: "{APIBASEURL}/hello" // The location that we will call for the backend api, this should be hosted in API Management, suffixed with the name of the API operation (in the sample this is '/hello').
                     }
                 }
                 document.getElementById("callapibtn").hidden = true;
                 document.getElementById("progress").hidden = true;
                 const myMSALObj = new msal.PublicClientApplication(config.msal);
                 myMSALObj.handleRedirectPromise().then((tokenResponse) => {
                     if(tokenResponse !== null){
                         console.log(tokenResponse.account);
                         document.getElementById("message").innerHTML = "Welcome, " + tokenResponse.account.name;
                         document.getElementById("signinbtn").hidden = true;
                         document.getElementById("callapibtn").hidden = false;
                     }}).catch((error) => {console.log("Error Signing in:" + error);
                 });
                 function login() {
                     try {
                         myMSALObj.loginRedirect({scopes: config.api.scopes});
                     } catch (err) {console.log(err);}
                 }
                 function getAPIData() {
                     document.getElementById("progress").hidden = false; 
                     document.getElementById("message").innerHTML = "Calling backend ... "
                     document.getElementById("cardheader").classList.remove('bg-success','bg-warning','bg-danger');
                     myMSALObj.acquireTokenSilent({scopes: config.api.scopes, account: getAccount()}).then(tokenResponse => {
                         const headers = new Headers();
                         headers.append("Authorization", `Bearer ${tokenResponse.accessToken}`);
                         fetch(config.api.backend, {method: "GET", headers: headers})
                             .then(async (response)  => {
                                 if (!response.ok)
                                 {
                                     document.getElementById("message").innerHTML = "Error: " + response.status + " " + JSON.parse(await response.text()).message;
                                     document.getElementById("cardheader").classList.add('bg-warning');
                                 }
                                 else
                                 {
                                     document.getElementById("cardheader").classList.add('bg-success');
                                     document.getElementById("message").innerHTML = await response.text();
                                 }
                                 }).catch(async (error) => {
                                     document.getElementById("cardheader").classList.add('bg-danger');
                                     document.getElementById("message").innerHTML = "Error: " + error;
                                 });
                     }).catch(error => {console.log("Error Acquiring Token Silently: " + error);
                         return myMSALObj.acquireTokenRedirect({scopes: config.api.scopes, forceRefresh: false})
                     });
                     document.getElementById("progress").hidden = true;
              }
             function getAccount() {
                 var accounts = myMSALObj.getAllAccounts();
                 if (!accounts || accounts.length === 0) {
                     return null;
                 } else {
                     return accounts[0];
                 }
             }
         </script>
      </body>
     </html>
    
  3. Navegue no ponto de final primário do site estático que armazenou anteriormente na última secção.Browse to the Static Website Primary Endpoint you stored earlier in the last section.

    Nota

    Parabéns, acabaste de implementar uma App de Página Única JavaScript para hospedar conteúdo estático de armazenamento de Azure.Congratulations, you just deployed a JavaScript Single Page App to Azure Storage Static content hosting.
    Uma vez que ainda não configuramos a aplicação JS com os seus detalhes Azure AD B2C – a página ainda não funcionará se a abrir.Since we haven’t configured the JS app with your Azure AD B2C details yet – the page won't work yet if you open it.

Configure o JavaScript SPA para Azure AD B2CConfigure the JavaScript SPA for Azure AD B2C

  1. Agora sabemos onde está tudo: podemos configurar o SPA com o endereço API de Gestão de API adequado e a aplicação/IDs de cliente Azure AD B2C.Now we know where everything is: we can configure the SPA with the appropriate API Management API address and the correct Azure AD B2C application / client IDs.
  2. Volte para a lâmina de armazenamento do portal AzureGo back to the Azure portal storage blade
  3. Selecione 'Contentores' (em 'Definições')Select 'Containers' (under 'Settings')
  4. Selecione o recipiente '$web' da listaSelect the '$web' container from the list
  5. Selecione index.html blob da listaSelect index.html blob from the list
  6. Clique em 'Editar'Click 'Edit'
  7. Atualize os valores de auth na secção msal config para corresponder à sua aplicação frontal que registou anteriormente em B2C.Update the auth values in the msal config section to match your front-end application you registered in B2C earlier. Utilize os comentários de código para obter pistas sobre como os valores config devem parecer.Use the code comments for hints on how the config values should look. O valor da autoridade tem de estar no formato:- https://{b2ctenantname}.b2clogin.com/tfp/{b2ctenantname}.onmicrosoft.com}/{signupandsigninpolicyname}, se usou os nossos nomes de amostra e o seu inquilino b2c é chamado de 'contoso', então esperaria que a autoridade fosse ' https://contoso.b2clogin.com/tfp/contoso.onmicrosoft.com}/Frontendapp_signupandsignin .The authority value needs to be in the format:- https://{b2ctenantname}.b2clogin.com/tfp/{b2ctenantname}.onmicrosoft.com}/{signupandsigninpolicyname}, if you have used our sample names and your b2c tenant is called 'contoso' then you would expect the authority to be 'https://contoso.b2clogin.com/tfp/contoso.onmicrosoft.com}/Frontendapp_signupandsignin'.
  8. Defina os valores api para corresponder ao seu endereço de backend (O Url base da API que gravou anteriormente, e os valores 'b2cScopes' foram registados anteriormente para a aplicação backend).Set the api values to match your backend address (The API Base Url you recorded earlier, and the 'b2cScopes' values were recorded earlier for the backend application).
  9. Clicar em GuardarClick Save

Desaprova os URIs de redirecionamento para a app frontal Azure AD B2CSet the redirect URIs for the Azure AD B2C frontend app

  1. Abra a lâmina Azure AD B2C e navegue para o registo de aplicação para a Aplicação Frontend JavaScript.Open the Azure AD B2C blade and navigate to the application registration for the JavaScript Frontend Application.

  2. Clique em 'Redirecionar URIs' e apagar o espaço reservado ' https://jwt.ms '' que inserimos anteriormente.Click 'Redirect URIs' and delete the placeholder 'https://jwt.ms' we entered earlier.

  3. Adicione um novo URI para o ponto final primário (armazenamento) (menos o corte para a frente).Add a new URI for the primary (storage) endpoint (minus the trailing forward slash).

    Nota

    Esta configuração resultará em que um cliente da aplicação frontend receba um token de acesso com reclamações apropriadas da Azure AD B2C.This configuration will result in a client of the frontend application receiving an access token with appropriate claims from Azure AD B2C.
    O SPA poderá adicionar isto como um símbolo portador no cabeçalho https na chamada para a API backend.The SPA will be able to add this as a bearer token in the https header in the call to the backend API.

    A API Management irá pré-validar as chamadas simbólicas, limite de taxa para o ponto final, tanto pelo sujeito do JWT emitido pelo Azure ID (o utilizador) como pelo endereço IP do chamador (dependendo do nível de serviço da Gestão da API, ver a nota acima), antes de passar o pedido à API da Função Azure recetora, adicionando a chave de segurança das funções.API Management will pre-validate the token, rate-limit calls to the endpoint by both the subject of the JWT issued by Azure ID (the user) and by IP address of the caller (depending on the service tier of API Management, see the note above), before passing through the request to the receiving Azure Function API, adding the functions security key.
    O SPA irá dar a resposta no navegador.The SPA will render the response in the browser.

    Parabéns, configuraste a Azure AD B2C, Azure API Management, Azure Functions, Azure App Service Authorization to work in perfect harmony!Congratulations, you’ve configured Azure AD B2C, Azure API Management, Azure Functions, Azure App Service Authorization to work in perfect harmony!

Agora temos uma aplicação simples com uma API segura simples, vamos testá-la.Now we have a simple app with a simple secured API, let's test it.

Testar a aplicação do clienteTest the client application

  1. Abra o URL da aplicação de amostra que anotado a partir da conta de armazenamento que criou anteriormente.Open the sample app URL that you noted down from the storage account you created earlier.
  2. Clique em "Iniciar sessão" no canto superior direito, este clique irá aparecer no seu Azure AD B2C ou iniciar sessão de perfil.Click “Sign In” in the top-right-hand corner, this click will pop up your Azure AD B2C sign up or sign in profile.
  3. A aplicação deve recebê-lo pelo seu nome de perfil B2C.The app should welcome you by your B2C profile name.
  4. Clique em "Call API" e a página deverá atualizar-se com os valores enviados da sua API protegida.Now Click "Call API" and the page should update with the values sent back from your secured API.
  5. Se clicar repetidamente no botão Call API e estiver a funcionar no nível de desenvolvimento ou acima da API Management, deve ter em atenção que a sua solução começará a limitar a API e esta funcionalidade deve ser reportada na app com uma mensagem apropriada.If you repeatedly click the Call API button and you're running in the developer tier or above of API Management, you should note that your solution will begin to rate limit the API and this feature should be reported in the app with an appropriate message.

E nós terminamosAnd we're done

Os passos acima podem ser adaptados e editados para permitir muitas utilizações diferentes do Azure AD B2C com a API Management.The steps above can be adapted and edited to allow many different uses of Azure AD B2C with API Management.

Passos seguintesNext steps