node.js - simple - node js secure rest api
Autenticación NodeJS REST mediante Passport y la red social OAuth2+ (4)
Estoy trabajando en la API REST
utilizando NodeJS
. Para la autenticación decidí usar el Passport
. Quiero verdaderamente api RESTful. Entonces significa que tengo que usar tokens en lugar de sesiones.
Quiero permitir a los usuarios iniciar sesión con un nombre de usuario y contraseña, o mediante redes sociales como Facebook, Google y Twitter.
Hago mi propio servidor OAuth2.0
para emitir Refresh tokens
Access
y Refresh tokens
utilizando el módulo oauth2orize
. Así que ahora puedo registrar un nuevo usuario y luego emitir tokens. Seguí este tutorial:
http://aleksandrov.ws/2013/09/12/restful-api-with-nodejs-plus-mongodb/
Verificando usuario para la ruta:
// api ------------------------------------------------------------------------------------
app.get(''/api/userInfo'',
passport.authenticate(''bearer'', { session: false }),
function(req, res) {
// req.authInfo is set using the `info` argument supplied by
// `BearerStrategy`. It is typically used to indicate scope of the token,
// and used in access control checks. For illustrative purposes, this
// example simply returns the scope in the response.
res.json({ user_id: req.user.userId, name: req.user.username, scope: req.authInfo.scope })
}
);
Todo esto funciona bastante bien. Lamentablemente no sé cómo implementar la autenticación social.
Estaba leyendo este tutorial:
http://scotch.io/tutorials/javascript/easy-node-authentication-facebook
Pero en este tutorial no están haciendo una api verdaderamente REST. Ya implementé el esquema de usuario de acuerdo con este tutorial donde los tokens para usuarios locales se almacenan en modelos separados.
// define the schema for our user model
var userSchema = mongoose.Schema({
local: {
username: {
type: String,
unique: true,
required: true
},
hashedPassword: {
type: String,
required: true
},
created: {
type: Date,
default: Date.now
}
},
facebook: {
id: String,
token: String,
email: String,
name: String
},
twitter: {
id: String,
token: String,
displayName: String,
username: String
},
google: {
id: String,
token: String,
email: String,
name: String
}
});
Pero ahora, ¿cómo puedo verificar el usuario?
passport.authenticate(''bearer'', { session: false }),
esto es verificar solo el token de portador en contra de mi db, pero ¿cómo puedo verificar tokens sociales? ¿Me estoy perdiendo de algo?
Estoy usando el inicio de sesión de Facebook para mi propia API RESTful para mi aplicación de Blocs de notas aquí . Comencé la aplicación como una que se usará como página web, pero la comunicación después del inicio de sesión se realizará a través de la API.
Entonces decidí crear una versión móvil de la misma aplicación que usará la API. Decidí hacerlo así: la aplicación móvil inicia sesión a través de Facebook y envía la identificación de usuario de Facebook y el token de acceso FB a la API, la API llama a la API de Facebook para verificar estos parámetros y, si tiene éxito, registra un nuevo usuario (o inicia sesión en un existente) en la base de datos de mi aplicación, crea un token personalizado para este usuario y lo devuelve a la aplicación móvil. Desde aquí, la aplicación móvil envía este token personalizado para autenticar la aplicación con la API.
Aquí hay un código:
La autenticación en la API (usa el módulo fbgraph npm):
var graph = require(''fbgraph''),
Promise = require(''bluebird'')
...
Promise.promisify(graph.get);
...
var postAuthHandler = function (req, res) {
var fbUserId = req.body.fbId,
fbAccessToken = req.body.fbAccessToken,
accessToken = req.body.accessToken;
...
graph.setAppSecret(config.facebook.app.secret);
graph.setAccessToken(fbAccessToken);
var graphUser;
var p = graph.getAsync(''me?fields=id,name,picture'')
.then(function (fbGraphUser) {
//when the given fb id and token mismatch:
if (!fbGraphUser || fbGraphUser.id !== fbUserId) {
console.error("Invalid user from fbAccessToken!");
res.status(HttpStatus.FORBIDDEN).json({});
return p.cancel();
}
graphUser = fbGraphUser;
return User.fb(fbUserId);
})
.then(function (user) {
if (user) {
//user found by his FB access token
res.status(HttpStatus.OK).json({accessToken: user.accessToken});
//stop the promises chain here
return p.cancel();
}
...create the user, generate a custom token and return it as above...
El modelo de usuario:
var userSchema = new mongoose.Schema({
facebookId: { type: String, required: true, unique: true },
accessToken: { type: String, required: true, unique: true },
name: { type: String, required: true },
photo: { type: String, required: true },
categories: [{ type: mongoose.Schema.Types.ObjectId, ref: ''Category'' }],
notepads: [{ type: mongoose.Schema.Types.ObjectId, ref: ''Notepad'' }]
});
La autenticación de Facebook en la aplicación móvil:
auth: function(fbId, fbAccessToken) {
return $http({
url: apiBase + ''/users/auth'',
data: {
fbId: fbId,
fbAccessToken: fbAccessToken
},
method: ''POST'',
cache: false
});
},
...
https://github.com/iliyan-trifonov/notepads-ionic/blob/master/www/js/services.js#L33 .
La aplicación móvil envía el token con la solicitud:
notepads: {
list: function() {
return $http({
url: apiBase + ''/notepads?insidecats=1'' + ''&token='' + User.get().accessToken/*gets the token from the local storage*/,
method: ''GET'',
cache: false
});
},
Es una aplicación jónica / angular / cordova. El inicio de sesión de Facebook desde la aplicación móvil inicia la aplicación de Facebook instalada en su teléfono o abre una ventana emergente para iniciar sesión en Facebook. Luego, una devolución de llamada devuelve la identificación del usuario de Facebook y el token de acceso a mi aplicación móvil.
El módulo fbgraph npm: https://github.com/criso/fbgraph
Iv''e creó los siguientes esquemas:
var userSchema = mongoose.Schema({
local: {
username: String,
password: String
},
facebook: {
id: String,
token: String,
email: String,
name: String
},
google: {
id: String,
token: String,
email: String,
name: String
},
token: {
type: Schema.Types.ObjectId,
ref: ''Token'',
default: null
}
});
var tokenSchema = mongoose.Schema({
value: String,
user: {
type: Schema.Types.ObjectId,
ref: ''User''
},
expireAt: {
type: Date,
expires: 60,
default: Date.now
}
});
Al iniciar sesión en mi aplicación web, uso un complemento social de PassportJS como facebook / google. Ejemplo:
//auth.js(router)
router.get(''/facebook'', passport.authenticate(''facebook'', {scope: [''email'']}));
router.get(''/facebook/callback'',
passport.authenticate(''facebook'', { successRedirect: ''/profile'',
failureRedirect: ''/'' }));
Ahora, cuando quiero navegar por mi aplicación web, uso la autenticación de sesión que se me facilitó a través del complemento de Facebook. Cuando un usuario desea solicitar un token de API, debe iniciar sesión para poder asociar ese token con el usuario.
Así que ahora un usuario tiene un token asociado que pueden usar para la autenticación Token para mi API.
A mis rutas API no les importan ni miran las sesiones, lo único que les importa es el token. He hecho esto creando un enrutador express y utilizando la estrategia de portador del pasaporte como middleware para todas las rutas que se dirigen a mi API.
//api.js (router)
//router middleware to use TOKEN authentication on every API request
router.use(passport.authenticate(''bearer'', { session: false }));
router.get(''/testAPI'', function(req, res){
res.json({ SecretData: ''abc123'' });
});
Así que ahora solo uso la autenticación de token para mis API (nunca mira los datos de sesión) y la autenticación de sesión para navegar fácilmente por mi aplicación web. A continuación se muestra un ejemplo de cómo utilizo sesiones para navegar por mi aplicación:
//secure.js(router) - Access private but NON-API routes.
//router middleware, uses session authentication for every request
router.use(function(req, res, next){
if(req.isAuthenticated()){
return next();
}
res.redirect(''/auth'');
});
//example router
router.get(''/profile'', function(req, res){
res.send("Private Profile data");
});
¡Espero que esto te ayude!
Las estrategias de los medios sociales para el pasaporte dependen de la sesión. No funcionarán sin uno.
Estoy ejecutando el mismo problema. Quería que mi servidor REST fuera sin estado.
En mi opinión hay 2 opciones.
- Agrega sesiones a tu servidor de descanso
- Agregue un nuevo servidor de autenticación que sea responsable de crear y entregar el token.
PD: Debería etiquetar esto como pasaporte para que los desarrolladores de puerto de paso puedan verlo.
Si usa un token de portador, todo lo que tiene que hacer es pasar el identificador único al usuario de la API. Lo más probable es que esto se haga en una sola llamada, tal vez en forma de inicio de sesión. Luego, cada llamada a la API requiere que el token que verifica el token exista en la base de datos y actualice su valor de Time To Live. No necesita tokens individuales en su esquema para cada inicio de sesión, necesita un esquema individual para tokens que tiene un valor de Time to Live (TTL)