.NET

21 jul, 2020

ASP.NET Core 3.1 — IdentityServer4 — API de Pagamento (Parte 3)

100 visualizações
Publicidade
Image for post

Antes de começarmos, se você não acompanhou a Parte 1 e a Parte 2, sugiro que você revise os entendimentos para seguir na configuração da API de Pagamento, que será o foco principal desta Parte 3.

Na Parte 1 eu falo sobre  e , a configuração feita lá é de um Client que é acessado através de um Id e uma senha (Secret) que possui um escopo para a ApiResource chamada Payment. Traduzindo isso significa que:

Uma aplicação cliente pode requisitar um token ao  com um usuário e senha, indicando que ele quer o token que permite o acesso a uma API que possui o escopo de pagamento, somente esse token gerado com esse Client permite o acesso à API de pagamento.

Criação do projeto da API

Para criar o projeto, siga as imagens a seguir:

Image for post

Dependendo de quando você instalou o SDK do .NET Core 3.1 o template pode vir com classes e controllers que são desnecessárias, então basta apagá-las. Eu dei o nome de  ao projeto e o coloquei dentro da pasta src > Web.

NuGet Package

Através do NuGet, adicione o pacote

IdentityServer4.AccessTokenValidation

Statup.cs

Configure o arquivo  conforme abaixo.

using IdentityServer4.AccessTokenValidation;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace Payment.Api
{
    public class Startup
    {
        public IConfiguration Configuration { get; }
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddAuthorization();

            services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
                .AddIdentityServerAuthentication(options =>
                {
                    options.Authority = Configuration.GetSection("IdentityServerAuthentication:Authority").Value;
                    options.RequireHttpsMetadata = false;

                    options.ApiName = Configuration.GetSection("IdentityServerAuthentication:ApiName").Value;
                });

            services.AddControllers();
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
                app.UseDeveloperExceptionPage();
            else
                app.UseHttpsRedirection();

            app.UseRouting();

            app.UseAuthentication();
            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }
}

 Tome cuidado com a ordem das linhas, isso faz diferença e pode ser o diferencial no funcionamento.

OpenID Connect

O método “AddIdentityServerAuthentication” usa por baixo dos panos uma camada de identificação simples que é uma abstração para a implementação do protocolo OAuth 2.0. É esta camada que permite que uma apliação cliente possa solicitar autorização a um serviço provedor de autenticação também conhecido como service provider.

Quando o token é passado na chamada da API, através do OpenID Connect é realizada uma requisição ao IdentityServer (service provider) que vai autenticar a chamada e permitir a autorização de acesso à controller.

Para o OpenID Connect, você vai ver nomenclaturas como  que significa a mesma coisa, bem como para o IdentityServer, você vai encontrar nomenclaturas como .

Github

Caso tenha dúvidas, baixe o código no Github. Eu estou separando cada uma das etapas destes posts em branchs distintos, este projeto da API usa a solution  e está no branch .

appsettings.json

Neste arquivo de configuração, já estão pré-definidas a configuração do  que é o serviço ao qual o token será verificado se é confiável ou não, no caso esta atribuição é do IdentityServer, por isso aqui vai a URL dele e está a configuração do nome do  que essa API permite a conexão.

Quando uma aplicação cliente requisita um token ao IdentityServer com um usuário e senha e especificando o escopo de qual API será acessada com esse token é este escopo do  que será validado. Você pode achar estranho que a configuração ApiName só permite um nome (uma string), não vai permitir que você faça com que uma API atenda a duas ou mais funções diferentes, pois acaba ferindo um dos princípios dos microsserviços, o da responsabilidade única, uma API possui um único objetivo e uma única responsabilidade.

Configure o arquivo  conforme a seguir.

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*",
  "IdentityServerAuthentication": {
    "Authority": "http://localhost:5005",
    "ApiName": "payment"
  }
}

Executando o IdentityServer

Antes de testarmos a API de Pagamento, precisamos que o IdentityServer esteja em execução, para isso, você pode executá-lo usando os comandos do Powershell conforme imagem a seguir, lembrando que o branch (identityserverinmemory) do IdentityServer é diferente da API, você pode conferir no Github.

Image for post

Token para o escopo da API de Pagamento

A imagem a seguir mostra como buscar o token que permite acessar a API payment que configuramos.

Image for post

token é o conjunto de caracteres do json da imagem, começa com “eyJhb….”.
Este token é do tipo  e permite acesso à API que pertence ao escopo  que falamos nos tópicos anteriores deste post. O tipo também pode ser chamado de token de autenticação, é um schema de autenticação utilizando protocolo HTTP e considerado seguro.
token é válido por 3600 segundos (1 hora), depois desse período ele passa a ser inválido e você precisa solicitar um novo token.

Particularidades sobre o token

  • Se você alterar o certificado digital, o token deixará de ser válido, seja o arquivo *.rsa ou o *.pfx, qualquer mudança vai invalidar o token gerado antes da alteração.
  • Você pode se perguntar também sobre o refresh token, para o grant_type  o refresh não é permitido, papo para outra hora, quando nos aprofundaremos nessas configurações e quais as opções permitidas, bem como aumentar o tempo que o token é válido.

Curiosidade sobre o token

Pegue token e entre no site da JWT, coloque o token no campo  e ao lado direito você vai poder ver algumas informações que estão contidas no token e que são públicas.
Minha sugestão é que você pesquise sobre o assunto, você vai descobrir coisas incríveis.

Controller da API de Pagamento

Vamos configurar a controller conforme o código a seguir.

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;

namespace Payment.Api.Controllers
{
    [Authorize]
    [Route("api/[controller]")]
    public class PaymentController : ControllerBase
    {
        [HttpGet]
        public ActionResult<IEnumerable<string>> Get()
        {
            return new string[] { "value1", "value2" };
        }
    }
}

Veja que no código estou forçando a obrigatoriedade de estar autorizado ([Authorized] como atributo da classe), também especifiquei uma rota.

Chamando a API de Pagamento

Execute o projeto, por linha de comando, F5, da forma que você quiser, eu configurei a API para a porta 5002.

Usei novamente o Postman para fazer o GET conforme imagem a seguir, porém nossa chamada precisa estar autorizada e para isso vamos configurar o Postman para autenticar com o tipo Bearer (falei mais acima sobre ele) e utilizando o token solicitado ao IdentityServer.

Image for post

Na aba Authorization, selecione o tipo Bearer Token e coloque o token no textbox em branco, depois disso clique em “Send”. Deve retornar um json com os valores “value1” e “value2”.

Você pode testar com o token gerado depois de 1 hora, sem token e outras formas criativas, o resultado deve ser o erro 401 (não autorizado).

 Este projeto está em constante evolução, então pode haver diferenças entre o código do gist e o código real. Além disso, estou codificando esta API para que ela possua a implementação do pattern facade para múltiplas formas de pagamento, para que tenha a camada anti-corrupção para termos um exemplo mais próximo do mundo real.

  ASP.NET Core 3.1 — IdentityServer4 — EF-support (Parte 4).