Integrando o login do Facebook no aplicativo AngularJS com o Satellizer
Publicados: 2022-03-11Com a chegada de estruturas de front-end ricas em recursos, como AngularJS, mais e mais lógica está sendo implementada no front-end, como manipulação/validação de dados, autenticação e muito mais. Satellizer, um módulo de autenticação baseado em token fácil de usar para AngularJS, simplifica o processo de implementação do mecanismo de autenticação em AngularJS, A biblioteca vem com suporte embutido para Google, Facebook, LinkedIn, Twitter, Instagram, GitHub, Bitbucket, Yahoo, Twitch e contas da Microsoft (Windows Live).
Neste artigo, vamos construir um webapp muito simples semelhante ao aqui que permite que você faça o login e veja as informações do usuário atual.
Autenticação x autorização
Estas são duas palavras assustadoras que você costuma encontrar quando seu aplicativo começa a integrar um sistema de usuário. Segundo a Wikipédia:
Autenticação é o ato de confirmar a verdade de um atributo de um único dado (datum) declarado verdadeiro por uma entidade.
A autorização é a função de especificar direitos de acesso a recursos relacionados à segurança da informação e segurança de computadores em geral e ao controle de acesso em particular.
Em termos leigos, vamos dar um exemplo de um site de blog com algumas pessoas trabalhando nele. Os blogueiros escrevem artigos e o gerente valida o conteúdo. Cada pessoa pode se autenticar (login) no sistema, mas seus direitos (autorização) são diferentes, então o blogueiro não pode validar o conteúdo enquanto o gerente pode.
Por que Satellizer
Você pode criar seu próprio sistema de autenticação em AngularJS seguindo alguns tutoriais como este bem detalhado: Tutorial JSON Web Token: Um Exemplo em Laravel e AngularJS. Sugiro a leitura deste artigo, pois ele explica muito bem o JWT (JSON Web Token) e mostra uma maneira simples de implementar a autenticação no AngularJS usando diretamente o armazenamento local e os interceptores HTTP.
Então, por que Satellizer? A principal razão é que ele suporta um punhado de logins de redes sociais como Facebook, Twitter, etc. Hoje em dia, especialmente para sites usados em dispositivos móveis, digitar nome de usuário e senha é bastante complicado e os usuários esperam poder usar seu site com poucos obstáculos usando logins sociais. Como integrar o SDK de cada rede social e seguir suas documentações é bastante repetitivo, seria bom dar suporte a esses logins sociais com o mínimo de esforço.
Além disso Satellizer é um projeto ativo no Github. Ativo é fundamental aqui, pois esses SDKs mudam com bastante frequência e você não quer ler sua documentação de vez em quando (qualquer pessoa que trabalhe com o SDK do Facebook sabe como é irritante)
Aplicativo AngularJS com login no Facebook
É aqui que as coisas começam a ficar interessantes.
Construiremos um aplicativo da web que possui um mecanismo de login/registro regular (ou seja, usando nome de usuário, senha) e também suporta logins sociais. Este webapp é muito simples, pois possui apenas 3 páginas:
- Página inicial: qualquer um pode ver
- Página de login: para inserir nome de usuário/senha
- Página secreta: que apenas usuários logados podem ver
Para backend, usaremos Python e Flask. Python e o framework Flask são bastante expressivos, então espero que a portabilidade do código para outras linguagens/frameworks não seja muito difícil. Obviamente, usaremos o AngularJS para front-end. E para os logins sociais, faremos integração apenas com o Facebook, pois é a rede social mais popular no momento.
Vamos começar!
Etapa 1: Projeto Bootstrap
Veja como estruturaremos nosso código:
- app.py - static/ - index.html - app.js - bower.json - partials/ - login.tpl.html - home.tpl.html - secret.tpl.html
Todo o código de back-end está em app.py . O código do front-end é colocado na pasta static/. Por padrão, o Flask servirá automaticamente o conteúdo de static/ folder. Todas as visualizações parciais estão em static/partials/ e são tratadas pelo módulo ui.router.
Para começar a codificar o back-end, precisaremos do Python 2.7.* e instalaremos as bibliotecas necessárias usando pip. É claro que você pode usar virtualenv para isolar um ambiente Python. Abaixo está a lista de módulos Python necessários para colocar em requirements.txt:
Flask==0.10.1 PyJWT==1.4.0 Flask-SQLAlchemy==1.0 requests==2.7.0
Para instalar todas essas dependências:
pip install -r requirements.txt
Em app.py temos algum código inicial para inicializar o Flask (as instruções de importação são omitidas por questões de brevidade):
app = Flask(__name__) @app.route('/') def index(): return flask.redirect('/static/index.html') if __name__ == '__main__': app.run(debug=True)
Em seguida, iniciamos o bower e instalamos o AngularJS e o ui.router :
bower init # here you will need to answer some question. when in doubt, just hit enter :) bower install angular angular-ui-router --save # install and save these dependencies into bower.json
Uma vez que essas bibliotecas são instaladas, precisamos incluir AngularJS e ui-router em index.html e criar roteamentos para 3 páginas: home, login e secret.
<body ng-app="DemoApp"> <a ui-sref="home">Home</a> <a ui-sref="login">Login</a> <a ui-sref="secret">Secret</a> <div ui-view></div> <script src="bower_components/angular/angular.min.js"></script> <script src="bower_components/angular-ui-router/release/angular-ui-router.min.js"></script> <script src="main.js"></script> </body>
Abaixo está o código que precisamos no main.js para configurar o roteamento:
var app = angular.module('DemoApp', ['ui.router']); app.config(function ($stateProvider, $urlRouterProvider) { $stateProvider .state('home', { url: '/home', templateUrl: 'partials/home.tpl.html' }) .state('secret', { url: '/secret', templateUrl: 'partials/secret.tpl.html', }) .state('login', { url: '/login', templateUrl: 'partials/login.tpl.html' }); $urlRouterProvider.otherwise('/home'); });
Neste ponto, se você executar o servidor python app.py , deverá ter essa interface básica em http://localhost:5000
Os links Home, Login e Secret devem funcionar neste ponto e mostrar o conteúdo dos templates correspondentes.
Parabéns, você acabou de montar o esqueleto! Se você encontrar algum erro, verifique o código no GitHub
Passo #2: Faça login e cadastre-se
Ao final desta etapa, você terá um webapp que poderá registrar/login usando e-mail e senha.
O primeiro passo é configurar o back-end. Precisamos de um modelo de usuário e uma maneira de gerar o token JWT para um determinado usuário. O modelo de usuário mostrado abaixo é realmente simplificado e não realiza nenhuma verificação básica, como se o campo de email contém “@”, ou se a senha do campo contém pelo menos 6 caracteres, etc.
class User(db.Model): id = db.Column(db.Integer, primary_key=True) email = db.Column(db.String(100), nullable=False) password = db.Column(db.String(100)) def token(self): payload = { 'sub': self.id, 'iat': datetime.utcnow(), 'exp': datetime.utcnow() + timedelta(days=14) } token = jwt.encode(payload, app.config['TOKEN_SECRET']) return token.decode('unicode_escape')
Usamos o módulo jwt em python para gerar a parte da carga útil no JWT. A parte iat e exp correspondem ao carimbo de data/hora em que o token é criado e expirado. Neste código, o token expirará em 2 semanas.
Após a criação do usuário do modelo, podemos adicionar os endpoints “login” e “register”. O código para ambos são bem parecidos, então aqui vou mostrar apenas a parte do “registro”. Observe que, por padrão, o Satellizer chamará os endpoints /auth/login e /auth/signup para o “login” e “register”, respectivamente.
@app.route('/auth/signup', methods=['POST']) def signup(): data = request.json email = data["email"] password = data["password"] user = User(email=email, password=password) db.session.add(user) db.session.commit() return jsonify(token=user.token())
Vamos verificar o endpoint usando curl primeiro:
curl localhost:5000/auth/signup -H "Content-Type: application/json" -X POST -d '{"email":"[email protected]","password":"xyz"}'
O resultado deve ficar assim:
{ "token": "very long string…." }
Agora que a parte de back-end está pronta, vamos atacar o front-end! Primeiro, precisamos instalar o satelizer e adicioná-lo como dependência em main.js:
bower install satellizer --save
Adicione o satélite como dependência:
var app = angular.module('DemoApp', ['ui.router', 'satellizer']);
O login e a inscrição no satelizer são bastante simples em comparação com toda a configuração até agora:
$scope.signUp = function () { $auth .signup({email: $scope.email, password: $scope.password}) .then(function (response) { // set the token received from server $auth.setToken(response); // go to secret page $state.go('secret'); }) .catch(function (response) { console.log("error response", response); }) };
Se você tiver alguma dificuldade em configurar o código, pode dar uma olhada no código no GitHub.

Passo #3: Mas a visão secreta não é realmente secreta, porque qualquer um pode vê-la!
Sim, está correto! Até agora, qualquer pessoa pode acessar a página secreta sem fazer login.
É hora de adicionar algum interceptor no AngularJS para garantir que se alguém for para a página secreta e se esse usuário não estiver logado, será redirecionado para a página de login.
Primeiro, devemos adicionar um sinalizador requiredLogin para distinguir a página secreta de outras.
.state('secret', { url: '/secret', templateUrl: 'partials/secret.tpl.html', controller: 'SecretCtrl', data: {requiredLogin: true} })
A parte “data” será usada no evento $stateChangeStart que é acionado toda vez que o roteamento muda:
app.run(function ($rootScope, $state, $auth) { $rootScope.$on('$stateChangeStart', function (event, toState) { var requiredLogin = false; // check if this state need login if (toState.data && toState.data.requiredLogin) requiredLogin = true; // if yes and if this user is not logged in, redirect him to login page if (requiredLogin && !$auth.isAuthenticated()) { event.preventDefault(); $state.go('login'); } }); });
Agora, o usuário não pode ir diretamente para a página secreta sem fazer login. Viva!
Como de costume, o código desta etapa pode ser encontrado aqui.
Passo #4: É hora de conseguir algo realmente secreto!
Neste momento, não há nada realmente secreto na página secreta. Vamos colocar algo pessoal lá.
Esta etapa começa criando um endpoint no back-end que só é acessível para um usuário autenticado, como ter um token válido. O endpoint /user abaixo retorna o user_id e o email do usuário correspondente ao token.
@app.route('/user') def user_info(): # the token is put in the Authorization header if not request.headers.get('Authorization'): return jsonify(error='Authorization header missing'), 401 # this header looks like this: “Authorization: Bearer {token}” token = request.headers.get('Authorization').split()[1] try: payload = jwt.decode(token, app.config['TOKEN_SECRET']) except DecodeError: return jsonify(error='Invalid token'), 401 except ExpiredSignature: return jsonify(error='Expired token'), 401 else: user_id = payload['sub'] user = User.query.filter_by(id=user_id).first() if user is None: return jsonify(error='Should not happen ...'), 500 return jsonify(id=user.id, email=user.email), 200 return jsonify(error="never reach here..."), 500
Novamente, usamos o módulo jwt para decodificar o token JWT incluído no cabeçalho 'Authorization' e para lidar com o caso em que o token expirou ou não é válido.
Vamos testar esse endpoint usando curl. Primeiro, precisamos obter um token válido:
curl localhost:5000/auth/signup -H "Content-Type: application/json" -X POST -d '{"email":"[email protected]","password":"xyz"}'
Então com este token:
curl localhost:5000/user -H "Authorization: Bearer {put the token here}"
O que dá este resultado:
{ "email": "[email protected]", "id": 1 }
Agora precisamos incluir este endpoint no Secret Controller. Isso é bastante simples, pois só precisamos chamar o endpoint usando o módulo $http regular. O token é inserido automaticamente no cabeçalho pelo Satellizer, portanto, não precisamos nos preocupar com todos os detalhes de salvar o token e colocá-lo no cabeçalho correto.
getUserInfo(); function getUserInfo() { $http.get('/user') .then(function (response) { $scope.user = response.data; }) .catch(function (response) { console.log("getUserInfo error", response); }) }
Finalmente, temos algo verdadeiramente pessoal na página secreta!
O código desta etapa está no GitHub.
Passo #5: Login no Facebook com Satellizer
Uma coisa legal sobre o Satellizer, como mencionado no início, é que torna a integração do login social muito mais fácil. Ao final desta etapa, os usuários podem fazer login usando sua conta do Facebook!
A primeira coisa a fazer é criar um aplicativo na página de desenvolvedores do Facebook para ter um application_id e um código secreto. Siga developers.facebook.com/docs/apps/register para criar uma conta de desenvolvedor do Facebook, caso ainda não tenha uma, e crie um aplicativo de site. Depois disso, você terá o ID do aplicativo e o segredo do aplicativo, como na captura de tela abaixo.
Assim que o usuário optar por se conectar ao Facebook, o Satellizer enviará um código de autorização para o endpoint /auth/facebook . Com esse código de autorização, o back-end pode recuperar um token de acesso do endpoint /oauth do Facebook que permite a chamada para a API do Facebook Graph para obter informações do usuário, como localização, user_friends, e-mail do usuário etc.
Também precisamos acompanhar se uma conta de usuário é criada com o Facebook ou por meio de inscrição regular. Para fazer isso, adicionamos facebook_id ao nosso modelo User.
facebook_id = db.Column(db.String(100))
O segredo do facebook é configurado por meio das variáveis de ambiente FACEBOOK_SECRET que adicionamos ao app.config .
app.config['FACEBOOK_SECRET'] = os.environ.get('FACEBOOK_SECRET')
Então, para iniciar o app.py , você deve definir esta variável env:
FACEBOOK_SECRET={your secret} python app.py
Aqui está o método que lida com logins do Facebook. Por padrão, o Satellizer chamará o endpoint /auth/facebook .
@app.route('/auth/facebook', methods=['POST']) def auth_facebook(): access_token_url = 'https://graph.facebook.com/v2.3/oauth/access_token' graph_api_url = 'https://graph.facebook.com/v2.5/me?fields=id,email' params = { 'client_id': request.json['clientId'], 'redirect_uri': request.json['redirectUri'], 'client_secret': app.config['FACEBOOK_SECRET'], 'code': request.json['code'] } # Exchange authorization code for access token. r = requests.get(access_token_url, params=params) # use json.loads instead of urlparse.parse_qsl access_token = json.loads(r.text) # Step 2. Retrieve information about the current user. r = requests.get(graph_api_url, params=access_token) profile = json.loads(r.text) # Step 3. Create a new account or return an existing one. user = User.query.filter_by(facebook_id=profile['id']).first() if user: return jsonify(token=user.token()) u = User(facebook_id=profile['id'], email=profile['email']) db.session.add(u) db.session.commit() return jsonify(token=u.token())
Para enviar uma solicitação ao servidor do Facebook, usamos os práticos pedidos de módulo. Agora a parte difícil no back-end está feita. No front-end, adicionar o login do Facebook é bastante simples. Primeiro, precisamos informar ao Satellizer nosso facebook_id adicionando este código na função app.config :
$authProvider.facebook({ clientId: {your facebook app id}, // by default, the redirect URI is http://localhost:5000 redirectUri: 'http://localhost:5000/static/index.html' });
Para fazer login usando o Facebook, basta chamar:
$auth.authenticate(“facebook”)
Como de costume, você pode verificar o código no GitHub
Neste momento, o webapp está completo em termos de funcionalidade. O usuário pode fazer o login/registro usando e-mail e senha normais ou usando o Facebook. Uma vez logado, o usuário pode ver sua página secreta.
Faça uma interface bonita
A interface não é muito bonita neste ponto, então vamos adicionar um pouco de Bootstrap para o layout e o módulo de torradeira angular para lidar bem com uma mensagem de erro, como quando o login falha.
O código para esta parte de embelezamento pode ser encontrado aqui.
Conclusão
Este artigo mostra uma integração passo a passo do Satellizer em um aplicativo web AngularJS (simples). Com o Satellizer, podemos adicionar facilmente outros logins sociais, como Twitter, Linkedin e muito mais. O código no front-end é praticamente o mesmo que no artigo. No entanto, o back-end varia, pois os SDKs de redes sociais têm endpoints diferentes com protocolos diferentes. Você pode dar uma olhada em https://github.com/sahat/satellizer/blob/master/examples/server/python/app.py que contém exemplos para Facebook, Github, Google, Linkedin, Twiter e Bitbucket. Em caso de dúvida, você deve dar uma olhada na documentação em https://github.com/sahat/satellizer.