Интеграция входа через Facebook в приложение AngularJS с помощью Satellizer

Опубликовано: 2022-03-11

С появлением многофункциональных интерфейсных фреймворков, таких как AngularJS, все больше и больше логики внедряется во внешний интерфейс, например манипулирование/проверка данных, аутентификация и многое другое. Satellizer, простой в использовании модуль аутентификации на основе токенов для AngularJS, упрощает процесс реализации механизма аутентификации в AngularJS. Библиотека поставляется со встроенной поддержкой Google, Facebook, LinkedIn, Twitter, Instagram, GitHub, Bitbucket, Yahoo, Twitch. и учетные записи Microsoft (Windows Live).

В этой статье мы создадим очень простое веб-приложение, подобное приведенному здесь, которое позволит вам войти в систему и просмотреть информацию о текущем пользователе.

Аутентификация против авторизации

Это два страшных слова, с которыми вы часто сталкиваетесь, когда ваше приложение начинает интегрировать пользовательскую систему. Согласно Википедии:

Аутентификация — это действие по подтверждению истинности атрибута отдельного фрагмента данных (данного), который объект считает истинным.

Авторизация — это функция указания прав доступа к ресурсам, связанным с информационной и компьютерной безопасностью в целом и с разграничением доступа в частности.

С точки зрения непрофессионала, давайте возьмем пример веб-сайта блога, над которым работают несколько человек. Блогеры пишут статьи, а менеджер проверяет их содержание. Каждый человек может аутентифицироваться (войти в систему) в системе, но их права (авторизация) различны, поэтому блоггер не может проверять контент, а менеджер может.

Почему Сателлайзер

Вы можете создать свою собственную систему аутентификации в AngularJS, следуя некоторым учебным пособиям, таким как очень подробное: Учебное пособие по JSON Web Token: пример в Laravel и AngularJS. Я предлагаю прочитать эту статью, так как она очень хорошо объясняет JWT (веб-токен JSON) и показывает простой способ реализации аутентификации в AngularJS с использованием непосредственно локального хранилища и перехватчиков HTTP.

Итак, почему Satellizer? Основная причина заключается в том, что он поддерживает несколько входов в социальные сети, такие как Facebook, Twitter и т. д. В настоящее время, особенно для веб-сайтов, используемых на мобильных устройствах, ввод имени пользователя и пароля довольно громоздкий, и пользователи ожидают, что смогут использовать ваш веб-сайт без особых помех. с помощью социальных логинов. Поскольку интеграция SDK каждой социальной сети и следование их документации довольно повторяются, было бы неплохо поддерживать эти социальные входы с минимальными усилиями.

Более того, Satellizer является активным проектом на Github. Активность здесь ключевая, так как эти SDK меняются довольно часто, и вы не хотите время от времени читать их документацию (любой, кто работает с Facebook SDK, знает, как это раздражает).

Приложение AngularJS с входом через Facebook

Вот тут-то и начинается самое интересное.

Мы создадим веб-приложение, которое имеет обычный механизм входа/регистрации (т. е. с использованием имени пользователя и пароля), а также поддерживает вход через социальные сети. Это веб-приложение очень простое, так как в нем всего 3 страницы:

  • Домашняя страница: каждый может видеть
  • Страница входа: для ввода имени пользователя/пароля
  • Секретная страница: ее могут видеть только авторизованные пользователи

Для серверной части мы будем использовать Python и Flask. Python и фреймворк Flask довольно выразительны, поэтому я надеюсь, что портирование кода на другие языки/фреймворки не составит большого труда. Мы, конечно же, будем использовать AngularJS для внешнего интерфейса. А для входа в социальные сети мы будем интегрироваться только с Facebook, поскольку в настоящее время это самая популярная социальная сеть.

Давайте начнем!

Шаг № 1: Bootstrap-проект

Вот как мы будем структурировать наш код:

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

Весь внутренний код находится в app.py. Интерфейсный код помещается в папку static/. По умолчанию Flask автоматически обслуживает содержимое папки static/. Все частичные представления находятся в static/partials/ и обрабатываются модулем ui.router.

Чтобы начать программировать серверную часть, нам потребуется Python 2.7.* и установить необходимые библиотеки с помощью pip. Конечно, вы можете использовать virtualenv для изоляции среды Python. Ниже приведен список необходимых модулей Python для добавления в файл requirements.txt:

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

Чтобы установить все эти зависимости:

 pip install -r requirements.txt

В app.py у нас есть исходный код для начальной загрузки Flask (операторы импорта опущены для краткости):

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

Затем мы инициализируем Bower и устанавливаем AngularJS и 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

После установки этих библиотек нам нужно включить AngularJS и ui-router в index.html и создать маршруты для 3 страниц: домашняя, логин и секрет.

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

Ниже приведен код, который нам понадобится в main.js для настройки маршрутизации:

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

На этом этапе, если вы запускаете сервер python app.py , у вас должен быть этот базовый интерфейс по адресу http://localhost:5000.

Основной интерфейс входа

Ссылки Home, Login и Secret должны работать на этом этапе и отображать содержимое соответствующих шаблонов.

Поздравляю, вы только что закончили настройку скелета! Если вы столкнулись с какой-либо ошибкой, проверьте код на GitHub.

Шаг № 2: Войдите и зарегистрируйтесь

В конце этого шага у вас будет веб-приложение, в котором вы можете зарегистрироваться/войти, используя адрес электронной почты и пароль.

Первым шагом является настройка бэкенда. Нам нужна модель пользователя и способ создания токена JWT для данного пользователя. Модель пользователя, показанная ниже, действительно упрощена и не выполняет даже каких-либо базовых проверок, таких как наличие в поле электронной почты «@» или наличие в поле пароля не менее 6 символов и т. д.

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

Мы используем модуль jwt в python для создания части полезной нагрузки в JWT. Части iat и exp соответствуют временной метке создания и истечения срока действия токена. В этом коде срок действия токена истекает через 2 недели.

После создания модели User мы можем добавить конечные точки «login» и «register». Код для обоих очень похож, поэтому здесь я просто покажу часть «регистрации». Обратите внимание, что по умолчанию Satellizer будет вызывать конечные точки /auth/login и /auth/signup для «логина» и «регистрации» соответственно.

 @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())

Давайте сначала проверим конечную точку с помощью curl:

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

Результат должен выглядеть так:

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

Теперь, когда серверная часть готова, давайте приступим к фронтенду! Во-первых, нам нужно установить satellizer и добавить его как зависимость в main.js:

 bower install satellizer --save

Добавьте сателлизер в качестве зависимости:

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

Вход и регистрация в Satellizer на самом деле довольно просты по сравнению со всеми настройками до сих пор:

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

Если у вас возникли трудности с настройкой кода, вы можете взглянуть на код на GitHub.

Шаг № 3: Но секретный вид на самом деле не секрет, потому что его может увидеть любой!

Да, это правильно! До сих пор любой желающий может зайти на секретную страницу без входа в систему.

Пришло время добавить какой-нибудь перехватчик в AngularJS, чтобы убедиться, что если кто-то перейдет на секретную страницу и если этот пользователь не вошел в систему, он будет перенаправлен на страницу входа.

Во-первых, мы должны добавить флаг requiredLogin, чтобы отличать секретную страницу от других.

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

Часть «данные» будет использоваться в событии $stateChangeStart, которое запускается каждый раз при изменении маршрута:

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

Теперь пользователь не может напрямую перейти на секретную страницу без входа в систему. Ура!

Как обычно, код этого шага можно найти здесь.

Шаг № 4: Пришло время получить что-то действительно секретное!

На данный момент на секретной странице нет ничего действительно секретного. Давайте поместим туда что-то личное.

Этот шаг начинается с создания конечной точки в серверной части, которая доступна только для аутентифицированного пользователя, например, имеющего действительный токен. Конечная точка /user ниже возвращает user_id и адрес электронной почты пользователя, соответствующего токену.

 @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

Опять же, мы используем модуль jwt для декодирования токена JWT, включенного в заголовок «Авторизация», и для обработки случая, когда срок действия токена истек или он недействителен.

Давайте проверим эту конечную точку с помощью curl. Во-первых, нам нужно получить действительный токен:

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

Затем с этим токеном:

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

Что дает этот результат:

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

Теперь нам нужно включить эту конечную точку в Secret Controller. Это довольно просто, так как нам просто нужно вызвать конечную точку, используя обычный модуль $http. Токен автоматически вставляется в заголовок Satellizer, поэтому нам не нужно заморачиваться со всеми подробностями сохранения токена и последующего размещения его в правильном заголовке.

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

Наконец-то у нас есть что-то действительно личное на секретной странице!

Секретная страница с адресом электронной почты и идентификатором пользователя.

Код этого шага находится на GitHub.

Шаг № 5: Вход через Facebook через Satellizer

Как упоминалось в начале, Satellizer хорош тем, что он значительно упрощает интеграцию входа через социальные сети. В конце этого шага пользователи могут войти в систему, используя свою учетную запись Facebook!

OAuth-аутентификация Facebook.

Первое, что нужно сделать, это создать приложение на странице разработчиков Facebook, чтобы иметь application_id и секретный код. Пожалуйста, перейдите по ссылке Developers.facebook.com/docs/apps/register, чтобы создать учетную запись разработчика Facebook, если у вас ее еще нет, и создать приложение для веб-сайта. После этого у вас будет идентификатор приложения и секрет приложения, как на скриншоте ниже.

Получение секрета приложения.

Как только пользователь решит подключиться к Facebook, Satellizer отправит код авторизации на конечную точку /auth/facebook . С помощью этого кода авторизации серверная часть может получить токен доступа из конечной точки Facebook /oauth , который позволяет вызову Facebook Graph API получать информацию о пользователе, такую ​​как местоположение, user_friends, адрес электронной почты пользователя и т. д.

Нам также необходимо отслеживать, создается ли учетная запись пользователя через Facebook или посредством обычной регистрации. Для этого мы добавляем facebook_id в нашу модель пользователя.

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

Секрет facebook настраивается через переменные env FACEBOOK_SECRET, которые мы добавляем в app.config .

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

Итак, чтобы запустить app.py , вы должны установить эту переменную env:

 FACEBOOK_SECRET={your secret} python app.py

Вот метод, который обрабатывает вход в Facebook. По умолчанию Satellizer вызывает конечную точку /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())

Для отправки запроса на сервер Facebook воспользуемся удобным модулем Requests. Теперь трудная часть на бэк-энде сделана. Во внешнем интерфейсе добавить логин Facebook довольно просто. Во-первых, нам нужно сообщить Satellizer наш facebook_id , добавив этот код в функцию 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' });

Чтобы войти с помощью Facebook, мы можем просто позвонить:

 $auth.authenticate(“facebook”)

Как обычно, вы можете проверить код на GitHub.

На данный момент веб-приложение завершено с точки зрения функциональности. Пользователь может войти/зарегистрироваться, используя обычную электронную почту и пароль или используя Facebook. После входа в систему пользователь может увидеть свою секретную страницу.

Сделать красивый интерфейс

На данный момент интерфейс не очень красивый, поэтому давайте добавим немного Bootstrap для макета и модуль углового тостера, чтобы хорошо обрабатывать сообщения об ошибках, например, при сбое входа.

Код для этой украшающей части можно найти здесь.

Заключение

В этой статье показана пошаговая интеграция Satellizer в (простое) веб-приложение AngularJS. С помощью Satellizer мы можем легко добавить другие социальные сети, такие как Twitter, Linkedin и другие. Код на фронтенде точно такой же, как в статье. Однако серверная часть различается, поскольку SDK социальных сетей имеют разные конечные точки с разными протоколами. Вы можете взглянуть на https://github.com/sahat/satellizer/blob/master/examples/server/python/app.py, который содержит примеры для Facebook, Github, Google, Linkedin, Twiter и Bitbucket. Если вы сомневаетесь, вам следует взглянуть на документацию на https://github.com/sahat/satellizer.

Связанный: Вход в один клик с помощью Blockchain: учебное пособие по MetaMask