sábado, 28 de julho de 2007

OpenID

OpenID Passei essa semana quebrando a cabeça no trabalho para desenvolver um protótipo de módulo relying party em Lua.

Relying party – também chamado consumer – é um dos nós que participam de uma autenticação OpenID.

O que é OpenID?


OpenID é um sistema livre descentralizado de autenticação e autorização centrado no usuário.

Ou seja, atualmente em cada sistema com autenticação, você precisa fazer um cadastro. Então você usuário é obrigado a manter uma redundância inconveniente de dados, que eventualmente precisam ser atualizados.

Para piorar os sistemas centralizam dados de todos os usuários, inclusive as senhas. Daí você usuário precisa ter uma senha para cada sistema, tendo de lembrar toda uma fauna de sEnH45 complicadas – ou de 123mudar.

Alguns arriscam sua identidade digital usando a mesma senha para tudo, o que não é aconselhável nem de longe.

OpenID surge em meio a essa realidade como uma solução para o problema.

Você usuário centraliza todos seus dados e sua senha em um único servidor de sua confiança, chamado server-agent ou OpenID provider.

Então você pode autenticar-se em sistemas que ofereçam suporte a OpenID – chamados relying parties ou consumers – sem que o sistema conheça sua senha, apenas fornecendo uma URL conhecida como identidade OpenID (identity). Ainda mais: quando for atualizar seus dados, só é preciso atualizar no agente servidor, pois não há redundância.

É um sistema descentralizado porque você não é obrigado a cadastrar seus dados no sistema onde deseja ser autenticado.

Protocolo


Há ferramentas prontas para Python, JSP, Ruby e PHP.

No entanto Lua é uma linguagem com uma comunidade muito pequena ainda para reagir tão rápido às novidades. Considerando-se ainda a filosofia da linguagem – SIMPLE: simple, light-weight and extensible, simples, leve e extensível –, é compreensível a necessidade de desenvolver os próprios módulos.

Se por um lado é inconveniente, por outro, se você realmente gosta de programar, Lua se torna uma linguagem muito divertida.

Em miúdos: se você trabalha com programação mas não gosta, faz isso só pra ganhar dinheiro, pare de ler por aqui e vai trabalhar. Espere alguém fazer um módulo por você para poder usar OpenID. =P


Continuando então o artigo – agora para programadores de verdade e curiosos –, o problema para desenvolver um módulo consumidor é entender o protocolo.

O protocolo OpenID negocia tunelado dentro do protocolo HTTP.

Resumidamente, a lógica é a seguinte:
  1. O cliente (navegador) acessa o consumidor;
  2. O consumidor envia um formulário ao cliente solicitando sua identidade OpenID;
  3. O cliente informa ao consumidor sua identidade;
  4. O consumidor normaliza a identidade – fica algo como http://user.example.com/;
  5. O consumidor acessa via HTTP a URL da identidade e pega o código HTML;
  6. O consumidor procura na URL marcadores link com atributos rel contendo openid.server, openid.delegate, openid2.provider e openid2.local_id – esses marcadores informam através do atributo href quem é o agente servidor e qual a identidade real do cliente;
  7. O consumidor acessa via HTTP o agente servidor em busca das chaves de comunicação – esse processo é chamado associação;
  8. Tendo sido feita a associação, o consumidor usa as chaves para redirecionar o cliente para a página de autenticação do agente servidor;
  9. O cliente se autentica no agente servidor informando sua senha, se já não estiver atenticado;
  10. O agente servidor pergunta ao cliente se ele autoriza o consumidor a ter acesso a seu perfil;
  11. Se o cliente permitir, o agente servidor redireciona o cliente para o consumidor, passando os dados solicitados.


Vamos então para o primeiro passo que precisamos implementar:

Formulário


O action do formulário deve apontar para o script que irá negociar com o agente servidor e precisa ter uma entrada texto para a identidade.

Esta entrada geralmente tem a identificação openid_login e seu formato CSS padrão pode ser encontrado aqui.

Normalização


O próximo passo já é executado no script.

Ele deve pegar a URL e normalizá-la.

Isto é feito assim:
  • =id não muda;
  • xri://=id vira =id;
  • xri://$dns*id.ex.com vira http://id.ex.com/;
  • xri://$ip*a.b.c.d vira http://a.b.c.d/;
  • http://id.ex.com/ e https://id.ex.com/ não muda;
  • id.ex.com vira http://id.ex.com/.


Na verdade estamos aqui neste artigo interessados apenas nas normalizações que geram URLs HTTP e HTTPS.

Associação


Na associação o consumidor acessa o agente servidor via HTTP.

O agente servidor é obtido de um desses marcadores tirados da consulta ao HTML da identidade:
  • <link rel="openid.server" href="http://example.com/server" />
  • <link rel="openid2.provider" href="http://example.com/server" />
  • <link rel="openid.server openid2.provider" href="http://example.com/server" />


Observação: para uma consulta HTTP em Lua você precisa da função socket.http.request() de LuaSocket.

Na associação envie ao agente servidor via GET os seguintes parâmetros:
  • openid.mode=associate
  • openid.assoc_type=HMAC-SHA1
  • openid.sesion_type=no-encryption


Quanto a criptografia, o ideal é usar DH-SHA1 (Diffie Hellman), mas, até que alguém faça um módulo de baixo nível para Lua de gerenciamento de chaves grandes, Lua é muito lenta para lidar com tais chaves e somos obrigados a trabalhar sem criptografia. =(

Feita esta consulta, o agente servidor deve retornar duas chaves no corpo:
  • assoc_handle – «maçaneta» (alguma tradução melhor?) de associação;
  • mac_key – chave de código de autenticação de mensagem.


Guarde essas informações!

Redirecionamento


Agora é preciso redirecionar o cliente para a página de login do agente servidor.

Faça isso contruindo uma URL apontando para o agente servidor e levando os seguintes parâmetros (via GET mesmo):
  • openid.ns – indica a versão de OpenID usada, para 2.0: http://specs.openid.net/auth/2.0
  • openid.mode=checkid_setup
  • openid.identity – a identidade do cliente
  • openid.claimed_id – novamente a identidade
  • openid.assoc_handle – a «maçaneta» de associação recebida
  • openid.return_to – a URL do script que irá tratar a resposta


Depois disso o cliente vai negociar diretamente com o agente servidor.

Recebendo de volta


No final o cliente será redirecionado de volta para o consumidor, mais especificamente para a URL informada por openid.return_to.

Esse script receberá parâmetros POST, sendo o principal openid.mode, que, se for id_res, o cliente está autenticado e autorizou o consumidor.

Outros parâmetros


Todos os parâmetros podem ser encontrados na especificação do protocolo.

TODO


Este artigo aborda o básico do básico da atenticação.

Com ele ainda não é possível recolher dados de SRE (informações de perfil), que deveriam ser passados nos subparâmetros de openid.sreg.

Mas para tanto é preciso usar criptografia – pelo que entendi, posso estar errado.

De qualquer forma, aqui está uma boa explicação do funcionamento do protocolo OpenID.

[]'s
Cacilhas
blog comments powered by Disqus