Symfony Login: facebook OAuth

Quem nunca teve um cliente ou chefe que te pediu aquela tela de login com o "botãozinho" do facebook?

Pois é. Eu também, em um dos projetos na Hefesto, precisei criar o famoso botãozinho e com o Symfony acabei descobrindo que o processo é relativamente simples com a ajudinha de algumas ferramentas e poucas configurações =)

Por onde começar?

O primeiro passo pra criar qualquer sistema de login, é entender o processo de autenticação e autorização criando a representação de um usuário no sistema e guardando os dados dele na sessão.

Facebook

Além disso, vamos precisar de um app registrado no facebook. Isso é feito lá no https://developers.facebook.com/apps. Basta acessar logado na sua conta da rede social

Dashboard do desenvolvedor

É só clickar no botão "Add a New App", dar o nome do seu app e selecionar a opção de login integrado nos cenários padrão que eles fornecem

cenários padrão fornecidos pelo facebook

E, com isso, a gente já tem nosso App Id disponível no canto direito superior.

Esse é o identificador da nossa aplicação para que o facebook autentique nossos usuários e vai ser bem importante em breve.

Mãos na massa

Tendo esse id em mãos, a gente pode começar a explorar a documentação da API de login que é basicamente um tutorial passo-a-passo em inglês mostrando como configurar a conta e usar o SDK pronto, feito em javascript, que a galera do facebook deixou pra comunidade =)

Configurando o app

O primeiro passo, segundo a documentação, é configurar a url de redirecionamento (após o login) da sua aplicação. Isso deve ser feito na seção de configurações do Facebook Login:

configurações do App no facebook

Mais especificamente, no campo Valid OAuth Redirect URIs. No nosso caso, como vamos testar tudo local, nossa aplicação apontará para nosso servidor local mesmo, na porta padrão do Symfony:

Perfeito, agora é só salvar as alterações com o botão Save Changes =)

Usando o SDK do facebook

Tendo a aplicação configurada lá no facebook, a gente pode usar a biblioteca javascript para enviar uma requisição pro facebook e dar inicio ao processo de autenticação.

Importando a biblioteca

Pra isso, basta a gente ter uma página importando a biblioteca com o botão do facebook. Isso pode ser feito importando o seguinte script na página

script de importação do SDK

Esse script só adiciona o javascript na página de forma assíncrona.

Inicializando

Tendo, o SDK carregado, é só inicializar chamando a função FB.init()

script de inicialização do SDK

A função recebe um JSON de configuração com diversas opções. As que nós estamos usando no script são apenas um conjunto básico que eu acredito ser mínimo viável.

Mas, aqui vale uma explorada na documentação porque tem bastante coisa legal =)

Chamando a API

Para fazer a chamada da API, precisamos verificar se o usuário já está logado no nosso App ou não, do lado do facebook.

Para isso, usamos a função FB.getLoginStatus() que retorna se o usuário está conectado ou não.

Se o usuário estiver conectado, podemos redirecionar ele para alguma URL no nosso back-end pra que ele passe pela camada de autenticação do Symfony, indicando quem é o usuário pro sistema guardar na sessão na url "/oauth/login", que vamos definir mais pra frente.

script de login chamando o SDK do facebook

Caso contrario, a gente chama a função FB.login(), que vai direcionar o usuário pro lado de autenticação do facebook, antes de chegar na gente =)

E o botãozinho?

Além disso, é importante colocar, finalmente, o botãozinho na tela! Assim, quando o usuário clickar no botão, disparamos nossa função fb_login() que vai cuidar do processo todo.

E podemos juntar tudo isso em qualquer página de login. Inclusive, até mesmo em paralelo com a autenticação por formulário do Symfony!

login.html.twig

Assim, você tem tanto o login OAuth quanto o nativo o que te da muito mais flexibilidade para tomar decisões diferentes para usuários de origens diferentes =)

Beleza, e como autentica no back-end?

Aqui, assim como qualquer processo de autenticação, pode acabar ficando um pouco mais complexo. Pra simplificar, vamos usar uma ferramenta maravilhosa chamada HWIO Auth Bundle.

A ferramenta abstrai toda essa camada de autenticação implementando o protocolo OAuth pra gente e integrando com diversas plataformas, inclusive o facebook!

Cada versão do bundle tem uma compatibilidade especifica com algumas versões do Symfony, por isso é importante instalar a versão correta. No nosso caso, vamos usar o Symfony 4 então ficamos com a versão 0.6 do bundle.

Instalando o HWIO Auth Bundle

O primeiro passo é sempre instalar. Então, seguindo a própria documentação do bundle, basta rodar os comandos

composer require php-http/guzzle6-adapter:^1.1
composer require php-http/httplug-bundle:1.13
composer require hwi/oauth-bundle:^0.6.3

É importante executar especificando as versões pra evitar conflito =)

Configurando o bundle

Graças ao flex disponível a partir da versão 4 do Symfony, boa parte dos arquivos de configuração já foram criados. Como o hwi_oauth_routing.yaml

https://gist.github.com/andrechavesg/ba5cccba42991abb4f74d2f34d2dbe4a

Que cria algumas rotas para autenticação.

Aqui é importante notar que o arquivo gerado traz a rota /login como padrão.

Porém, se você, como eu, precisar autenticar pelo formulário padrão do Symfony também, é interessante trocar essa rota para /oauth/login para evitar conflitos. Essa é a mesma rota que a gente definiu lá atrás no script do SDK =)

hwi_oauth_routing.yaml

Além do xml de rotas, também foi criado o hwi_oauth.yaml

whi_oauth.yaml

Aqui, configuramos quais são os plugins que queremos. Ele já vem como do facebook por padrão =)

Além disso, já temos o apontamento do id do nosso App! Apenas precisamos definir lá no arquivo .env

.env com os parâmetros do facebook

Além do App id definido na variável FB_ID, temos a FB_SECRET que fica disponível lá nas configurações básicas do seu App do facebook

https://developers.facebook.com/apps/225654845054108/settings/basic/

Basta clickar em Show para exibir o hash e colar na variável FB_SECRET

User provider

Perfeito, com isso temos quase tudo configurado. Mas, até agora, onde a gente recebeu os dados da API pra encontrar/criar nosso Usuario com os dados da rede social?

Exatamente, em lugar nenhum. Esse processo é basicamente indicar pro bundle um serviço provedor de usuarios (user provider) baseado nos dados retornados pela API.

Configurando

Antes de criar a classe que vai definitivamente receber os dados, é importante apontar essa configuração na camada de autenticação do symfony (security.yaml).

Pra isso, basta apontar a configuração oauth, dentro do firewall

https://gist.github.com/andrechavesg/dd52b5c032baf95e281a58bbbc07f393

No nosso caso, temos o firewall main, e dentro dele apontamos a configuração do oauth =)

Um ponto importante, é a url informada no resource_owners, que é gerado automaticamente com a chave do facebook apontando pra url /login/check-facebook, porém essa url não existe no bundle.

Depois de dar uma leve googlada descobri que nessa versão do bundle, a url correta é /connect/service/facebook. Portanto, é importante alterar a configuração automática. Nosso security.yaml ficará algo como

security.yaml final

Além disso, aqui já temos que indicar qual será o serviço responsável pelo processamento do usuário na chave oauth_user_provider passando o valor oauth_user_provider.

Criando o serviço de autenticação

Por isso, agora é importante criar definitivamente o serviço oauth_user_provider lá no services.yaml

https://gist.github.com/andrechavesg/7e992a090a82d820df5773a2c38496bb

Aqui a gente já apontou pra classe no namespace App\Security\OAuthUserProvider.

Portanto, o último passo, é criar finalmente a classe OAuthUserProvider implementando as interfaces UserProviderInterface do Symfony e OAuthAwareUserProviderInterface do nosso querido bundle =)

OAuthUserProvider.php

A parte importante de verdade dessa classe é o método loadUserByOAuthUserResponse, responsável por receber os dados após a autenticação no servidor do facebook!

Ali a gente tem acesso ao response, uma variável responsável por guardar as informações do usuário autenticado

dados do usuário autenticado

Aqui, a gente consegue acessar coisas como nome, sobrenome, email, apelido e até foto do perfil!

Ou seja, é nesse momento que você monta seu usuário baseado nos dados que vieram da autenticação OAuth =)

E pronto!

Com isso, já podemos subir o servidor e acessar a url de login localhost:8000/login com o nosso formulário. E, ao clickar no famoso botãozinho temos a tela de autenticação do facebook!

Tela de login OAuth do facebook

E ao clickar em continuar, somos redirecionados já autenticados pelo Symfony, com os dados do facebook =)

usuário autenticado

Considerações finais

O processo de autenticação com OAuth não é muito diferente do processo de autenticação padrão do Symfony.

A grande diferença entre um e o outro é a chamada do SDK em javascript no front que não é necessária na autenticação padrão e o bundle HWIO para processar a requisição de volta.

Uma outra solução é não usar o bundle e processar o request na mão. Porém, com ele a gente tem a vantagem de, tendo esse ambiente configurado, apenas apontar as outras configurações no firewall do security.yaml assim como fizemos com o facebook.

Sim, daqui pra frente se você quiser fazer autenticação com qualquer plataforma da lista disponível na documentação, incluindo google, github, instagram, linkedin, e vários outros, basta abrir o link e seguir o tutorial de configuração e pronto!

Uma flexibilidade bem difícil de se alcançar fazendo a autenticação na mão =)

Próximos passos

No próximo post, vamos falar um pouco mais sobre criptografia como foi sugerido pelo Jonatas no último post.

E ai, o que você achou da autenticação OAuth como facebook no Symfony? Conhece alguma outra forma ou tem alguma outra sugestão de tema pra um post? Compartilha aqui com a gente nos comentários!