2014-01-23 6 views
20

Ich habe versucht, so viele verschiedene Antworten und Beiträge wie möglich zu lesen, aber ich kann mich immer noch nicht mit einer Lösung zufrieden geben, die meinen Bedürfnissen entspricht. Ich versuche, die beste (effizienteste, aber meist sicherste) Art der Benutzerauthentifizierung, Anmeldung usw. zu erarbeiten.Authentifizierung für Node.js App mit Angular.js und iOS Clients

Ich habe einen Node.js-Server, der auf Express läuft; Ich habe eine Angular.js Web App; und ich habe eine iOS App. Ich stelle eine RESTful API mit Express/Node.js zur Verfügung.

Plätzchen

Die ersten Dinge, die ich der Lese Cookies zu verwenden, und eine Session-ID/login-Token auf der Serverseite zu speichern (gehasht) und auf der Client-Seite (ungehashte). Der Client würde diese ID bei jeder Anfrage übertragen, der Server würde ihn hashen, parsen und die Anfrage entsprechend bearbeiten. Dies fühlt sich nicht RESTful an (kein großes Problem), aber noch wichtiger, müsste ich meine API duplizieren: eine für Benutzername/Passwort-Authentifizierung (z. B. via curl) und eine für Cookie-basierte Authentifizierung (z. B. meine Web-App)?

Ein weiteres Problem mit diesem: was ich tun würde, wenn ich mehrere Verbindungen von dem einen Benutzer, z. Sie sind in zwei Browsern, einem iPhone und einem iPad angemeldet. Wäre der Speicher ihrer Sitzungs-IDs jetzt ein Array?

HTTP Basic Auth

Die nächste Idee war, HTTP Basic Auth zu verwenden (mit SSL), die einfach genug scheint, ist aber nicht zu empfehlen, da Sie einen Benutzernamen und ein Passwort mit jeder Anforderung übertragen müssen. Wenn ich es mit HTTP Basic Auth machen würde, würde ich dann den Benutzernamen und das Passwort in Cookies (oder lokalen HTML-Speicher) speichern, um die "Remember Me" -Funktionalität zu ermöglichen? Oder könnte ich beides kombinieren: Benutze HTTP Basic Auth für die eigentlichen Anfragen (poste einen neuen Beitrag, etc.) und benutze einfach eine Sitzungs-ID, die in einem Cookie für die erste Anmeldung gespeichert wurde/Erinnerst du mich Aspekte?

Ist die Übertragung einer Sitzungs-ID sicherer als die Übertragung des Benutzerkennworts? Wie? Die Sitzungs-ID wird scheinbar als Kennwort fungieren, daher hätte die Übertragung derselben die gleichen Sicherheitsprobleme wie die Übertragung eines Kennworts.

Basic Auth scheint auf allen Plattformen unterstützt zu werden, was ideal ist. Der Hauptnachteil scheint zu sein, Clientauthentifizierungsdaten mit jeder Anfrage zu übertragen. Gibt es eine Möglichkeit, dieses Problem zu beheben?

OAuth

OAuth scheint für meine Bedürfnisse wie viel des Guten. Ich denke, ich würde die Fähigkeit verlieren, curl-Befehle auszuführen, um meine API zu testen. Wie ist OAuth eine Verbesserung gegenüber der Cookie-Methode?

Wie Sie wahrscheinlich sagen können, bin ich ein wenig durch die verschiedenen verfügbaren Informationen verwirrt, also wenn Sie eine Reihe von guten Links haben - anwendbar auf dieses Szenario - würde ich lieben, sie zu lesen. Ich versuche eine Lösung zu finden, die über alle Plattformen hinweg passt, aber immer noch so sicher wie möglich ist. Auch wenn ich irgendeine meiner Terminologie falsch verstanden habe, korrigiere mich bitte, weil es die Suche für mich einfacher macht.

Danke.

Update:

Ich habe über dieses Problem nachzudenken, und ich habe eine Idee hatte. Bitte sagen Sie mir, ob das dumm/unsicher ist/irgendein Feedback, weil ich nicht sicher bin, ob es gut ist.

Wenn sich der Benutzer anmeldet, generieren wir eine zufällige Sitzungs-ID (gesalzen usw.).Diese optionale Sitzungs-ID wird an den Client gesendet, den der Client speichern kann (z. B. in Cookies), wenn sie wählen; Die Sitzungs-ID wird in der Datenbank gespeichert.

Diese Sitzungs-ID wird dann optional mit jeder Anforderung entweder als HTTP-Authentifizierungsheader oder als Abfragezeichenfolge gesendet oder der Client kann einfach den Benutzernamen und das Kennwort senden, wenn sie dies wünschen (was uns unsere reguläre REST-API gibt). Auf der Serverseite prüfen wir zuerst nach einem Sitzungs-ID-Parameter. Wenn dieser nicht vorhanden ist, suchen wir nach Benutzername/Passwort. Wenn beides nicht der Fall ist.

Auf dem Server überprüfen wir, dass die Session-ID mit dem korrekten Benutzernamen zugeordnet ist. Wenn dies der Fall ist, schließen wir die Anfrage ab.

Jedes Mal, wenn sich der Benutzer anmeldet, erstellen wir eine neue Sitzungs-ID oder löschen die aktuelle und senden diese mit der Antwort an die Anmeldeanforderung.

Ich denke, das lässt mich die regelmäßige REST-API verwenden, gegebenenfalls mit Grund Auth und pflegen Sitzungen/erinnere mich Funktionalität. Es löst das Problem mit mehreren Logins nicht, aber ansonsten denke ich, dass es so wäre. Lass es mich wissen, bitte.

+3

Haben Sie Passport.js schon einmal gesehen? http://passportjs.org/ –

+1

@MWay danke dafür. Ich werde mich definitiv weiter damit befassen. Könntest du bitte die Sicherheit der Lösung, die ich im Abschnitt "Update" gepostet habe, kommentieren? Wenn es nicht machbar ist, werde ich wahrscheinlich mit Passport gehen. – matthewpalmer

+0

@matt, muss diese session_ids Ewigkeit nicht machen ... Speichere sie nicht in der DB, nur im Cache ... wir speichern diese Tokens in der redis mit 24h ttl und erneuern diese ttl bei jeder Benutzeranfrage. .. Wenn der Benutzer 24 Stunden still war, ist dieses Token abgelaufen ... –

Antwort

25

Ich würde eine tokenbasierte Authentifizierung verwenden, bei der Sie mit jeder Anfrage automatisch ein Token senden können. Sie müssen sich einmal anmelden, der Server stellt Ihnen ein Token zur Verfügung, das Sie dann mit jeder Anfrage senden können. Dieses Token wird dem HTML-Header hinzugefügt, sodass Sie nicht jede Anfrage an den Browser ändern müssen.

Sie können bestimmte Anrufe in der API so eingestellt, dass sie immer ein Zeichen benötigen, während andere möglicherweise nicht Token geschützt werden.

Für Express, können Sie Express-jwt (https://www.npmjs.org/package/express-jwt)

var expressJwt = require('express-jwt'); 

// Protect the /api routes with JWT 
app.use('/api', expressJwt({secret: secret})); 

app.use(express.json()); 
app.use(express.urlencoded()); 

Wenn Sie Sie authentifizieren möchten diese Funktion in Ihrem Express-Server erstellen:

app.post('/authenticate', function (req, res) { 
    //if is invalid, return 401 
    if (!(req.body.username === 'john.doe' && req.body.password === 'foobar')) { 
    res.send(401, 'Wrong user or password'); 
    return; 
    } 

    var profile = { 
    first_name: 'John', 
    last_name: 'Doe', 
    email: '[email protected]', 
    id: 123 
    }; 

    // We are sending the profile inside the token 
    var token = jwt.sign(profile, secret, { expiresInMinutes: 60*5 }); 

    res.json({ token: token }); 
}); 

Und für geschützte Anrufe etwas

das beginnt mit/api:

app.get('/api/restricted', function (req, res) { 
    console.log('user ' + req.user.email + ' is calling /api/restricted'); 
    res.json({ 
    name: 'foo' 
    }); 
}); 

In Ihrer Angular-Anwendung n Sie können mit einloggen:

$http 
     .post('/authenticate', $scope.user) 
     .success(function (data, status, headers, config) { 
     $window.sessionStorage.token = data.token; 
     $scope.message = 'Welcome'; 
     }) 
     .error(function (data, status, headers, config) { 
     // Erase the token if the user fails to log in 
     delete $window.sessionStorage.token; 

     // Handle login errors here 
     $scope.message = 'Error: Invalid user or password'; 
     }); 

Und durch ein Authentifizierungs-Interceptor zu schaffen, wird es automatisch das Token bei jeder Anfrage senden:

myApp.factory('authInterceptor', function ($rootScope, $q, $window) { 
    return { 
    request: function (config) { 
     config.headers = config.headers || {}; 
     if ($window.sessionStorage.token) { 
     config.headers.Authorization = 'Bearer ' + $window.sessionStorage.token; 
     } 
     return config; 
    }, 
    response: function (response) { 
     if (response.status === 401) { 
     // handle the case where the user is not authenticated 
     } 
     return response || $q.when(response); 
    } 
    }; 
}); 

myApp.config(function ($httpProvider) { 
    $httpProvider.interceptors.push('authInterceptor'); 
}); 

Wenn Sie alten Browser unterstützen haben, das nicht lokale Speicher unterstützen . Sie können die $window.sessionStorage mit einer Bibliothek wie AmplifyJS (http://amplifyjs.com/) austauschen. Amplify verwendet beispielsweise den verfügbaren lokalen Speicher. Dies würde in etwa wie folgt übersetzen:

if (data.status === 'OK') { 
     //Save the data using Amplify.js 
     localStorage.save('sessionToken', data.token); 
     //This doesn't work on the file protocol or on some older browsers 
     //$window.sessionStorage.token = data.token; 
     $location.path('/pep'); 
    } 
    }).error(function (error) { 
    // Erase the token if the user fails to log in 
    localStorage.save('sessionToken', null); 
    // Handle login errors here 
    $scope.message = 'Error: Invalid user or password'; 
    }); 

Und das authintercepter wir tauschen:

angular.module('myApp.authInterceptor', ['myApp.localStorage']).factory('authInterceptor', [ 
    '$rootScope', 
    '$q', 
    'localStorage', 
    function ($rootScope, $q, localStorage) { 
    return { 
     request: function (config) { 
     config.headers = config.headers || {}; 
     config.headers.Authorization = 'Bearer ' + localStorage.retrieve('sessionToken'); 
     return config; 
     }, 
     response: function (response) { 
     if (response.status === 401) { 
     } 
     return response || $q.when(response); 
     } 
    }; 
    } 
]); 

Sie alles außer AmplifyJS in diesem Artikel finden:

http://blog.auth0.com/2014/01/07/angularjs-authentication-with-cookies-vs-token/

+0

Ok, aber ich bin gezwungen, jedes Mal Benutzernamen und Passwort zu senden, und ein XSS-Angriff könnte möglich sein. Warum nicht stattdessen Cookies verwenden? Zumindest Passwort ist dort nicht klar – nick

+3

Nein. Sie müssen nur Ihren Benutzernamen und Ihr Passwort einmal über einen HTTP-POST senden. Dies unterscheidet sich nicht von einer Anmeldung bei der Verwendung von Cookies. Danach verwenden Sie das Token im HTTP-Header, um sich zu authentifizieren, anstatt dies mit einem Cookie in Ihrem HTTP-Header zu tun. Wenn Sie sich vor XSS schützen möchten, müssten Sie alle Eingaben filtern, die Ihre Anwendung ohnehin erhält. Und es wäre sinnvoll, HTTPS für Daten über die Leitung zu verwenden. Die Vorteile der tokenbasierten Authentifizierung werden in dem Beitrag beschrieben, der in meiner Antwort verlinkt ist. –

0

Ich benutze Generator-angular-fullstack, die/api-Dienste sind nicht gesichert, erhalten Sie Ihre _id von/api/users/me, abmelden, und gehen Sie zu/api/users/your_id_hier, Sie werden feststellen, dass die/api nicht gesichert.

Verwandte Themen