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