mirror of
https://github.com/cryptpad/cryptpad.git
synced 2026-09-14 11:05:41 +05:00
230 lines
7.9 KiB
JavaScript
230 lines
7.9 KiB
JavaScript
const Util = require("../common-util");
|
|
const Commands = module.exports;
|
|
const SSOUtils = require('../sso-utils');
|
|
const nThen = require("nthen");
|
|
const BlockStore = require("../storage/block");
|
|
const Block = require("../commands/block");
|
|
|
|
const TYPES = SSOUtils.TYPES;
|
|
const checkConfig = SSOUtils.checkConfig;
|
|
const getProviderConfig = SSOUtils.getProviderConfig;
|
|
const isValidConfig = SSOUtils.isValidConfig;
|
|
|
|
|
|
// Create an SSO authentication request
|
|
const auth = Commands.SSO_AUTH = function (Env, body, cb) {
|
|
if (!checkConfig(Env)) { return void cb('INVALID_SERVER_CONFIG'); }
|
|
const { provider } = body;
|
|
const cfg = getProviderConfig(Env, provider);
|
|
if (!cfg) { return void cb('UNRECOGNIZED_IDP'); }
|
|
if (!isValidConfig(cfg)) { return void cb('INVALID_IDP_CONFIG'); }
|
|
|
|
cb();
|
|
};
|
|
auth.complete = function (Env, body, cb, req, res) {
|
|
// If we're here it means a valid provider was given. We can start the authentication process
|
|
const { provider, register, publicKey } = body;
|
|
const cfg = getProviderConfig(Env, provider);
|
|
const idp = TYPES[cfg.type.toLowerCase()];
|
|
idp.auth(Env, cfg, (err, obj) => {
|
|
if (err) { return void cb(err); } // TODO log
|
|
|
|
const { url, token } = obj;
|
|
|
|
SSOUtils.writeRequest(Env, {
|
|
id: token,
|
|
type: idp.type,
|
|
provider: provider,
|
|
publicKey: publicKey,
|
|
register: Boolean(register)
|
|
}, (err) => {
|
|
if (err) { return void cb("E_REQ_WRITE"); }
|
|
|
|
let value = `ssotoken="${token}"; SameSite=Strict; HttpOnly`;
|
|
res.setHeader('Set-Cookie', value);
|
|
cb(void 0, {url: url});
|
|
});
|
|
});
|
|
};
|
|
|
|
// Receive authentication data from the IdP.
|
|
// Read the auth request, create a JWT and get a block seed for this SSO user.
|
|
const authCb = Commands.SSO_AUTH_CB = function (Env, body, cb, req) {
|
|
if (!checkConfig(Env)) { return void cb('INVALID_SERVER_CONFIG'); }
|
|
const { publicKey } = body;
|
|
const cookies = req.cookies;
|
|
const ssotoken = cookies.ssotoken;
|
|
if (!ssotoken) { return void cb('NO_COOKIE'); }
|
|
SSOUtils.readRequest(Env, ssotoken, (err, value) => {
|
|
if (err) { return void cb('ENOENT'); }
|
|
const data = Util.tryParse(value);
|
|
if (!data || !data.type || !TYPES[data.type]) { return void cb('INVALID_REQUEST_DATA'); }
|
|
if (publicKey !== data.publicKey) { return void cb('WRONG_SIGNATURE_KEYS'); }
|
|
cb();
|
|
});
|
|
};
|
|
authCb.complete = function (Env, body, cb, req) {
|
|
// If we're here it means a valid cookie was given. We can continue the authentication
|
|
const { url } = body;
|
|
const cookies = req.cookies;
|
|
const ssotoken = cookies.ssotoken;
|
|
if (!ssotoken) { return void cb('NO_COOKIE'); }
|
|
SSOUtils.readRequest(Env, ssotoken, (err, value) => {
|
|
SSOUtils.deleteRequest(Env, ssotoken);
|
|
if (err) { return void cb('ENOENT'); }
|
|
const data = Util.tryParse(value);
|
|
const cfg = getProviderConfig(Env, data.provider);
|
|
const idp = TYPES[data.type];
|
|
const register = data.register;
|
|
idp.authCb(Env, cfg, ssotoken, url, (err, obj) => {
|
|
if (err) { return void cb(err); }
|
|
const {id, idpData, name} = obj;
|
|
|
|
let next = (user, isRegister) => {
|
|
SSOUtils.createJWT(Env, id, data.provider, idpData, (err, jwt) => {
|
|
if (err) { return void cb(err); }
|
|
cb(void 0, {
|
|
jwt: jwt,
|
|
name: name,
|
|
seed: user.seed,
|
|
password: Boolean(user.password),
|
|
register: isRegister
|
|
});
|
|
});
|
|
};
|
|
|
|
if (register) {
|
|
SSOUtils.writeUser(Env, id, (err, userData) => {
|
|
if (err && err.code === 'EEXIST') {
|
|
return void SSOUtils.readUser(Env, id, (err, userData) => {
|
|
if (err) { return void cb(err); }
|
|
next(userData, false);
|
|
});
|
|
}
|
|
if (err) { return void cb(err); }
|
|
next(userData, true);
|
|
});
|
|
} else {
|
|
SSOUtils.readUser(Env, id, (err, userData) => {
|
|
if (err) { return void cb(err); }
|
|
next(userData, false);
|
|
});
|
|
}
|
|
});
|
|
});
|
|
};
|
|
|
|
// XXX write block change-password? should be the same as otp but without otp code
|
|
const register = Commands.SSO_WRITE_BLOCK = function (Env, body, cb) {
|
|
const { publicKey, content } = body;
|
|
|
|
// they must provide a valid block public key
|
|
if (!Block.isValidBlockId(publicKey)) { return void cb("INVALID_KEY"); }
|
|
if (publicKey !== content.publicKey) { return void cb("INVALID_KEY"); }
|
|
const jwt = content.auth;
|
|
if (!jwt) { return void cb('NO_JWT'); }
|
|
|
|
BlockStore.isAvailable(Env, publicKey, (err, result) => {
|
|
if (err || result) {
|
|
return void cb(err || 'EEXISTS');
|
|
}
|
|
// No block at this location: continue
|
|
cb();
|
|
});
|
|
};
|
|
register.complete = function (Env, body, cb) {
|
|
const { publicKey, content } = body;
|
|
const jwt = content.auth;
|
|
const pw = content.hasPassword;
|
|
let payload;
|
|
let ssoUser;
|
|
// XXX UPDATE sso_user add password boolean
|
|
nThen((w) => {
|
|
SSOUtils.checkJWT(Env, jwt, w((err, _payload) => {
|
|
if (err) {
|
|
w.abort();
|
|
return void cb('INVALID_JWT_SIGNATURE');
|
|
}
|
|
payload = _payload;
|
|
}));
|
|
}).nThen((w) => {
|
|
const { sub } = payload;
|
|
SSOUtils.readUser(Env, sub, w((err, user) => {
|
|
if (err) {
|
|
w.abort();
|
|
console.log(err, sub);
|
|
return void cb('SSO_NO_USER');
|
|
}
|
|
ssoUser = user;
|
|
}));
|
|
}).nThen((w) => {
|
|
const { sub } = payload;
|
|
SSOUtils.writeBlock(Env, publicKey, sub, w((err) => {
|
|
if (err) {
|
|
w.abort();
|
|
return void cb('SSO_BLOCK_WRITE');
|
|
}
|
|
}));
|
|
}).nThen((w) => {
|
|
Block.writeLoginBlock(Env, content, w((err) => {
|
|
if (err) { w.abort(); }
|
|
}));
|
|
}).nThen((w) => {
|
|
if (!pw) { return; }
|
|
const { sub } = payload;
|
|
ssoUser.password = true;
|
|
SSOUtils.updateUser(Env, sub, ssoUser, w());
|
|
}).nThen(() => {
|
|
const { data, provider } = payload;
|
|
SSOUtils.makeSession(Env, publicKey, provider, data, cb);
|
|
});
|
|
};
|
|
|
|
const login = Commands.SSO_VALIDATE = function (Env, body, cb) {
|
|
const { publicKey, jwt } = body;
|
|
|
|
// they must provide a valid block public key
|
|
if (!Block.isValidBlockId(publicKey)) { return void cb("INVALID_KEY"); }
|
|
if (!jwt) { return void cb('NO_JWT'); }
|
|
|
|
BlockStore.isAvailable(Env, publicKey, (err, result) => {
|
|
if (err && !result) { return void cb(err); }
|
|
// Block found
|
|
cb();
|
|
});
|
|
};
|
|
login.complete = function (Env, body, cb) {
|
|
const { publicKey, jwt } = body;
|
|
|
|
let payload;
|
|
nThen((w) => {
|
|
SSOUtils.checkJWT(Env, jwt, w((err, _payload) => {
|
|
if (err) {
|
|
w.abort();
|
|
return void cb('INVALID_JWT_SIGNATURE');
|
|
}
|
|
payload = _payload;
|
|
}));
|
|
}).nThen((w) => {
|
|
const { sub } = payload;
|
|
SSOUtils.readUser(Env, sub, w((err) => {
|
|
if (err) {
|
|
w.abort();
|
|
console.log(err, sub);
|
|
return void cb('SSO_NO_USER');
|
|
}
|
|
}));
|
|
}).nThen((w) => {
|
|
SSOUtils.readBlock(Env, publicKey, w((err) => {
|
|
if (err) {
|
|
w.abort();
|
|
return void cb('SSO_BLOCK_WRITE');
|
|
}
|
|
}));
|
|
}).nThen(() => {
|
|
const { data, provider } = payload;
|
|
SSOUtils.makeSession(Env, publicKey, provider, data, cb);
|
|
});
|
|
|
|
};
|