ASP
.NET Core - Usando o IdentityServer4
![]() |
Neste artigo veremos como usar o IdentityServer4 com a ASP .NET Core. |
Eu já apresentei o IdentityServer4 neste artigo : ASP .NET Core - Apresentando o IdentityServer4
E hoje vou mostrar como usar o IdentityServer4(IS4) em uma aplicação ASP .NET Core Web API mas antes vou fazer um apanhado geral dos conceitos e responsabilidades relacionados com o IS4.
O que é o IdentityServer4 ?
O IdentityServer4 (IS4) é a versão mais recente do IdentityServer que é um framework OpenID Connect e OAuth 2.0 gratuito que podemos usar com a ASP.NET Core. A ideia principal ao usar o IS4 é centralizar o provedor de autenticação.
Assim vamos supor
que você tenha 3 APIs ou microservicos que precisam da segurança da
autenticação. Neste cenário você não precisa
ter que definir a lógica de autenticação em cada aplicativo. Em vez disso,
usando o IS4 você consegue centralizar o Controle de Acesso para que
cada API/microserviço seja protegida pelo IdentityServer de forma centralizada.
Outro recurso existente é quando um cliente (aplicativo Web) deseja
acessar uma API protegida, o IdentityServer4 gera e
valida tokens de acesso para
tornar isso possível.
Como o Identity Server funciona ?
É bem simples :
- Os usuários usam
os 'clientes', como uma aplicação
ASP .NET Core MVC, para acessar os
dados;
- Os usuários serão autenticados pelo
IdentityServer para usar o cliente;
- Depois que os usuários são autenticados para usar o Cliente, o cliente envia
uma solicitação ao Recurso da API;
- Tanto o Cliente quanto os Recursos da API são protegidos por uma única entidade, o
IdentityServer;
- O cliente pode solicitar um token de acesso com o qual ele pode acessar as
respostas da API;
- O IS4 também pode ser para emitir os tokens de segurança para os clientes;
Desta forma, estamos centralizando o Mecanismo de Autenticação em um único servidor.
Quais as responsabilidades do IdentityServer4 ?
O Identity Server é uma solução de segurança completa para seus projetos. Aqui estão seus principais recursos e responsabilidades :
Como começar com o IdentityServer4 ?
Existem diversas maneiras de iniciar projetos com o IdentityServer4.
A mais fácil é rápida é usar templates prontos onde você instala os templates IdentityServer4 usando a ferramenta de linha de comando - CLI - e escolhe um template para criar de forma automática um projeto.
Vou mostrar os comandos que podemos usar para instalar os templates e criar um projeto com o IS4 usando a CLI :
dotnet new -i
IdentityServer4.Templates md testeis4 cd testeis4 md src cd src dotnet new is4empty -n IdentityServer dotnet new is4ui -n IdentityServer cd .. dotnet new sln -n TesteIS4 dotnet sln add .\src\IdentityServer\IdentityServer.csproj |
O primeiro comando : dotnet new -i IdentityServer4.Templates
Vai restaurar e instalar os templates que podemos usar para criar projeto com o IdentityServer4.
Após executar este comando emita o comando dotnet new para ver os novos templates instalados:
A seguir vamos criar a pasta testeis4 e dentro desta pasta vamos criar a pasta src e nesta pasta usar os templates is4empty e is4ui com o comando dotnet new que vai criar um projeto padrão básico com o IdentityServer4 :
Ao final podemos entrar na pasta do projeto e visualizar a sua estrutura :
Para criar um projeto com uma implementação do IS4 em um projeto ASP .NET Core usando configurações e Usuário na memória basta executar o seguinte comando : dotnet new is4inmem
Essa seria a
maneira mais direta e fácil de criar um projeto com o IS4 integrado mas neste
artigo eu não vou usar essa abordagem pois ela oculta grande parte da complexidade e você
acaba não sabendo o que realmente acontece nos bastidores.
Vamos implementar o servidor a partir do zero e, assim você fica familiarizado com seu funcionamento,
e
estará pronto para usar esses templates com mais conhecimento.
Criando projetos e usando o IdentityServer4
Nesta etapa vamos iniciar a integração do IdentityServer com a ASP .NET Core realizando as seguintes tarefas:
Vamos usar o ambiente do .NET 5.0 e o Visual Studio 2019 Community versão 16.8.3.
Abra o VS 2019 Community e crie uma solução em branco usando o template Blank Solution chamada: AspnIdentityServer4
A seguir vamos incluir um novo projeto ASP .NET Core acionando o menu File-> Add new Project e usando o template ASP.NET Core Empty e informando o nome IdentityServer
Estamos criando um projeto vazio e vamos agora instalar o pacote do IdentityServer4 neste projeto abrindo a janela do Package Manager Console e digitando o comando : Install-Package IdentityServer4
Ao final podemos abrir a janela Solution Explorer e confirmar a inclusão dos pacotes no projeto :
Agora vamos iniciar a configuração do IdentityServer4 que neste artigo esta sendo feita para fins de demonstração.
Vamos criar a classe IdentityConfiguration.cs na raiz do projeto e definir o código abaixo:
using IdentityModel;
using IdentityServer4.Models;
using IdentityServer4.Test;
using System.Collections.Generic;
using System.Security.Claims;
public class IdentityConfiguration
{
public static List<TestUser> TestUsers =>
new List<TestUser>
{
new TestUser
{
SubjectId = "1144",
Username = "macoratti",
Password = "numsey",
Claims =
{
new Claim(JwtClaimTypes.Name, "Macoratti Net"),
new Claim(JwtClaimTypes.GivenName, "Macoratti"),
new Claim(JwtClaimTypes.FamilyName, "Net"),
new Claim(JwtClaimTypes.WebSite, "http://macoratti.net"),
}
}
};
public static IEnumerable<IdentityResource> IdentityResources =>
new IdentityResource[]
{
new IdentityResources.OpenId(),
new IdentityResources.Profile(),
};
public static IEnumerable<ApiScope> ApiScopes =>
new ApiScope[]
{
new ApiScope("myApi.read"),
new ApiScope("myApi.write"),
};
public static IEnumerable<ApiResource> ApiResources =>
new ApiResource[]
{
new ApiResource("myApi")
{
Scopes = new List<string>{ "myApi.read","myApi.write" },
ApiSecrets = new List<Secret>{ new Secret("supersecret".Sha256()) }
}
};
public static IEnumerable<Client> Clients =>
new Client[]
{
new Client
{
ClientId = "cwm.client",
ClientName = "Client Credentials Client",
AllowedGrantTypes = GrantTypes.ClientCredentials,
ClientSecrets = { new Secret("secret".Sha256()) },
AllowedScopes = { "myApi.read" }
},
};
}
}
|
Vamos entender o código criado:
1- Definimos um usuário de teste onde os dados estão definidos no código para simplificar o exemplo:
public static
List<TestUser> TestUsers => new List<TestUser> { new TestUser { SubjectId = "1144", Username = "macoratti", Password = "numsey", Claims = { new Claim(JwtClaimTypes.Name, "Macoratti Net"), new Claim(JwtClaimTypes.GivenName, "Macoratti"), new Claim(JwtClaimTypes.FamilyName, "Net"), new Claim(JwtClaimTypes.WebSite, "http://macoratti.net"), } } }; |
No código atribuímos os valores para nome e senha e definimos algumas claims, e, assim teremos como retorno um TestUser com algumas declarações Json Web Tokens (JWT) definidas previamente.
2- A seguir definimos o Recurso usado.
Os recursos de identidade são dados como userId, email, no. de telefone que são dados exclusivos para uma identidade do usuário.
No código incluímos o OpenId e os Recursos de Perfil ou Profile :
public static IEnumerable<IdentityResource>
IdentityResources => new IdentityResource[] { new IdentityResources.OpenId(), new IdentityResources.Profile(), }; |
Nota: O OpenID permite que você use uma conta existente para fazer login em vários sites, sem a necessidade de criar novas senhas.
3- A seguir definimos o escopo da API.
Como nossa intenção é proteger uma API, logo esta API pode ter escopos que neste contexto significa o que o usuário autorizado pode fazer.
Nota: Os escopos representam o que um aplicativo cliente pode fazer e são normalmente modelados como recursos, que vêm em dois tipos: Identidade e API.
No nosso exemplo vamos tratar com 2 escopos por enquanto - leitura, gravação. (Read, Write) e também vamos nomear nossa API como myAPI.
public static IEnumerable<ApiScope>
ApiScopes => new ApiScope[] { new ApiScope("myApi.read"), new ApiScope("myApi.write"), }; |
4- A seguir definimos os recursos da API
Neste código definimos a própria API onde damos a ela o nome de myApi e definimos os escopos suportados junto com o secret, e, aqui , você tem que aplicar um hash neste secret, visto que este código hash será salvo internamente no IdentityServer.
public static IEnumerable<ApiResource>
ApiResources => new ApiResource[] { new ApiResource("myApi") { Scopes = new List<string>{ "myApi.read","myApi.write" }, ApiSecrets = new List<Secret>{ new Secret("supersecret".Sha256()) } } }; |
5- Definindo os usuários e a forma de interação
Finalmente, temos que definir quem terá acesso ao nosso recurso protegido, que em nosso caso é myApi. Para isso vamos fornecer um nome de cliente e uma identificação.
No código estamos definindo GrantType como ClientCredentials sinalizando como o cliente pode interagir com o serviço de token.
public static IEnumerable<Client>
Clients => new Client[] { new Client { ClientId = "cwm.client", ClientName = "Client Credentials Client", AllowedGrantTypes = GrantTypes.ClientCredentials, ClientSecrets = { new Secret("secret".Sha256()) }, AllowedScopes = { "myApi.read" } }, }; |
Registrando o IdentityServer4 na ASP .NET Core
Vamos agora registrar o IdentityServer4 no contêiner DI da ASP.NET Core definindo no método ConfigureServives da classe Startup.cs o código abaixo onde estamos usando todos os recursos estáticos, clientes e usuários que definimos em nossa classe IdentityConfiguration acima:
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace IdentityServer
{
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddIdentityServer()
.AddInMemoryClients(IdentityConfiguration.Clients)
.AddInMemoryIdentityResources(IdentityConfiguration.IdentityResources)
.AddInMemoryApiResources(IdentityConfiguration.ApiResources)
.AddInMemoryApiScopes(IdentityConfiguration.ApiScopes)
.AddTestUsers(IdentityConfiguration.TestUsers)
.AddDeveloperSigningCredential();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/", async context =>
{
await context.Response.WriteAsync("Hello World!");
});
});
}
}
}
|
Definindo armazenamento de configuração na memória e credenciais de assinatura
Como estamos definindo no código as configurações do IdentityServer temos que configurar alguns armazenamento na memória.
Essas configurações são codificadas no projeto HOST e são carregadas apenas uma vez quando o aplicativo é inicializado. Isso é usado principalmente para as fases de desenvolvimento e prototipagem. Esta abordagem também pode ser válida para cenários de produção se a configuração raramente mudar com o tempo.
Outro detalhe é que o IdentityServer precisa de certificados para verificar seu uso. Mas para fins de desenvolvimento e uma vez que não temos nenhum certificado conosco, vamos usar a extensão AddDeveloperSigningCredential().
Nota: Essa extensão cria uma chave temporária no momento da inicialização. A chave gerada será mantida no sistema de arquivos para que permaneça estável entre as reinicializações do servidor (pode ser desativada passando false). Isso funciona e resolve problemas quando os caches de metadados cliente/API ficam fora de sincronia durante o desenvolvimento.
Finalmente no método Configure vamos incluir a linha de código destacada para incluir o middleware do IdentityServer:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseIdentityServer();
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/", async context =>
{
await context.Response.WriteAsync("Hello World!");
});
});
}
|
Concluindo esta etapa podemos executar o projeto.
Para isso altere o servidor de IIS Express para o servidor Kestrel no menu do Visual Studio:
Executando o projeto teremos o resultado abaixo onde vemos o servidor Kestrel inicializar e o navegador abrir exibir a mensagem "Hello World"
Nota: Você também pode rodar no IIS Express. Neste caso a porta vai ser diferente. No meu ambiente a porta é localhost:44306.
Se você chegou até aqui e conseguiu acompanhar e realizar todas as etapas sem erros , parabéns.
Na próxima parte do artigo vamos continuar analisando a documentação do OpenID.
Pegue o projeto
aqui:
AspnIdentityServer4.zip
"Porque não nos
pregamos a nós mesmos, mas a Cristo Jesus, o Senhor; e nós mesmos somos vossos
servos por amor de Jesus.
Porque Deus, que disse que das trevas resplandecesse a luz, é quem resplandeceu
em nossos corações, para iluminação do conhecimento da glória de Deus, na face
de Jesus Cristo."
2 Coríntios 4:5,6
Referências:
ASP .NET Core - Implementando a segurança com
ASP.NET Core MVC - Criando um Dashboard .
C# - Gerando QRCode - Macoratti
ASP .NET - Gerando QRCode com a API do Google
ASP .NET Core 2.1 - Como customizar o Identity
Usando o ASP .NET Core Identity - Macoratti
ASP .NET Core - Apresentando o IdentityServer4
ASP .NET Core 3.1 - Usando Identity de cabo a rabo