Integrazione dell'accesso di Facebook nell'app AngularJS con Satellizer
Pubblicato: 2022-03-11Con l'arrivo di framework front-end ricchi di funzionalità come AngularJS, sul front-end viene implementata sempre più logica, come la manipolazione/convalida dei dati, l'autenticazione e altro ancora. Satellizer, un modulo di autenticazione basato su token facile da usare per AngularJS, semplifica il processo di implementazione del meccanismo di autenticazione in AngularJS. La libreria viene fornita con supporto integrato per Google, Facebook, LinkedIn, Twitter, Instagram, GitHub, Bitbucket, Yahoo, Twitch e account Microsoft (Windows Live).
In questo articolo, creeremo una webapp molto semplice simile a quella qui che ti consente di accedere e vedere le informazioni dell'utente corrente.
Autenticazione vs autorizzazione
Queste sono 2 parole spaventose che incontri spesso quando la tua app inizia a integrare un sistema utente. Secondo Wikipedia:
L' autenticazione è l'atto di confermare la verità di un attributo di un singolo dato (un dato) ritenuto vero da un'entità.
L'autorizzazione è la funzione di specificare i diritti di accesso alle risorse relative alla sicurezza delle informazioni e alla sicurezza informatica in generale e al controllo degli accessi in particolare.
In parole povere, prendiamo un esempio di un sito Web di blog con alcune persone che ci lavorano. I blogger scrivono articoli e il manager convalida il contenuto. Ogni persona può autenticarsi (login) nel sistema ma i suoi diritti (autorizzazione) sono diversi, quindi il blogger non può convalidare i contenuti mentre il manager può farlo.
Perché Satellizer
Puoi creare il tuo sistema di autenticazione in AngularJS seguendo alcuni tutorial come questo molto dettagliato: JSON Web Token Tutorial: An Example in Laravel e AngularJS. Suggerisco di leggere questo articolo in quanto spiega molto bene JWT (JSON Web Token) e mostra un modo semplice per implementare l'autenticazione in AngularJS utilizzando direttamente l'archiviazione locale e gli intercettori HTTP.
Allora perché Satellizer? Il motivo principale è che supporta una manciata di accessi ai social network come Facebook, Twitter, ecc. Al giorno d'oggi, soprattutto per i siti Web utilizzati su dispositivi mobili, digitare nome utente e password è piuttosto ingombrante e gli utenti si aspettano di poter utilizzare il tuo sito Web con pochi ostacoli utilizzando gli accessi social. Poiché l'integrazione dell'SDK di ogni social network e la relativa documentazione è piuttosto ripetitivo, sarebbe bello supportare questi accessi social con il minimo sforzo.
Inoltre Satellizer è un progetto attivo su Github. L'attivo è fondamentale qui poiché questi SDK cambiano abbastanza frequentemente e non vuoi leggere la loro documentazione ogni tanto (chiunque lavori con l'SDK di Facebook sa quanto sia fastidioso)
App AngularJS con accesso Facebook
È qui che le cose iniziano a farsi interessanti.
Costruiremo un'app Web con un meccanismo di accesso/registrazione regolare (ovvero utilizzando nome utente, password) e supporta anche gli accessi social. Questa webapp è molto semplice in quanto ha solo 3 pagine:
- Pagina iniziale: chiunque può vedere
- Pagina di accesso: per inserire nome utente/password
- Pagina segreta: visibile solo agli utenti loggati
Per il backend, useremo Python e Flask. Python e il framework Flask sono piuttosto espressivi, quindi spero che il porting del codice in altri linguaggi/framework non sia molto difficile. Ovviamente useremo AngularJS per il front-end. E per gli accessi social, ci integreremo solo con Facebook in quanto è il social network più popolare in questo momento.
Iniziamo!
Passaggio 1: progetto Bootstrap
Ecco come struttureremo il nostro codice:
- app.py - static/ - index.html - app.js - bower.json - partials/ - login.tpl.html - home.tpl.html - secret.tpl.html
Tutto il codice di back-end è in app.py . Il codice front-end viene inserito nella cartella static/. Per impostazione predefinita, Flask servirà automaticamente il contenuto di static/ folder. Tutte le viste parziali sono in static/partial/ e gestite dal modulo ui.router.
Per iniziare a codificare il back-end, avremo bisogno di Python 2.7.* e installeremo le librerie richieste usando pip. Ovviamente puoi usare virtualenv per isolare un ambiente Python. Di seguito è riportato l'elenco dei moduli Python richiesti da inserire in Requirements.txt:
Flask==0.10.1 PyJWT==1.4.0 Flask-SQLAlchemy==1.0 requests==2.7.0
Per installare tutte queste dipendenze:
pip install -r requirements.txt
In app.py abbiamo del codice iniziale per eseguire il bootstrap di Flask (le istruzioni di importazione sono omesse per brevità):
app = Flask(__name__) @app.route('/') def index(): return flask.redirect('/static/index.html') if __name__ == '__main__': app.run(debug=True)
Quindi eseguiamo l' inizializzazione di Bower e installiamo AngularJS e 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 volta installate queste librerie, dobbiamo includere AngularJS e ui-router in index.html e creare percorsi per 3 pagine: 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>
Di seguito è riportato il codice di cui abbiamo bisogno in main.js per configurare il routing:
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'); });
A questo punto se esegui il server python app.py , dovresti avere questa interfaccia di base su http://localhost:5000
I collegamenti Home, Login e Secret dovrebbero funzionare a questo punto e mostrare il contenuto dei modelli corrispondenti.
Congratulazioni, hai appena finito di montare lo scheletro! Se riscontri qualche errore, controlla il codice su GitHub
Passaggio 2: accedi e registrati
Al termine di questo passaggio, avrai una webapp che puoi registrare/accedere utilizzando e-mail e password.
Il primo passo è configurare il backend. Abbiamo bisogno di un modello utente e di un modo per generare il token JWT per un determinato utente. Il modello Utente mostrato di seguito è davvero semplificato e non esegue nemmeno controlli di base come se il campo e-mail contiene "@", o se la password del campo contiene almeno 6 caratteri, ecc.
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')
Usiamo il modulo jwt in python per generare la parte del payload in JWT. Le parti iat ed exp corrispondono al timestamp in cui il token è stato creato e scaduto. In questo codice, il token scadrà tra 2 settimane.
Dopo aver creato l'utente del modello, possiamo aggiungere gli endpoint "login" e "register". Il codice per entrambi è abbastanza simile, quindi qui mostrerò solo la parte "registra". Si noti che per impostazione predefinita, Satellizer chiamerà gli endpoint /auth/login e /auth/signup rispettivamente per "login" e "register".
@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())
Controlliamo prima l'endpoint usando curl:
curl localhost:5000/auth/signup -H "Content-Type: application/json" -X POST -d '{"email":"[email protected]","password":"xyz"}'
Il risultato dovrebbe assomigliare a questo:
{ "token": "very long string…." }
Ora che il back-end è pronto, attacchiamo il front-end! Innanzitutto, dobbiamo installare satellizer e aggiungerlo come dipendenza in main.js:
bower install satellizer --save
Aggiungi satellite come dipendenza:
var app = angular.module('DemoApp', ['ui.router', 'satellizer']);
L'accesso e la registrazione in Satellizer sono in realtà abbastanza semplici rispetto a tutte le impostazioni fino ad ora:
$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 hai difficoltà a impostare il codice, puoi dare un'occhiata al codice su GitHub.

Passaggio 3: ma la vista segreta non è davvero segreta, perché chiunque può vederla!
Si, è corretto! Fino ad ora, chiunque può accedere alla pagina segreta senza effettuare il login.
È ora di aggiungere un intercettore in AngularJS per assicurarsi che se qualcuno va alla pagina segreta e se questo utente non ha effettuato l'accesso, verrà reindirizzato alla pagina di accesso.
Per prima cosa, dovremmo aggiungere un flag requiredLogin per distinguere la pagina segreta dalle altre.
.state('secret', { url: '/secret', templateUrl: 'partials/secret.tpl.html', controller: 'SecretCtrl', data: {requiredLogin: true} })
La parte "data" verrà utilizzata nell'evento $stateChangeStart che viene attivato ogni volta che il routing cambia:
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'); } }); });
Ora, l'utente non può accedere direttamente alla pagina segreta senza effettuare il login. Evviva!
Come al solito, il codice di questo passaggio può essere trovato qui.
Passaggio 4: è ora di ottenere qualcosa di veramente segreto!
In questo momento, non c'è niente di veramente segreto nella pagina segreta. Mettiamo qualcosa di personale lì.
Questo passaggio inizia con la creazione di un endpoint nel back-end accessibile solo a un utente autenticato, ad esempio con un token valido. L'endpoint /utente seguente restituisce l' id_utente e l'e -mail dell'utente corrispondente 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
Ancora una volta, utilizziamo il modulo jwt per decodificare il token JWT incluso nell'intestazione 'Autorizzazione' e per gestire il caso quando il token è scaduto o non valido.
Testiamo questo endpoint usando curl. Innanzitutto, dobbiamo ottenere un token valido:
curl localhost:5000/auth/signup -H "Content-Type: application/json" -X POST -d '{"email":"[email protected]","password":"xyz"}'
Quindi con questo token:
curl localhost:5000/user -H "Authorization: Bearer {put the token here}"
Che dà questo risultato:
{ "email": "[email protected]", "id": 1 }
Ora dobbiamo includere questo endpoint nel Secret Controller. Questo è abbastanza semplice in quanto dobbiamo solo chiamare l'endpoint usando il normale modulo $http. Il token viene inserito automaticamente nell'intestazione da Satellizer, quindi non dobbiamo preoccuparci di tutti i dettagli per salvare il token e quindi inserirlo nell'intestazione corretta.
getUserInfo(); function getUserInfo() { $http.get('/user') .then(function (response) { $scope.user = response.data; }) .catch(function (response) { console.log("getUserInfo error", response); }) }
Finalmente, abbiamo qualcosa di veramente personale nella pagina segreta!
Il codice di questo passaggio è su GitHub.
Passaggio 5: accedi a Facebook con Satellizer
Una cosa bella di Satellizer, come accennato all'inizio, è che rende l'integrazione dell'accesso social molto più semplice. Al termine di questo passaggio, gli utenti possono accedere utilizzando il proprio account Facebook!
La prima cosa da fare è creare un'applicazione sulla pagina degli sviluppatori di Facebook per avere un application_id e un codice segreto. Segui developer.facebook.com/docs/apps/register per creare un account sviluppatore Facebook, se non ne hai già uno, e creare un'app per il sito web. Successivamente, avrai l'ID dell'applicazione e il segreto dell'applicazione come nello screenshot qui sotto.
Una volta che l'utente sceglie di connettersi con Facebook, Satellizer invierà un codice di autorizzazione all'endpoint /auth/facebook . Con questo codice di autorizzazione, il back-end può recuperare un token di accesso dall'endpoint di Facebook /oauth che consente alla chiamata all'API di Facebook Graph di ottenere informazioni sull'utente come posizione, user_friends, e-mail dell'utente, ecc.
Dobbiamo anche tenere traccia se un account utente viene creato con Facebook o tramite la registrazione regolare. Per fare ciò, aggiungiamo facebook_id al nostro modello Utente.
facebook_id = db.Column(db.String(100))
Il segreto di facebook viene configurato tramite le variabili env FACEBOOK_SECRET che aggiungiamo ad app.config .
app.config['FACEBOOK_SECRET'] = os.environ.get('FACEBOOK_SECRET')
Quindi, per avviare app.py , dovresti impostare questa variabile env:
FACEBOOK_SECRET={your secret} python app.py
Ecco il metodo che gestisce gli accessi a Facebook. Per impostazione predefinita, Satellizer chiamerà l'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())
Per inviare una richiesta al server di Facebook, utilizziamo il pratico modulo request. Ora la parte difficile sul back-end è fatta. Sul front-end, aggiungere l'accesso a Facebook è abbastanza semplice. Innanzitutto, dobbiamo comunicare a Satellizer il nostro facebook_id aggiungendo questo codice nella funzione 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' });
Per accedere tramite Facebook, possiamo semplicemente chiamare:
$auth.authenticate(“facebook”)
Come al solito, puoi controllare il codice su GitHub
In questo momento, la webapp è completa in termini di funzionalità. L'utente può effettuare il login/registrarsi utilizzando e-mail e password normali o utilizzando Facebook. Una volta effettuato l'accesso, l'utente può vedere la sua pagina segreta.
Crea una bella interfaccia
L'interfaccia non è molto carina a questo punto, quindi aggiungiamo un po' di Bootstrap per il layout e il modulo del tostapane angolare per gestire bene un messaggio di errore, come quando l'accesso non riesce.
Il codice per questa parte di abbellimento può essere trovato qui.
Conclusione
Questo articolo mostra un'integrazione passo passo di Satellizer in una (semplice) webapp AngularJS. Con Satellizer, possiamo aggiungere facilmente altri accessi social come Twitter, Linkedin e altro. Il codice sul front-end è praticamente lo stesso dell'articolo. Tuttavia, il back-end varia poiché gli SDK di social network hanno endpoint diversi con protocolli diversi. Puoi dare un'occhiata a https://github.com/sahat/satellizer/blob/master/examples/server/python/app.py che contiene esempi per Facebook, Github, Google, Linkedin, Twiter e Bitbucket. In caso di dubbio, dovresti dare un'occhiata alla documentazione su https://github.com/sahat/satellizer.