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

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 ApiResources e Clients, 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 IdentityServer 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:

Payment API

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 Payment.Api 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 Startup.cs conforme abaixo.

As linhas de código mais importantes neste arquivo são:

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();  
app.UseAuthentication();  
app.UseAuthorization();  

Observação: 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 oidc que significa a mesma coisa, bem como para o IdentityServer, você vai encontrar nomenclaturas como idsrv.

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 FullProjectOne-Web.sln e está no branch payment-api.

appsettings.json

Neste arquivo de configuração, já estão pré-definidas a configuração do Authority 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 ApiResource 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 ApiResource 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 appsettings.json conforme a seguir.

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 (identityserver-inmemory) do IdentityServer é diferente da API, você pode conferir no Github.

IdentityServer Running

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.

IdentityServer buscando token

O token é o conjunto de caracteres do json da imagem, começa com "eyJhb....".
Este token é do tipo Bearer e permite acesso à API que pertence ao escopo payment que falamos nos tópicos anteriores deste post. O tipo Bearer também pode ser chamado de token de autenticação, é um schema de autenticação utilizando protocolo HTTP e considerado seguro.
O 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 client_credentials 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 Encoded 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.

[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.

Payment-Api Access

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).

PS: 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.

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