Skip to content

Instantly share code, notes, and snippets.

@robwormald
Created December 6, 2013 02:23
Show Gist options
  • Save robwormald/7817593 to your computer and use it in GitHub Desktop.
Save robwormald/7817593 to your computer and use it in GitHub Desktop.
Sails + passport + oauth2orize stuffs
/**
* AuthController
*
* @module :: Controller
* @description :: Contains logic for handling auth requests.
*/
var passport = require('passport');
var GoogleStrategy = require('passport-google').Strategy;
module.exports = {
process: function(req,res){
//console.log(req)
passport.authenticate(
'local',{ successReturnToOrRedirect: '/', failureRedirect: '/login' })(req, res);
},
signin : function(req,res){
res.view('login')
},
googlelogin : function(req,res,next){
passport.authenticate(
'google',{ successReturnToOrRedirect: '/', failureRedirect: '/login' })(req, res,next);
},
googlecallback : function(req,res,next){
console.log('???')
passport.authenticate('google',{ successReturnToOrRedirect: '/', failureRedirect: '/login' })(req, res,next);
},
logout: function(req,res){
req.logout();
res.redirect('/');
},
connect : function(req,res){
console.log('authenticated websocket connection')
res.json({ping : 'pong'})
}
};
var passport = require('passport'),
BearerStrategy = require('passport-http-bearer').Strategy,
BasicStrategy = require('passport-http').BasicStrategy,
LocalStrategy = require('passport-local').Strategy,
ClientPasswordStrategy = require('passport-oauth2-client-password').Strategy,
GoogleOAuth2Strategy = require('passport-google-oauth').OAuth2Strategy;
var utils = require('../innitUtils.js');
var fs = require('fs');
var file = 'googleConfig.json';
var _googleConfig = JSON.parse(fs.readFileSync(file, 'utf8'))
var serverURL = process.env.URL || "http://localhost:1337"
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findOne({id:id}, function (err, user) {
// if(user.domainKey){}
done(err, user);
});
});
/**
* LocalStrategy
*
* This strategy is used to authenticate users based on a username and password.
* Anytime a request is made to authorize an application, we must ensure that
* a user is logged in before asking them to approve the request.
*/
passport.use(
new LocalStrategy(
function (username, password, done) {
process.nextTick(
function () {
User.findOne({
username: username
}).done(
function (err, user) {
if (err) {
console.log(err);
return;
}
if (!user) {
return done(
null, false, {
message: 'Unknown user ' + username
});
}
if (user.validPassword(password)) {
return done(
null, false, {
message: 'Invalid password'
});
}
return done(null, user);
})
});
}));
console.log()
passport.use(new GoogleOAuth2Strategy({
clientID: _googleConfig.web.client_id,
clientSecret: _googleConfig.web.client_secret,
callbackURL: process.env.HOST + "/auth/google/callback",
offline : true,
scope : ["openid","email","https://www.googleapis.com/auth/plus.me","https://www.googleapis.com/auth/admin.directory.user.readonly","https://www.googleapis.com/auth/admin.directory.orgunit.readonly","https://www.googleapis.com/auth/admin.directory.group.readonly","https://www.googleapis.com/auth/admin.directory.device.mobile.readonly","https://www.googleapis.com/auth/admin.directory.device.chromeos.readonly"],
passReqToCallback : true
},
function(req,accessToken, refreshToken, profile, done) {
console.log(accessToken)
console.log(refreshToken)
console.log(profile)
User.findOne({identifier : profile.id},function(err,user){
if (err) {
return done(err);
}
if (!user) {
if(profile._json && profile._json.verified_email){
// console.log(profile._json)
User.create({
identifier : profile.id,
username : profile._json.email,
password : utils.uid(16),
email : profile._json.email,
domain_key : profile._json['hd'],
provider : 'google'
}).done(function(err,_user){
if(accessToken){
// Token.create({
// user : user.id,
// token : accessToken,
// refreshToken : refreshToken || null,
// provider : 'google'
// }).done(function(err,token){
// user.credentials.google = token
// return done(null,user)
// })
console.log(_user)
return done(null,_user)
}
else{
return done(null,_user)
}
})
}
}
else{
return done(null, user);
}
})
}
));
/**
* BasicStrategy & ClientPasswordStrategy
*
* These strategies are used to authenticate registered OAuth clients. They are
* employed to protect the `token` endpoint, which consumers use to obtain
* access tokens. The OAuth 2.0 specification suggests that clients use the
* HTTP Basic scheme to authenticate. Use of the client password strategy
* allows clients to send the same credentials in the request body (as opposed
* to the `Authorization` header). While this approach is not recommended by
* the specification, in practice it is quite common.
*/
passport.use(new BasicStrategy(
function (username, password, done) {
User.findOne({
email: username
}, function (err, user) {
if (err) {
return done(err);
}
if (!user) {
return done(null, false);
}
if (!user.password != password) {
return done(null, false);
}
return done(null, user);
});
}));
passport.use(new ClientPasswordStrategy(
function (clientId, clientSecret, done) {
Client.findOne({
id: clientId
}, function (err, client) {
if (err) {
return done(err);
}
if (!client) {
return done(null, false);
}
if (client.clientSecret != clientSecret) {
return done(null, false);
}
return done(null, client);
});
}));
/**
* BearerStrategy
*
* This strategy is used to authenticate users based on an access token (aka a
* bearer token). The user must have previously authorized a client
* application, which is issued an access token to make requests on behalf of
* the authorizing user.
*/
passport.use(new BearerStrategy(
function(accessToken, done) {
Token.findOne({accessToken:accessToken}, function(err, token) {
if (err) { return done(err); }
if (!token) { return done(null, false); }
var info = {scope: '*'}
User.findOne({
id: token.user
}).done(
function (err, user) {
User.findOne({
id: token.user
},done(err,user,info));
});
});
}
));
var passport = require('passport'),
oauth2orize = require('oauth2orize'),
jwtBearer = require('oauth2orize-jwt-bearer').Exchange,
login = require('connect-ensure-login'),
utils = require('../innitUtils.js');
module.exports = {
express: {
customMiddleware: function(app)
{
/** oAuth Server **/
app.use(passport.initialize());
app.use(passport.session());
var server = oauth2orize.createServer();
// Register supported grant types.
//
// OAuth 2.0 specifies a framework that allows users to grant client
// applications limited access to their protected resources. It does this
// through a process of the user granting access, and the client exchanging
// the grant for an access token.
// Grant authorization codes. The callback takes the `client` requesting
// authorization, the `redirectURI` (which is used as a verifier in the
// subsequent exchange), the authenticated `user` granting access, and
// their response, which contains approved scope, duration, etc. as parsed by
// the application. The application issues a code, which is bound to these
// values, and will be exchanged for an access token.
server.grant(oauth2orize.grant.code(function(client, redirectURI, user, ares, done) {
var code = utils.uid(16);
Authcode.create({
code: code,
client: client.id,
redirectURI: redirectURI,
user: user.id,
scope: ares.scope
}).done(function(err,code){
if(err){return done(err,null);}
return done(null,code.code);
});
}));
// Grant implicit authorization. The callback takes the `client` requesting
// authorization, the authenticated `user` granting access, and
// their response, which contains approved scope, duration, etc. as parsed by
// the application. The application issues a token, which is bound to these
// values.
server.grant(oauth2orize.grant.token(function(client, user, ares, done) {
console.log('a')
console.log(ares)
var token = utils.uid(256);
Token.create({
accessToken: token,
user: user.id,
client: client.id,
//scope: code.scope
}).done(function(err,token){
if (err) {
console.log(err)
return done(err);
}
console.log(token.accessToken)
return done(null, token.accessToken);
});
}));
// Exchange authorization codes for access tokens. The callback accepts the
// `client`, which is exchanging `code` and any `redirectURI` from the
// authorization request for verification. If these values are validated, the
// application issues an access token on behalf of the user who authorized the
// code.
server.exchange(oauth2orize.exchange.code(function(client, code, redirectURI, done) {
Authcode.findOne({
code: code
}).done(function(err,code){
if (err || !code) {
return done(err);
}
if (client.id !== code.clientId) {
return done(null, false);
}
if (redirectURI !== code.redirectURI) {
return done(null, false);
}
var token = utils.uid(256);
Token.create({
token: token,
user: code.user,
client: code.client,
scope: code.scope
}).done(function(err,token){
if (err) {
return done(err);
}
return done(null, token);
});
});
}));
// Exchange user id and password for access tokens. The callback accepts the
// `client`, which is exchanging the user's name and password from the
// authorization request for verification. If these values are validated, the
// application issues an access token on behalf of the user who authorized the code.
server.exchange(oauth2orize.exchange.password(function(client, username, password, scope, done) {
console.log('password xchange')
//Validate the client
Client.findOne({id : client.id}).done(function(err, localClient) {
console.log('password xchange')
if (err) { return done(err); }
if(localClient === null) {
return done(null, false);
}
if(localClient.clientSecret !== client.clientSecret) {
return done(null, false);
}
//Validate the user
User.findByUsername(username, function(err, user) {
if (err) { return done(err); }
if(user === null) {
return done(null, false);
}
if(user.validPassword(password)) {
return done(null, false);
}
//Everything validated, return the token
var token = utils.uid(256);
Token.create({
token : token,
user : user.id,
client : client.id
}).then(function(err) {
if (err) { return done(err); }
done(null, token);
});
});
});
}));
//TODO : google JWT token stuff
// server.exchange('urn:ietf:params:oauth:grant-type:jwt-bearer', jwtBearer(function(client, data, signature, done) {
// var crypto = require('crypto')
// , pub = // TODO - Load your pubKey registered to the client from the file system or database
// , verifier = crypto.createVerify("RSA-SHA256");
// verifier.update(JSON.stringify(data));
// if (verifier.verify(pub, signature, 'base64')) {
// // TODO - base64url decode data then verify client_id, scope and expiration are valid
// AccessToken.create(client, scope, function(err, accessToken) {
// if (err) { return done(err); }
// done(null, accessToken);
// });
// }
// }));
app.get('/oauth/authorize', login.ensureLoggedIn(), server.authorize(function (clientID, redirectURI, done) {
console.log(clientID)
Client.findOne({
clientKey: clientID
}, function (err, cli) {
console.log(cli)
if (err) {
return done(err);
}
if (!cli) {
return done(null, false);
}
if (cli.redirectURI != redirectURI) {
return done(null, false);
}
return done(null, cli, cli.redirectURI);
});
}), function (req, res) {
res.render('dialog', {
transactionID: req.oauth2.transactionID,
user: req.user,
cli: req.oauth2.client
});
});
app.post('/oauth/authorize/decision',
login.ensureLoggedIn(),
server.decision());
server.serializeClient(function(client, done) {
return done(null, client.id);
});
server.deserializeClient(function(id, done) {
Client.findOne(id, function(err, client) {
if (err) { return done(err); }
return done(null, client);
});
});
app.get('/token',
passport.authenticate('oauth2-client-password', { session: false }),
server.token(),
server.errorHandler());
app.get('/tokenInfo',function (req, res) {
console.log(req.query)
if (req.query.access_token) {
Token.find({token : req.query.access_token}).then(function (token) {
console.log(token)
if (!token) {
res.status(400);
res.json({ error: "invalid_token" });
} else if(new Date() > token.expirationDate) {
res.status(400);
res.json({ error: "invalid_token" });
}
else {
Client.find(token.client, function (err, client) {
if (err || !client) {
res.status(400);
res.json({ error: "invalid_token"});
} else {
if(token.expirationDate) {
var expirationLeft = Math.floor((token.expirationDate.getTime() - new Date().getTime()) / 1000);
if(expirationLeft <= 0) {
res.json({ error: "invalid_token"});
} else {
res.json({ audience: client.id, expires_in: expirationLeft});
}
} else {
console.log('woohoo?')
res.json({validToken: true })
}
}
});
}
});
} else {
res.status(400);
res.json({ error: "invalid_token"});
}
})
//this function is only to test callbacks
app.get('/testCallback',function(req,res){
res.render('test',{params:req.query});
});
}
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment