ASP.NET Core 3.1 - IdentityServer4 - MVC-API Request (Parte 9)

No Post 8 eu mostrei como fazer com que o IdentityServer4 se tornasse uma aplicação para que o usuário pudesse se autenticar com seu login e senha, sem que houvesse a necessidade da aplicação MVC implementar o seu próprio login.

Isso significa que podemos ter várias aplicações que implementam o mesmo login com mesmo usuário e senha? A response é sim.

OpenID Connect

Lá na configuração do OpenID Connect, eu comentei que falaria de algumas características especificas mais adiante, vamos falar de uma delas:

  • ResponseType: Após realizar o login no nosso Facebook simulado, a resposta HTTP, traz junto consigo o access_token e o id_token. Este último por sua vez é trazido quando habilitada a opção OpenIdConnectResponseType.CodeIdToken no ResponseType, você pode revisitar o post anterior para uma breve espiada.

IdentityServer4 - AllowedScopes

Lá na configuração do Client MVC do IdentityServer4, eu indiquei que um dos escopos permitidos seria o escopo payment, isso é o que vai permitir que com o access_token eu consiga acessar a API sem solicitar um token específico para isso. Você pode conferir essa configuração lá na Parte 7 onde fala sobre mvc-client.

Chamada da API de Pagamento

O access_token será utilizado como a autorização para a API, sem a necessidade de a todo momento solicitar um token ao IdentityServer para depois chamar a API.

Executando

Execute os 3 projetos:

Acesse a URL da aplicação MVC, realize o login com o usuário e senha, mostrei tudo funcionando na Parte 8.

Depois de logado, vai aparecer um menu novo, chamado API conforme exibido na imagem a seguir.
MVC-Logged

Clique no menu API e você vai ter um resultado muito parecido com o da imagem a seguir.
MVC-Logged-Request-Payment-API

Access Token

Falei um pouco sobre ele mais acima, mas é vale a pena lembrar que esse token expira por padrão em 3600 segundos (1 hora), este valor pode ser alterado lá na configuração do Client. Mais informações eu recomendo olhar a documentação aqui.

ID Token

O ID Token é uma extensão do OpenID Connect que implementa o OAuth 2.0, isso permite que você tenha acesso aos dados do usuário logado. Através dele você também pode controlar as informações do c_hash, você precisa implementar esse controle que vai apoiar na questão de segurança evitando possiveis problemas como code substitution attack. Você pode ver mais informações aqui.
Eu aproveito para comentar que farei um post específico só sobre segurança, o que não deveria mais ser utilizado nas configurações do IdentityServer4 e quais as melhores alternativas.

JSON

Este é o JSON que é retornado pela API de Pagamento, veja que há a indicação de que o usuário está logado, mas como isso é possível se a API não acessa o Identity, tão pouco acessa banco de dados e nem sabe qual é o usuário que pediu autorização de acesso? Isso é possível por conta de todas as configurações que fizemos, nos conectando ao IdentityServer4.

Uso do HttpClientFactory

Há tempos atrás, a Microsoft passou uma orientação sobre o uso do HttpClient em requisições muito utilizadas por microsserviços e comentou sobre os possíveis problemas em algumas formas de implementação.
Em função disso, eu aproveitei que fiz a chamada da API de Pagamento e implementei o uso do HttpClientFactory através de injeção de dependência (DI) no projeto MVC, aproveite para dar uma olhada neste funcionamento que implementa a interface IHttpClientFactory como sendo um singleton com a implementação por nome.

As alterações feitas estão no arquivo Startup.cs

services.AddHttpClient("payment", options =>  
{
    options.BaseAddress = new Uri("http://localhost:5002");
});

e no arquivo HomeController.cs

public async Task<IActionResult> RequestApi()  
{
    var client = _httpClientFactory.CreateClient("payment");

    var accessToken = HttpContext.GetTokenAsync("access_token").Result;
    var idToken = HttpContext.GetTokenAsync("id_token").Result;

    client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", accessToken);

    var response = await client.GetAsync("/api/payment");
    if (!response.IsSuccessStatusCode)
    {
        return View(nameof(Privacy));
    }

    var model = new PaymentApiViewModel
    {
        AccessToken = accessToken,
        IdToken = idToken,
        JsonResult = response.Content.ReadAsStringAsync().Result
    };

    return View(model);
}

Sugiro você baixar o projeto para pegar todas as alterações, acesse o código fonte no GitHub.

Continua em ASP.NET Core 3.1 - IdentityServer4 - Client-Security (Parte 10).