Skip to content
This repository was archived by the owner on Jul 31, 2019. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ public/stylesheets/userbar.css
dist/
client/
mozilla.webmaker.org
package-lock.json
6 changes: 3 additions & 3 deletions env.dist
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,9 @@ export AWS_SECRET_ACCESS_KEY="foo"
# id.wm.o config
#

export OAUTH_CLIENT_ID="test"
export OAUTH_CLIENT_SECRET="test"
export OAUTH_AUTHORIZATION_URL="http://localhost:1234"
export OAUTH_WEBMAKER_CLIENT_ID="test"
export OAUTH_WEBMAKER_CLIENT_SECRET="test"
export OAUTH_WEBMAKER_AUTH_URL="http://localhost:1234"

# Default content title, which should match a folder inside the repo's /default folder
export DEFAULT_PROJECT_TITLE="empty-project"
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@
"node-uuid": "~1.4.7",
"npm-run-all": "^4.0.2",
"nunjucks": "^2.3.0",
"passport": "^0.3.2",
"passport-webmaker": "^1.1.1",
"properties-parser": "0.3.1",
"q-io": "1.13.2",
"request": "2.80.0",
Expand Down
9 changes: 8 additions & 1 deletion server/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ let express = require("express");
let path = require("path");
let favicon = require("serve-favicon");
let url = require("url");
let passport = require("passport");

let env = require("./lib/environment");
let templatize = require("./templatize");
Expand All @@ -16,6 +17,7 @@ let localize = require("./localize");
let HttpError = require("./lib/http-error.js");
let routes = require("./routes")();
let Utils = require("./lib/utils");
let passportConfig = require("./passport");

let server = express();
let environment = env.get("NODE_ENV");
Expand Down Expand Up @@ -53,6 +55,7 @@ requests.disableHeaders([ "x-powered-by" ])
.url({ extended: true })
.lessOptimizations(path.join(root, "public"), cssAssets, !isDevelopment)
.healthcheck()
.passport(passport)
.sessions({
key: "mozillaThimble",
secret: env.get("SESSION_SECRET"),
Expand Down Expand Up @@ -114,11 +117,15 @@ localize(server, Object.assign(env.get("L10N"), {
excludeLocaleInUrl: [ "/projects/remix-bar" ]
}));

/**
* Passport
*/
passportConfig(passport);

/**
* API routes
*/
routes.init(server);
routes.init(server, passport);


/*
Expand Down
23 changes: 15 additions & 8 deletions server/lib/middleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const ASSERT_TOKEN = env.get("ASSERT_TOKEN");
module.exports = function middlewareConstructor(config) {
// Set up a token decryptor using the default cryptr 2.0.0 algorithm.
let cryptr = new Cryptr(env.get("SESSION_SECRET"));

// Set up a fallback decryptor that matches the cryptr 1.0.0 algorithm
// https://github.com/MauriceButler/cryptr/compare/fabae97a61119d69f03fc189f7c95dda826c96b7...master#diff-168726dbe96b3ce427e7fedce31bb0bcR9
let cryptrFallback = new Cryptr(env.get("SESSION_SECRET"), "aes256");
Expand Down Expand Up @@ -63,7 +63,7 @@ module.exports = function middlewareConstructor(config) {
* sign out and sign in again (to bust browser cache).
*/
checkForAuth(req, res, next) {
if(req.session.user) {
if(req.session.passport.user) {
return next();
}

Expand All @@ -86,7 +86,7 @@ module.exports = function middlewareConstructor(config) {
}

// Decrypt oauth token
req.user = req.session.user;
req.user = req.session.passport.user;
let token = req.user.token = cryptr.decrypt(req.session.token);

if(ASSERT_TOKEN) {
Expand All @@ -102,23 +102,23 @@ module.exports = function middlewareConstructor(config) {
}

let tokenType = typeof token;

if(tokenType !== "string") {
console.log("ASSERT_TOKEN FAILED: Expected token type to be String, instead got: " + tokenType);
return false;
}

if(!/^[a-z0-9]{64}$/.test(token)) {
console.log("ASSERT_TOKEN FAILED: Expected token to only have chars a-z, 0-9. Also got: '" + token.replace(/[a-z0-9]/g, ' ') + "'");
return false;
}

return true;
};

if (!assert(token)) {
console.log("ASSERT_TOKEN FAILED: retrying decryption using aes-256 rather than aes-256-ctr");

token = req.user.token = cryptrFallback.decrypt(req.session.token);

if (!assert(token)) {
Expand All @@ -137,7 +137,7 @@ module.exports = function middlewareConstructor(config) {
qs = `?${qs}`;
}

if(req.session.user) {
if(req.session.passport.user) {
next();
} else {
res.redirect(307, `/${locale}/anonymous/${uuid.v4()}${qs}`);
Expand Down Expand Up @@ -286,6 +286,13 @@ module.exports = function middlewareConstructor(config) {
clearRedirects(req, res, next) {
delete req.session.home;
next();
},

logout(req, res) {
let locale = (req.localeInfo && req.localeInfo.lang) ? req.localeInfo.lang : "en-US";
req.logout();
req.session = null;
res.redirect(307, `/${locale}`);
}
};
};
31 changes: 31 additions & 0 deletions server/passport.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
let WebmakerStrategy = require("passport-webmaker").Strategy;
let url = require("url");
let Cryptr = require("cryptr");

let env = require("./lib/environment");
let oauth = env.get("OAUTH");

module.exports = function (passport) {
let cryptr = new Cryptr(env.get("SESSION_SECRET"));

passport.use(new WebmakerStrategy({
clientID: oauth.webmaker_client_id,
clientSecret: oauth.webmaker_client_secret,
authorizationURL: url.resolve(oauth.webmaker_auth_url, "/login/oauth/authorize"),
tokenURL: url.resolve(oauth.webmaker_auth_url, "/login/oauth/access_token"),
profileURL: url.resolve(oauth.webmaker_auth_url, "/user"),
state: true,
passReqToCallback: true
}, function (req, accessToken, refreshToken, profile, done) {
req.session.token = cryptr.encrypt(accessToken);
return done(null, profile);
}));

passport.serializeUser(function(user, done) {
done(null, user);
});

passport.deserializeUser(function(user, done) {
done(null, user);
});
};
6 changes: 6 additions & 0 deletions server/request.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,12 @@ Request.prototype = {
res.json({ http: 'okay', version: version });
});

return this;
},
passport(passport) {
this.server.use(passport.initialize());
this.server.use(passport.session());

return this;
}
};
Expand Down
42 changes: 42 additions & 0 deletions server/routes/auth/callback.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
var HttpError = require("../../lib/http-error");

module.exports = function(config, passport, req, res, next) {
var locale = req.session.locale;

if(!locale) {
// This can happen when we try to logout again when we are already
// logged out (i.e. the session doesn't exist and hence req.session.locale
// is undefined)
locale = (req.localeInfo && req.localeInfo.lang) ? req.localeInfo.lang : "en-US";
}

//var strategy = req.params.strategy.toLowerCase();
var editorURL = `/${locale}/editor`;

// TODO: When we implement multiple strategies, we need to incorporate this into an if/else or switch block.
// Right now we ignore the "strategy" variable, because we already know the only valid response is "webmaker".
passport.authenticate("webmaker", function(err, user) {
if (err) {
res.status(500);
return next(HttpError.format({
message: `(Passport) Failed to authenticate user.`,
context: err
}, req));
}

if (!user) {
return res.redirect(`/${locale}/login/webmaker`);
}

req.logIn(user, function(err) {
if (err) {
res.status(500);
return next(HttpError.format({
message: `(Passport) Failed to serialize user session cookie.`,
context: err
}, req));
}
return res.redirect(editorURL);
});
})(req, res, next);
};
13 changes: 8 additions & 5 deletions server/routes/auth/index.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
module.exports = {
init: function(app, middleware, config) {
app.get("/login",
require("./login").bind(app, config));
init: function(app, middleware, config, passport) {

app.get("/callback",
app.get("/login/:strategy",
require("./login").bind(app, config, passport));

app.get("/login/:strategy/callback",
middleware.setErrorMessage("errorAuthenticating"),
require("./oauth2-callback").bind(app, config));
require("./callback").bind(app, config, passport));

app.get("/logout", middleware.logout);
}
};
14 changes: 6 additions & 8 deletions server/routes/auth/login.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module.exports = function(config, req, res) {
module.exports = function(config, passport, req, res, next) {
if (req.query.anonymousId) {
req.session.project = {
anonymousId: req.query.anonymousId,
Expand All @@ -10,12 +10,10 @@ module.exports = function(config, req, res) {

req.session.locale = (req.localeInfo && req.localeInfo.lang) ? req.localeInfo.lang : "en-US";

var loginType = "&action=" + (req.query.signup ? "signup" : "signin");
var state = "&state=" + req.cookies.state;
//var strategy = req.params.strategy.toLowerCase();
var action = req.query.signup ? "signup" : "signin";

res.set({
"Cache-Control": "no-cache"
});

res.redirect(307, config.loginURL + state + loginType);
// TODO: When we implement multiple strategies, we need to incorporate this into an if/else or switch block.
// Right now we ignore the "strategy" variable, because we already know the only valid response is "webmaker".
passport.authenticate("webmaker", { scopes: ["user", "email"], action: action })(req, res, next);
};
Loading