API REST para site que usa Facebook para autenticação

86

Temos um site onde a única forma de fazer o login e autenticar-se no site é com o Facebook (não foi minha escolha). Na primeira vez que você faz login no Facebook, uma conta é criada automaticamente para você.

Agora queremos criar um aplicativo para iPhone para nosso site e também uma API pública para que outros usem nosso serviço.

Esta questão é sobre como autenticar nosso site a partir do aplicativo / API e está dividida em 2 partes:

  1. Qual é a maneira correta de lidar com a autenticação REST de uma API para um site que usa apenas Facebook OAuth como método de autenticação?

    Eu li e pesquisei muito sobre métodos padrão de autenticação para API REST. Não podemos usar métodos como autenticação básica sobre HTTPS , pois não há credenciais para um usuário como tal. Algo como este parece ser apenas para a autenticação de aplicações usando a API.

    Atualmente, a melhor maneira que posso pensar é que você atinge um ponto final / autorizar em nossa API, ele redireciona para o OAuth do Facebook, depois redireciona de volta para o site e fornece um 'token' que o usuário da API pode usar para autenticar subsequentemente solicitações de.

  2. Para um aplicativo oficial que criamos, não precisaríamos necessariamente usar a API pública da mesma maneira. Qual seria a melhor maneira de falar com nosso site e autenticar usuários?

Eu entendo (eu acho) como autenticar aplicativos de terceiros que estão usando nossa API, usando chaves API (públicas) e chaves secretas (privadas). No entanto, quando se trata de autenticar o usuário que está usando o aplicativo, estou ficando um tanto confuso sobre como fazer isso, quando a única maneira de autenticar um usuário é o Facebook.

Sinto que estou perdendo algo muito óbvio ou não entendo totalmente como as APIs REST públicas devem funcionar, então qualquer conselho e ajuda seriam muito apreciados.

Adão
fonte
Tenho uma pergunta semelhante em stackoverflow.com/questions/30230482/… que fala sobre alguns deles
JVK

Respostas:

99

ATUALIZAÇÃO: veja abaixo

Também tenho pensado muito sobre essa questão. Ainda não está totalmente claro para mim, mas aqui está o caminho que estou pensando em seguir. Estou criando uma API REST com autenticação apenas para meus usuários no Facebook Connect.

No CLIENTE:

  1. Use a API do Facebook para fazer login e obter um código OAUTH2.
  2. Troque este código por um token de acesso.
  3. Em cada chamada para minha API personalizada, incluirei o ID do usuário do Facebook e o token de acesso.

Na API (para cada método que requer autenticação do usuário):

  1. Faça uma solicitação para o gráfico / me do Facebook usando o token de acesso acima.
  2. Verifique se o ID do usuário do Facebook retornado corresponde ao ID do usuário passado para minha API acima.
  3. Se o token de acesso expirou, é necessária uma comunicação adicional.

Eu ainda tenho que testar isso. Como isso soa?

--- Atualização: 27 de julho de 2014 para responder à pergunta ---

Eu só uso a troca acima uma vez no login. Depois de determinar qual usuário está fazendo login, crio meu próprio token de acesso, e esse token é usado desse ponto em diante. Então, o novo fluxo se parece com este ...

No CLIENTE:

  1. Use a API do Facebook para fazer login e obter um código OAUTH2.
  2. Troque este código por um token de acesso.
  3. Solicite um token de acesso de minha API, incluindo o token do Facebook como parâmetro

Na API

  1. Receba solicitação de token de acesso.
  2. Faça uma solicitação para o gráfico / me do Facebook usando o token de acesso do Facebook
  3. Verifique se o usuário do Facebook existe e corresponde a um usuário em meu banco de dados
  4. Criar meu próprio token de acesso, salvá-lo e devolvê-lo ao cliente para ser usado a partir deste ponto em diante
Chris Greenwood
fonte
Ei, essa é uma resposta tardia, mas sua solução parece corrigir o problema que tive na minha primeira resposta. É claro que você teria que usar HTTPS para não enviar o par (id, token) na transmissão em texto não criptografado. Mas parece que deve funcionar!
Olivier Lance
Acho bom me ligar no servidor e gerar seu próprio token de acesso, que será usado pelo cliente móvel.
Der_Meister
Descobri que não é uma prática armazenar o segredo do FB no aplicativo móvel. Avisos do Facebook para armazená-lo apenas em seu servidor. developers.facebook.com/docs/opengraph/using-actions/…
Der_Meister
Se enviarmos user_id e access_token para o servidor API todas as vezes (como post / get params). Ele criará uma falha de segurança se alguém conseguir interceptar a conexão?
Nathan Do
@NathanDo Use HTTPS entre o seu cliente e o servidor API e não deve ser um problema se alguém interceptar a conexão (vulnerabilidades do tipo Heartbleed à parte).
dcr
15

Esta é minha implementação usando JWTs (JSON Web Tokens), basicamente semelhante à resposta atualizada de Chris. Usei o SDK JS do Facebook e JWT.

Aqui está minha implementação.

  1. Cliente: Use o SDK JS do Facebook para fazer login e obter o token de acesso.

  2. Cliente: Solicite JWT da minha API chamando o /verify-access-tokenendpoint.

  3. MyAPI: Recebe o token de acesso, verifique-o chamando o /meendpoint da API do Facebook.

  4. MyAPI: se o token de acesso for válido, encontra o usuário no banco de dados e efetua login no usuário, se existir Crie um JWT com os campos obrigatórios como carga útil, defina uma validade, assine com a chave secreta e envie de volta ao cliente.

  5. Cliente: armazena o JWT no armazenamento local.

  6. Cliente: envia o token (o JWT da etapa 5) junto com a solicitação para a próxima chamada de API.

  7. MyAPI: valide o token com a chave secreta, se o token for válido, troque o token por um novo, envie de volta para o cliente junto com a resposta da API. (Nenhuma chamada de API externa para verificação do token aqui depois) [se o token for inválido / expirado, solicitar ao cliente para autenticar novamente e repetir a partir de 1]

  8. Cliente Substitui o token armazenado pelo novo e use-o na próxima chamada de API. Uma vez que a expiração do token é atendida, o token expira revogando o acesso à API.

Cada token é usado uma vez.

Leia mais respostas sobre segurança e JWT

Quão seguro é o JWT

Se você pode decodificar o JWT, como eles são seguros?

JSON Web Tokens (JWT) como tokens de autenticação e identificação do usuário

All ѕ Vаиітy
fonte
3
Acho que o nº 3 deveria ser /debug_token, portanto, você pode verificar se o token é realmente para o seu aplicativo.
Peppe LG
2
Não solicite access_tokenno Cliente. Use "fluxo de trabalho de código". Passe codepara MyAPI e fazer outra viagem de volta para o Facebook para troca codecom access_token. Isso é explicado com mais detalhes aqui: developers.facebook.com/docs/facebook-login/security
omikron
5

Estou tentando responder a mesma pergunta e tenho lido muito recentemente ...

Não terei "a" resposta, mas as coisas estão ficando um pouco mais claras para mim. Você leu os comentários no artigo que mencionou ? Eu os achei muito interessantes e úteis.

Como resultado, e à luz de como as coisas evoluíram desde que o primeiro artigo foi escrito, eis o que acho que farei:

  • HTTPS em todos os lugares - isso permite que você esqueça HMAC, assinatura, nonce, ...

  • Use OAuth2:

    • Quando as solicitações de autenticação vêm de meus próprios aplicativos / site, use este 'truque' (ou uma variação dele) descrito em uma resposta ao artigo mencionado antes.

    • No meu caso, tenho dois tipos de usuários: aqueles com credenciais de login / senha clássicas e aqueles que se inscreveram no Facebook Connect.
      Então, eu forneceria um formulário de login regular com um botão "Login com Facebook". Se o usuário fizer login com suas credenciais "clássicas", eu simplesmente as enviaria para meu ponto de extremidade OAuth2 com um grant_type=password.
      Se ele decidir fazer login pelo Facebook, acho que seria um processo de duas etapas:

      • Primeiro, use o Facebook iOS SDK para abrir uma FBSession
      • Quando isso for feito e o aplicativo tiver o controle de volta, deve haver uma maneira de obter um ID do Facebook para esse usuário. Eu enviaria este ID sozinho para meu endpoint OAuth2 com uma concessão de extensão entendida por meu servidor como "usando um ID de usuário FB".

Por favor, note que eu ainda estou pesquisando muito sobre todas essas coisas, então essa pode não ser uma resposta perfeita ... talvez nem mesmo a correta! Mas acho que seria um bom ponto de partida. A ideia de usar uma "concessão de extensão" para a autenticação do Facebook pode envolver ter que registrá-lo para fazer as coisas corretamente? Não tenho muita certeza.

De qualquer forma, espero ter podido ajudá-lo ainda um pouco, e que pelo menos possa iniciar uma discussão para encontrar a melhor solução para este problema :)

Atualizar
O login do Facebook não é uma solução conforme apontado nos comentários: qualquer pessoa pode enviar um ID de usuário arbitrário e fazer login como esse usuário na API.

Que tal fazer assim:

  • Mostra um formulário de login com um botão "login do Facebook"
  • Se este método de login for escolhido, aja mais ou menos como o SDK do Facebook: abra uma página da web do seu servidor de autenticação, que iniciará o login do Facebook.
  • Assim que o usuário fizer o login, o Facebook usará seu URL de redirecionamento para confirmar; fazer esse URL apontar para outro ponto de extremidade do seu servidor de autenticação (possivelmente com um parâmetro extra indicando que a chamada veio de um aplicativo?)
  • Quando o endpoint de autenticação é atingido, a autenticação pode identificar com segurança o usuário, reter seu ID de usuário FB / Sessão FB e retornar um token de acesso ao seu aplicativo usando um esquema de URL personalizado, assim como o SDK do Facebook faria

Parece melhor?

Olivier Lance
fonte
1
Obrigado por sua resposta! Eu tinha pensado sobre a maior parte do que você disse, mas a principal preocupação que tive ao usar o FB iOS SDK e, em seguida, enviar o ID do usuário do Facebook era que não seria fácil enviar qualquer ID que você deseja para o seu endpoint API e alegar ser outro usuário? É aqui que eu geralmente acabo travando.
Adam
De fato! Foi estúpido da minha parte, não pensar nisso ... Então a solução tem que passar pelo seu próprio servidor de autenticação de alguma forma ...
Olivier Lance
Atualizei minha resposta com outra ideia de solução ... mas acabei de perceber que você a mencionou em sua pergunta original! Não consigo ver mais nada no momento ...
Olivier Lance