- Visão Geral da Solução
Este guia detalha a implementação de um sistema de Single Sign-On (SSO) para aplicações MVC internas, utilizando o IdentityServer4 como provedor de identidade e o protocolo OpenID Connect (OIDC). O objetivo é permitir que clientes MVC autentiquem usuários através de uma página de login centralizada, empregendo o fluxo de concessão implícita (Implicit Grant) do OAuth2.
Para simplificar a integração, a autenticação do cliente MVC será gerenciada por cookies, e faremos uso direto dos serviços de identidade OIDC embutidos no IdentityServer4.
- Conceitos Chave
IdentityResource: Define recursos de identidade conforme especificado pelo protocolo OIDC. Esses recursos possuem um nome exclusivo e contêm declarações (claims) sobre o usuário que são incluídas no token de identidade após a autenticação. Exemplos comuns incluemOpenId(necessário para OIDC) eProfile(para informações básicas do perfil do usuário).IIdentityServerInteractionService: Uma interface fornecida pelo IdentityServer4 que facilita a interação com o servidor de identidade, como o redirecionamento para a página de login ou o processamento de respostas de consentimento. Embora útil para cenários mais complexos, para a configuração básica de cliente MVC com OIDC, seu uso pode ser implícito ou minimizado.
- Configuração do Cliente Web MVC
Para começar, crie um novo projeto de aplicação web MVC. Para este exemplo, o cliente MVC será configurado para rodar no endereço http://localhost:5030.
3.1. Adição de Pacotes NuGet Essenciais
Adicione o seguinte pacote ao seu projeto MVC. Este pacote fornece o middleware necessário para integrar o OpenID Connect com ASP.NET Core:
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="3.1.0" />
Nota: A versão do pacote deve ser compatível com a versão do seu ASP.NET Core. A versão 3.1.0 é um exemplo para IdentityServer4 v4.x.x.
3.2. Registro do Serviço OIDC e Ativação do Middleware de Autenticação
No arquivo Startup.cs do seu projeto MVC, configure os serviços de autenticação e o middleware:
// Método ConfigureServices: Registra os serviços de autenticação
public void ConfigureServices(IServiceCollection servicos)
{
servicos.AddControllersWithViews();
servicos.AddAuthentication(configAutenticacao =>
{
configAutenticacao.DefaultScheme = "Cookies"; // Define o esquema padrão para armazenamento de sessão
configAutenticacao.DefaultChallengeScheme = "oidc"; // Define o esquema para iniciar o desafio OIDC
})
.AddCookie("Cookies", opcoesCookie =>
{
// Configurações adicionais para o cookie de autenticação, se necessário
opcoesCookie.Cookie.Name = "AplicacaoMvcSso";
opcoesCookie.ExpireTimeSpan = TimeSpan.FromMinutes(30);
opcoesCookie.SlidingExpiration = true;
})
.AddOpenIdConnect("oidc", opcoesOidc =>
{
opcoesOidc.SignInScheme = "Cookies"; // Esquema a ser usado para persistir a identidade após o login OIDC
opcoesOidc.Authority = "http://localhost:5010"; // URL base do IdentityServer
opcoesOidc.RequireHttpsMetadata = false; // Desabilitado para ambiente de desenvolvimento HTTP
opcoesOidc.ClientId = "cliente_mvc_app"; // ID do cliente registrado no IdentityServer
opcoesOidc.SaveTokens = true; // Salva os tokens recebidos (access_token, id_token, refresh_token)
opcoesOidc.GetClaimsFromUserInfoEndpoint = true; // Opcional: Busca claims adicionais do UserInfo Endpoint
});
}
// Método Configure: Ativa o middleware de autenticação
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// ... outros middlewares
app.UseRouting();
app.UseAuthentication(); // Deve vir antes do UseAuthorization
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
Para proteger as páginas do cliente MVC, use o atributo [Authorize]. Ao tentar acessar uma página protegida, o usuário será automaticamente redirecionado para a página de login do IdentityServer.
[Authorize]
public class HomeController : Controller
{
// ... métodos do controlador
}
- Configuração do Lado do Servidor (IdentityServer4)
No projeto do IdentityServer, é necessário definir os recursos de identidade (IdentityResource) e registrar o cliente MVC.
4.1. Adição de Recursos de Identidade e Definição do Cliente
No arquivo de configuração do IdentityServer (ex: Config.cs), adicione os recursos de identidade e o cliente:
using IdentityServer4.Models;
using IdentityServer4;
using System.Collections.Generic;
public static class Configuracao
{
// Define os recursos de identidade disponíveis
public static IEnumerable<IdentityResource> ObterRecursosIdentidade()
{
return new List<IdentityResource>
{
new IdentityResources.OpenId(), // Recurso obrigatório para OIDC
new IdentityResources.Profile() // Inclui claims como nome, sobrenome, etc.
};
}
// Define os clientes que podem se conectar ao IdentityServer
public static IEnumerable<Client> ObterClientes()
{
return new List<Client>
{
new Client
{
ClientId = "cliente_mvc_app", // ID do cliente, deve corresponder ao configurado no MVC
ClientName = "Aplicação MVC Interna",
RequireConsent = false, // Desabilita a tela de consentimento para este cliente
AllowedGrantTypes = GrantTypes.Implicit, // Fluxo de concessão implícita
RedirectUris = { "http://localhost:5030/signin-oidc" }, // URI de retorno após o login bem-sucedido
PostLogoutRedirectUris = { "http://localhost:5030/signout-callback-oidc" }, // URI de retorno após o logout
AllowedScopes = new List<string> // Escopos que o cliente pode solicitar
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile
}
}
};
}
// Outros métodos para ApiResources, ApiScopes, TestUsers, etc.
}
No método ConfigureServices do Startup.cs do IdentityServer, certifique-se de registrar esses recursos:
public void ConfigureServices(IServiceCollection servicos)
{
servicos.AddIdentityServer()
.AddDeveloperSigningCredential() // Apenas para desenvolvimento; use certificado em produção
// .AddInMemoryApiResources(Configuracao.ObterRecursosApi()) // Se houver ApiResources
.AddInMemoryIdentityResources(Configuracao.ObterRecursosIdentidade())
.AddInMemoryClients(Configuracao.ObterClientes())
// .AddInMemoryApiScopes(Configuracao.ObterEscoposApi()) // Para IdentityServer4 v3.1+
.AddTestUsers(Configuracao.ObterUsuariosTeste()); // Usuários para teste em memória
}
- Verificação da Autenticação
Para testar a configuração, inicie o projeto do IdentityServer (normalmente em http://localhost:5010) e, em seguida, o projeto do cliente MVC (http://localhost:5030). Ao acessar a página inicial do cliente MVC (se ela estiver protegida com [Authorize]), você será automaticamente redirecionado para a página de login do IdentityServer.
Insira as credenciais de um usuário configurado no Configuracao.ObterUsuariosTeste() do IdentityServer.
5.1. Implementação do Método de Login (Exemplo do IdentityServer)
No controlador de conta do IdentityServer (ex: AccountController.cs), o método de login deve processar as credenciais e autenticar o usuário:
[HttpPost]
public async Task<IActionResult> Entrar(DadosLoginViewModel credenciais, string urlRedirecionamento)
{
ViewData["UrlRetorno"] = urlRedirecionamento;
// Assumindo que _servicoUsuarios é um serviço que gerencia os usuários de teste
var usuarioEncontrado = _servicoUsuarios.BuscarPorNomeUsuario(credenciais.NomeUsuario);
if (usuarioEncontrado == null)
{
ModelState.AddModelError(nameof(credenciais.NomeUsuario), "Usuário não existe.");
return View(credenciais);
}
if (_servicoUsuarios.ValidarCredenciais(credenciais.NomeUsuario, credenciais.Senha))
{
var propriedadesAutenticacao = new AuthenticationProperties
{
IsPersistent = true, // Mantém a sessão do usuário persistente
ExpiresUtc = DateTimeOffset.UtcNow.Add(TimeSpan.FromMinutes(30)) // Expira em 30 minutos
};
var usuarioIdentity = new IdentityServerUser(usuarioEncontrado.SubjectId)
{
DisplayName = usuarioEncontrado.Username
};
// Realiza o login do usuário no contexto HTTP
await HttpContext.SignInAsync(usuarioIdentity, propriedadesAutenticacao);
// Redireciona para a URL original após o login
return Redirect(urlRedirecionamento);
}
ModelState.AddModelError(string.Empty, "Credenciais inválidas.");
return View(credenciais);
}