Integración del inicio de sesión de Facebook en la aplicación AngularJS con Satellizer

Publicado: 2022-03-11

Con la llegada de marcos front-end ricos en funciones como AngularJS, se implementa cada vez más lógica en el front-end, como manipulación/validación de datos, autenticación y más. Satellizer, un módulo de autenticación basado en token fácil de usar para AngularJS, simplifica el proceso de implementación del mecanismo de autenticación en AngularJS. La biblioteca viene con soporte integrado para Google, Facebook, LinkedIn, Twitter, Instagram, GitHub, Bitbucket, Yahoo, Twitch y cuentas de Microsoft (Windows Live).

En este artículo, crearemos una aplicación web muy simple similar a la que se muestra aquí, que le permite iniciar sesión y ver la información del usuario actual.

Autenticación vs Autorización

Estas son 2 palabras aterradoras que a menudo encuentras una vez que tu aplicación comienza a integrar un sistema de usuario. Según Wikipedia:

La autenticación es el acto de confirmar la veracidad de un atributo de un solo dato (un dato) declarado verdadero por una entidad.

La autorización es la función de especificar derechos de acceso a los recursos relacionados con la seguridad de la información y la seguridad informática en general y al control de acceso en particular.

En términos sencillos, tomemos un ejemplo de un sitio web de blog con algunas personas trabajando en él. Los bloggers escriben artículos y el administrador valida el contenido. Cada persona puede autenticarse (iniciar sesión) en el sistema, pero sus derechos (autorización) son diferentes, por lo que el blogger no puede validar el contenido mientras que el administrador sí.

¿Por qué Satellizer?

Puedes crear tu propio sistema de autenticación en AngularJS siguiendo algunos tutoriales como este muy detallado: JSON Web Token Tutorial: An Example in Laravel and AngularJS. Sugiero leer este artículo ya que explica muy bien JWT (JSON Web Token) y muestra una forma sencilla de implementar la autenticación en AngularJS utilizando directamente el almacenamiento local y los interceptores HTTP.

Entonces, ¿por qué Satellizer? La razón principal es que admite un puñado de inicios de sesión de redes sociales como Facebook, Twitter, etc. Hoy en día, especialmente para los sitios web que se usan en dispositivos móviles, escribir el nombre de usuario y la contraseña es bastante engorroso y los usuarios esperan poder usar su sitio web con pocos obstáculos. mediante el uso de inicios de sesión sociales. Como integrar el SDK de cada red social y seguir sus documentaciones es bastante repetitivo, sería bueno admitir estos inicios de sesión sociales con un mínimo esfuerzo.

Además, Satellizer es un proyecto activo en Github. Active es clave aquí, ya que estos SDK cambian con bastante frecuencia y no desea leer su documentación de vez en cuando (cualquiera que trabaje con Facebook SDK sabe lo molesto que es)

Aplicación AngularJS con inicio de sesión de Facebook

Aquí es donde las cosas empiezan a ponerse interesantes.

Construiremos una aplicación web que tenga un mecanismo regular de inicio de sesión/registro (es decir, usando nombre de usuario, contraseña) y que también admita inicios de sesión sociales. Esta webapp es muy simple ya que solo tiene 3 páginas:

  • Página de inicio: cualquiera puede ver
  • Página de inicio de sesión: para ingresar nombre de usuario/contraseña
  • Página secreta: que solo los usuarios registrados pueden ver

Para el backend, usaremos Python y Flask. Python y el marco Flask son bastante expresivos, así que espero que trasladar el código a otros lenguajes/marcos no sea muy difícil. Por supuesto, usaremos AngularJS para el front-end. Y para los inicios de sesión sociales, nos integraremos solo con Facebook, ya que es la red social más popular en este momento.

¡Empecemos!

Paso #1: Proyecto Bootstrap

Así es como estructuraremos nuestro código:

 - app.py - static/ - index.html - app.js - bower.json - partials/ - login.tpl.html - home.tpl.html - secret.tpl.html

Todo el código de back-end está en app.py. El código de front-end se coloca en la carpeta static/. De forma predeterminada, Flask entregará automáticamente el contenido de la carpeta static/. Todas las vistas parciales están en static/partials/ y son manejadas por el módulo ui.router.

Para comenzar a codificar el back-end, necesitaremos Python 2.7.* e instalaremos las bibliotecas necesarias mediante pip. Por supuesto, puede usar virtualenv para aislar un entorno de Python. A continuación se muestra la lista de módulos de Python necesarios para colocar en requirements.txt:

 Flask==0.10.1 PyJWT==1.4.0 Flask-SQLAlchemy==1.0 requests==2.7.0

Para instalar todas estas dependencias:

 pip install -r requirements.txt

En app.py tenemos un código inicial para arrancar Flask (las declaraciones de importación se omiten por brevedad):

 app = Flask(__name__) @app.route('/') def index(): return flask.redirect('/static/index.html') if __name__ == '__main__': app.run(debug=True)

A continuación, iniciamos Bower e instalamos AngularJS y 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

Una vez que estas bibliotecas estén instaladas, debemos incluir AngularJS y ui-router en index.html y crear enrutamientos para 3 páginas: inicio, inicio de sesión y secreto.

 <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>

A continuación se muestra el código que necesitamos en main.js para configurar el enrutamiento:

 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'); });

En este punto, si ejecuta el servidor python app.py , debería tener esta interfaz básica en http://localhost:5000

Interfaz básica de inicio de sesión

Los enlaces Inicio, Inicio de sesión y Secreto deberían funcionar en este punto y mostrar el contenido de las plantillas correspondientes.

¡Felicitaciones, acabas de terminar de configurar el esqueleto! Si encuentra algún error, consulte el código en GitHub

Paso #2: Inicie sesión y regístrese

Al final de este paso, tendrá una aplicación web en la que podrá registrarse/iniciar sesión con el correo electrónico y la contraseña.

El primer paso es configurar el backend. Necesitamos un modelo de usuario y una forma de generar el token JWT para un usuario determinado. El modelo de usuario que se muestra a continuación está realmente simplificado y no realiza ni siquiera comprobaciones básicas, como si el correo electrónico del campo contiene "@", o si la contraseña del campo contiene al 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 el módulo jwt en python para generar la parte de carga útil en JWT. Las partes iat y exp corresponden a la marca de tiempo en que se crea y expira el token. En este código, el token caducará en 2 semanas.

Después de que se creó el usuario modelo, podemos agregar los puntos finales de "inicio de sesión" y "registro". El código para ambos es bastante similar, así que aquí solo mostraré la parte de "registro". Tenga en cuenta que, de manera predeterminada, Satellizer llamará a los puntos finales /auth/login y /auth/signup para "iniciar sesión" y "registrarse" 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())

Primero verifiquemos el punto final usando curl:

 curl localhost:5000/auth/signup -H "Content-Type: application/json" -X POST -d '{"email":"[email protected]","password":"xyz"}'

El resultado debería verse así:

 { "token": "very long string…." }

Ahora que la parte de back-end está lista, ¡ataquemos el front-end! Primero, necesitamos instalar satellizer y agregarlo como una dependencia en main.js:

 bower install satellizer --save

Agregue satelizer como dependencia:

 var app = angular.module('DemoApp', ['ui.router', 'satellizer']);

Iniciar sesión y registrarse en satellizer es bastante simple en comparación con toda la configuración hasta ahora:

 $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); }) };

Si tiene alguna dificultad para configurar el código, puede echar un vistazo al código en GitHub.

Paso #3: Pero la vista secreta no es realmente secreta, ¡porque cualquiera puede verla!

¡Si, eso es correcto! Hasta ahora, cualquiera puede ir a la página secreta sin iniciar sesión.

Es hora de agregar algún interceptor en AngularJS para asegurarse de que si alguien va a la página secreta y si este usuario no ha iniciado sesión, será redirigido a la página de inicio de sesión.

En primer lugar, debemos agregar un indicador de inicio de sesión requerido para distinguir la página secreta de otras.

 .state('secret', { url: '/secret', templateUrl: 'partials/secret.tpl.html', controller: 'SecretCtrl', data: {requiredLogin: true} })

La parte de "datos" se usará en el evento $stateChangeStart que se activa cada vez que cambia el enrutamiento:

 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'); } }); });

Ahora, el usuario no puede ir directamente a la página secreta sin iniciar sesión. ¡Hurra!

Como de costumbre, el código de este paso se puede encontrar aquí.

Paso #4: ¡Es hora de obtener algo realmente secreto!

En este momento, no hay nada realmente secreto en la página secreta. Pongamos algo personal ahí.

Este paso comienza con la creación de un punto final en el back-end al que solo puede acceder un usuario autenticado, por ejemplo, con un token válido. El punto final /usuario a continuación devuelve el ID de usuario y el correo electrónico del usuario correspondiente al 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

Nuevamente, usamos el módulo jwt para decodificar el token JWT incluido en el encabezado 'Autorización' y para manejar el caso cuando el token está vencido o no es válido.

Probemos este punto final usando curl. Primero, necesitamos obtener un token válido:

 curl localhost:5000/auth/signup -H "Content-Type: application/json" -X POST -d '{"email":"[email protected]","password":"xyz"}'

Luego con este token:

 curl localhost:5000/user -H "Authorization: Bearer {put the token here}"

Lo que da este resultado:

 { "email": "[email protected]", "id": 1 }

Ahora necesitamos incluir este punto final en el controlador secreto. Esto es bastante simple ya que solo necesitamos llamar al punto final usando el módulo $http regular. El token se inserta automáticamente en el encabezado por Satellizer, por lo que no necesitamos preocuparnos por todos los detalles de guardar el token y luego colocarlo en el encabezado correcto.

 getUserInfo(); function getUserInfo() { $http.get('/user') .then(function (response) { $scope.user = response.data; }) .catch(function (response) { console.log("getUserInfo error", response); }) }

¡Finalmente, tenemos algo verdaderamente personal en la página secreta!

La página secreta, que muestra el correo electrónico y la identificación del usuario.

El código de este paso está en GitHub.

Paso #5: Iniciar sesión en Facebook con Satellizer

Lo bueno de Satellizer, como se mencionó al principio, es que facilita mucho la integración del inicio de sesión social. ¡Al final de este paso, los usuarios pueden iniciar sesión con su cuenta de Facebook!

Autenticación OAuth de Facebook.

Lo primero que debe hacer es crear una aplicación en la página de desarrolladores de Facebook para tener un application_id y un código secreto. Vaya a developer.facebook.com/docs/apps/register para crear una cuenta de desarrollador de Facebook si aún no tiene una y cree una aplicación de sitio web. Después de eso, tendrá el ID de la aplicación y el secreto de la aplicación como se muestra en la siguiente captura de pantalla.

Obtener el secreto de la aplicación.

Una vez que el usuario elige conectarse con Facebook, Satellizer enviará un código de autorización al punto final /auth/facebook . Con este código de autorización, el back-end puede recuperar un token de acceso desde el punto final de Facebook /oauth que permite la llamada a la API Graph de Facebook para obtener información del usuario, como la ubicación, los amigos del usuario, el correo electrónico del usuario, etc.

También debemos realizar un seguimiento de si una cuenta de usuario se crea con Facebook o mediante un registro regular. Para hacerlo, agregamos facebook_id a nuestro modelo de usuario.

 facebook_id = db.Column(db.String(100))

El secreto de Facebook se configura a través de las variables env FACEBOOK_SECRET que agregamos a app.config .

 app.config['FACEBOOK_SECRET'] = os.environ.get('FACEBOOK_SECRET')

Entonces, para iniciar app.py , debe configurar esta variable env:

 FACEBOOK_SECRET={your secret} python app.py

Este es el método que maneja los inicios de sesión de Facebook. De manera predeterminada, Satellizer llamará al punto final /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 una solicitud al servidor de Facebook, utilizamos el práctico módulo de solicitudes. Ahora la parte difícil en el back-end está hecha. En el front-end, agregar el inicio de sesión de Facebook es bastante simple. Primero, necesitamos decirle a Satellizer nuestro facebook_id agregando este código en la función 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 iniciar sesión usando Facebook, simplemente podemos llamar a:

 $auth.authenticate(“facebook”)

Como de costumbre, puedes consultar el código en GitHub

En este momento, la aplicación web está completa en términos de funcionalidad. El usuario puede iniciar sesión/registrarse usando un correo electrónico y contraseña regulares o usando Facebook. Una vez iniciada la sesión, el usuario puede ver su página secreta.

hacer una bonita interfaz

La interfaz no es muy bonita en este punto, así que agreguemos un poco de Bootstrap para el diseño y el módulo de tostadora angular para manejar bien un mensaje de error, como cuando falla el inicio de sesión.

El código para esta parte embellecedora se puede encontrar aquí.

Conclusión

Este artículo muestra una integración paso a paso de Satellizer en una aplicación web AngularJS (simple). Con Satellizer, podemos agregar fácilmente otros inicios de sesión sociales como Twitter, Linkedin y más. El código en el front-end es bastante el mismo que en el artículo. Sin embargo, el back-end varía ya que los SDK de redes sociales tienen diferentes puntos finales con diferentes protocolos. Puede consultar https://github.com/sahat/satellizer/blob/master/examples/server/python/app.py que contiene ejemplos para Facebook, Github, Google, Linkedin, Twiter y Bitbucket. En caso de duda, debe consultar la documentación en https://github.com/sahat/satellizer.

Relacionado: Inicio de sesión con un clic con Blockchain: un tutorial de MetaMask