A integração entre ASP.NET Identity e FIDO2 é um processo complexo. Nesse post será explorado e resolvido essa complexidade. Assim você poderá adicionar FIDO2 em suas aplicações ASP.NET.

Neste post será implementado um fluxo Passwordless, onde não é necessário a senha do usuário, com ASP.NET Identity.

Resultado final

Ao final desse post essa aplicação será construída:

Obrigado FEITIAN

Esse post só foi possivel graças a FEITIAN que me disponibilizou um device FIDO2 para testes.

Essa demo foi inteiramenta desenvolvida utilizando esse modelo BioPass FIDO Security Key

BioPass FIDO Security Key

FIDO2 e WebAuthn

FIDO2 e WebAuthn são padrões de autenticação aberto. Suportado pelos navegadores e implementado por grandes empresas de tecnologia, como Microsoft, Google etc.

O objetivo principal é permitir que um usuário faça login sem senhas, criando fluxos sem senha ou com MFA.

O padrão não se limita a aplicativos da Web. Tem suporte para Active Directory e aplicativos nativos.

A tecnologia é baseado em chaves públicas/privadas, permitindo que a autenticação aconteça sem compartilhar um segredo entre o usuário e a plataforma. Isso traz muitos benefícios, como logins mais fáceis, seguros e torna as tentativas de phishing extremamente difíceis.

ASP.NET Identity

O ASP.NET Identity é um componente para gerenciamento de usuários. É práticamente um Standard no ecossistema .NET para controle de usuários.

SHOW ME THE CODE

Essa solução utiliza os componentes fido2-net-lib. É um componente que passou em todos os testes conformidade ao padrão FIDO2. Ou seja, é um componente que segue a risca todos os quesitos de segurança necessários.

O problema desse componente reside no fato dele não ser amigável para ser armazenado num banco de dados. É necessário fazer muitas conversões para que o EntityFramework, por exemplo, armazene e recupere o estado dos objetos que o fido1-net-lib necessita.

Para contornar esse problema será utilizado o NetDevPack.Fido2.EntityFramework.Store. Um wrapper que resolve o problema de armazenamento dos componentes do fido2-net-lib

Criando a solution

Abra o Visual Studio 2022 e crie um novo projeto do tipo ASP.NET Core Web App (Model-View-Controller) com nome de Fido2.Passwordless.

Não esqueça de adicionar suporte a usuários no step Additional Information.

Assim que carregar a solução, adicione as dependencias do Fido2.AspNet e NetDevPack.Fido2.EntityFramework.Store (Botão direito em cima da Solution > Add Nuget Packages).

Abra o Program.cs e localize a configuração do ApplicationDbContext. Abra essa classe e altere de acordo com o código abaixo:

Esse código é a implementação do NetDevPack.Fido2.EntityFramework.Store. Essa classe CredentialStore é quem irá armazenar a chave pública das security keys FIDO2.

Ainda em Program.cs adicione as configurações do Fido2

Nesse trecho está sendo informado ao ASP.NET para configurar Fido2 e também configurar NetDevPack.Fido2

O Fido2 também é dependente de uma configuração que vai no appsettings.json

Certifique que a porta que ASP.NET associou a sua aplicação é a mesma que está no appsettings.json. Para isso verifique o arquivo launchSettings.json na pasta Properties.

Controller, View e js

Agora vem a parte pesada, um monte de código que ao final de cada um, será dado a explicação das partes mais importantes.

Crie a seguinte estrutura:

  1. Adicione uma Controller PasswordlessController.cs
  2. Crie uma Class na pasta Model chamado PasswordlessModel.cs
  3. Adicione duas Views na pasta Views/Passwordless
    • Index.cshtml
    • Login.cshtml
  4. Adicione dois arquivos javascript em wwwroot/js/
    • register.passwordless.js
    • login.passwordless.js
    • helpers.js

O resultado final será esse:

Estrutura

PasswordlessController.cs

Na controller PasswordlessController.cs adicione o código conforme a seguir.

Pontos importantes:

O processo de Registrar novo usuário tem dois pontos importantes:

  1. GetAttestationOptions Esse método cria os parâmetros necessários para enviar ao Security Key, além de verificar se o Usuário já não possui uma chave cadastrada no site. Esses parametros são enviados ao Security Key através do WebAuthn via Javascript. Esses parametros vão instruir o device a criar uma nova chave privada e enviar a chave pública de volta para a aplicação. Que vai receber esse parametros no método Index(PasswordlessModel model, string returnUrl).

  2. O método Index(PasswordlessModel model, string returnUrl) recebe a resposta que o Security Key enviou ao site através do Javascript, que por sua vez faz um POST para esse método. Ele confronta as informações enviadas com aquelas que salvou em "Memória" no método GetAttestationOptions e também faz a validação da Assinatura que o Security Key fez através da chave pública que ele recebeu.

  3. GetAssertionOptions Esse método cria os parâmetros necessários para o security key apenas validar o usuário, ou seja, efetuar login. O security key por sua vez recebe esses parâmetros, gera uma assinatura através da Chave Privada e envia ao Javascript através do WebAuthn.

  4. O método Login(PasswordlessModel.LoginModel model) recebe a assinatura que foi gerado pelo Security Key. Então busca a chave pública desse usuário que foi informado no Site e tenta validar a Assinatura. Se a chave pública desse usuário validar essa assinatura, significa que o Security Key utilizado é de fato desse usuário, portanto efetua o Login.

register.passwordless.js

O arquivo javascript register.passwordless.js terá o seguinte código.

Os pontos mais importantes são:

  1. O js intercepta o click do botão Register logo na primeira linha do Js. Assim ele faz um GET no método GetAttestationOptions passando as informações do usuário, como Email e Nome. Esse método como descrito acima, gera os parametros que deverá ser enviado ao Security Key através do WebAuthn. O método também faz um parse de algumas propriedades para adequar ao padrão WebAuthn e chama o Security Key na linha 66
newCredential = await navigator.credentials.create({
    publicKey: makeCredentialOptions
});

Assim que o Security Key responde o browser, o javascript pega a resposta, põe num campo Hidden e faz um POST do form para o método Index(PasswordlessModel model, string returnUrl). Esse método além de cadastrar a chave pública do usuário, cria esse usuário com o ASP.NET Identity e segue o fluxo padrão do ASP.NET Identity.

login.passwordless.js

O arquivo javascript login.passwordless.js terá o seguinte código.

Os pontos mais importantes são:

  1. O js intercepta o click do botão Login logo na primeira linha do Js. Assim ele faz um GET no método GetAssertionOptions passando apenas o login do usuário, nesse caso o e-mail. Esse método como descrito acima, gera os parametros que deverá ser enviado ao Security Key através do WebAuthn. O método também faz um parse de algumas propriedades para adequar ao padrão WebAuthn e chama o Security Key na linha 64
credential = await navigator.credentials.get({ publicKey: makeAssertionOptions })

Assim que o Security Key responde o browser, o javascript pega a resposta, põe num campo Hidden e faz um POST do form para o método Login(PasswordlessModel.LoginModel login, string returnUrl). Esse método verifica se a chave público do usuário é capaz de validar a assinatura da chave privada desse Security Key. Se conseguir validar, significa que o Security Key é de fato do usuário, portanto ele pode logar.

Demais arquivos

Os demais arquivos é um bundle de código HTML e a Model utilizado na Controller.

  • Index.cshtml
  • Login.cshtml
  • PasswordlessModel.cs
  • helpers.js

E por fim, altere o arquivo _Layout.cshtml para adicionar as rotas para essas páginas.

Conclusão

A integração é trabalhosa, principalmente se você abrir mão de utilizar o NetDevPack.Fido2.EntityFramework.Store, afinal além de lidar com toda complexidade do Fido2, ainda tem que converter suas classes e armazenar em algum lugar. A exceção, seria se estiver utilizando um banco NoSQL, como o MongoDb, por exemplo.

Mas no final, é um amontoado de código para comunicar via javascript com o WebAuthn e devolver a resposta para o ASP.NET Core.

Os demos oficiais não ajudam ao excluir o ASP.NET Identity das demos, dificultando a adoção. A maioria de nós, que vamos utilizar uma abordagem Passwordless, não podemos simplesmente abrir mão da Senha em nossas aplicações por um motivo óbvio: Quantos usuários possuem uma Security Key?

Então precisamos coexistir com o ASP.NET Identity e FIDO2.

Referências