mirror of
https://github.com/cryptpad/cryptpad.git
synced 2026-09-12 19:49:59 +05:00
SSO: OIDC auth
This commit is contained in:
parent
318202531f
commit
18d6ccdfd3
1
.gitignore
vendored
1
.gitignore
vendored
@ -22,4 +22,5 @@ block/
|
||||
logs/
|
||||
privileged.conf
|
||||
config/config.js
|
||||
config/sso.js
|
||||
*.sh
|
||||
|
||||
21
config/sso.example.js
Normal file
21
config/sso.example.js
Normal file
@ -0,0 +1,21 @@
|
||||
module.exports = {
|
||||
// Enable SSO login on this instance
|
||||
enabled: false,
|
||||
// Block registration for non-SSO users on this instance
|
||||
enforced: false,
|
||||
// Allow users to add an additional CryptPad password to their SSO account
|
||||
cpPassword: false,
|
||||
// List of SSO providers
|
||||
list: [
|
||||
/*
|
||||
{
|
||||
name: 'google',
|
||||
type: 'oidc',
|
||||
url: 'https://accounts.google.com',
|
||||
client_id: "{your_client_id}",
|
||||
client_secret: "{your_client_secret}"
|
||||
}
|
||||
*/
|
||||
]
|
||||
};
|
||||
|
||||
@ -43,6 +43,39 @@ define([
|
||||
setRedirectTo();
|
||||
}
|
||||
|
||||
Exports.ssoRegister = function (provider, cb) {
|
||||
var keys = Nacl.sign.keyPair();
|
||||
localStorage.CP_sso_auth = JSON.stringify({
|
||||
s: Nacl.util.encodeBase64(keys.secretKey),
|
||||
p: Nacl.util.encodeBase64(keys.publicKey)
|
||||
});
|
||||
ServerCommand(keys, {
|
||||
command: 'SSO_AUTH',
|
||||
provider: provider,
|
||||
register: true
|
||||
}, cb);
|
||||
};
|
||||
Exports.ssoRegisterCb = function () {
|
||||
var b64Keys = Util.tryParse(localStorage.CP_sso_auth);
|
||||
if (!b64Keys) {
|
||||
throw new Error("MISSING_SIGNATURE_KEYS");
|
||||
}
|
||||
var keys = {
|
||||
secretKey: Nacl.util.decodeBase64(b64Keys.s),
|
||||
publicKey: Nacl.util.decodeBase64(b64Keys.p)
|
||||
};
|
||||
ServerCommand(keys, {
|
||||
command: 'SSO_AUTH_CB',
|
||||
url: window.location.href
|
||||
}, function (err, data) {
|
||||
delete localStorage.CP_sso_auth;
|
||||
if (data && data.state) { window.location.href = '/register/'; }
|
||||
});
|
||||
};
|
||||
Exports.ssoLogin = function () {
|
||||
|
||||
};
|
||||
|
||||
var allocateBytes = Exports.allocateBytes = function (bytes) {
|
||||
var dispense = Cred.dispenser(bytes);
|
||||
|
||||
|
||||
@ -38,6 +38,8 @@ define([
|
||||
undefined:
|
||||
h('button#register.cp-secondary', Msg.login_register)
|
||||
),
|
||||
h('button.login', Msg.login_login),
|
||||
h('br'),
|
||||
h('button.login', Msg.login_login)
|
||||
])
|
||||
]),
|
||||
|
||||
@ -79,7 +79,8 @@ define([
|
||||
UI.createCheckbox('import-recent', Msg.register_importRecent, true)
|
||||
]),
|
||||
termsCheck,
|
||||
h('button#register', Msg.login_register)
|
||||
h('button#register', Msg.login_register),
|
||||
Config.sso ? h('div.cp-register-sso') : undefined
|
||||
])
|
||||
]),
|
||||
])
|
||||
|
||||
105
lib/challenge-commands/sso.js
Normal file
105
lib/challenge-commands/sso.js
Normal file
@ -0,0 +1,105 @@
|
||||
const Util = require("../common-util");
|
||||
const Commands = module.exports;
|
||||
const SSOUtils = require('../sso-utils');
|
||||
|
||||
const checkConfig = (Env) => {
|
||||
return Env && Env.sso && Env.sso.enabled && Array.isArray(Env.sso.list) && Env.sso.list.length;
|
||||
};
|
||||
const getProviderConfig = (Env, provider) => {
|
||||
if (!checkConfig) { return; }
|
||||
if (!provider) { return; }
|
||||
const data = Env.sso.list.find((cfg) => { return cfg.name === provider; });
|
||||
return data;
|
||||
};
|
||||
|
||||
const TYPES = SSOUtils.TYPES;
|
||||
|
||||
const isValidConfig = (cfg) => {
|
||||
if (!cfg) { return; }
|
||||
if (!cfg.type) { return; }
|
||||
const type = cfg.type.toLowerCase();
|
||||
const idp = TYPES[type];
|
||||
if (!idp) { return; }
|
||||
return idp.checkConfig(cfg);
|
||||
};
|
||||
|
||||
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});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
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'); }
|
||||
console.log('stored', data.publicKey);
|
||||
console.log('used', publicKey);
|
||||
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];
|
||||
idp.authCb(Env, cfg, ssotoken, url, (err, obj) => {
|
||||
if (err) { return void cb(err); }
|
||||
const {id, idpData} = obj;
|
||||
console.log(id, idpData);
|
||||
SSOUtils.makeUser(Env, id, (err, userData) => {
|
||||
if (err) { return void cb(err); }
|
||||
|
||||
// TODO
|
||||
// makeTempSession()
|
||||
|
||||
cb(void 0, { state: true });
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
@ -223,6 +223,8 @@ module.exports.create = function (config) {
|
||||
evictionReport: {},
|
||||
commandTimers: {},
|
||||
|
||||
sso: config.sso,
|
||||
|
||||
// initialized as undefined
|
||||
bearerSecret: void 0,
|
||||
curvePrivate: curve.secretKey,
|
||||
|
||||
@ -73,6 +73,9 @@ COMMANDS.TOTP_REVOKE = TOTP.TOTP_REVOKE;
|
||||
COMMANDS.TOTP_WRITE_BLOCK = TOTP.TOTP_WRITE_BLOCK;
|
||||
COMMANDS.TOTP_REMOVE_BLOCK = TOTP.TOTP_REMOVE_BLOCK;
|
||||
|
||||
const SSO = require("./challenge-commands/sso.js");
|
||||
COMMANDS.SSO_AUTH = SSO.SSO_AUTH;
|
||||
COMMANDS.SSO_AUTH_CB = SSO.SSO_AUTH_CB;
|
||||
|
||||
var randomToken = () => Nacl.util.encodeBase64(Nacl.randomBytes(24)).replace(/\//g, '-');
|
||||
|
||||
@ -141,7 +144,7 @@ var handleCommand = function (Env, req, res) {
|
||||
date: date,
|
||||
});
|
||||
});
|
||||
});
|
||||
}, req);
|
||||
} catch (err) {
|
||||
Env.Log.error("CHALLENGE_COMMAND_THROWN_ERROR", {
|
||||
error: Util.serializeError(err),
|
||||
@ -290,7 +293,7 @@ var handleResponse = function (Env, req, res) {
|
||||
});
|
||||
}
|
||||
res.status(200).json(content);
|
||||
});
|
||||
}, req, res);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@ -10,6 +10,7 @@ const AuthCommands = require("./http-commands");
|
||||
const JWT = require("jsonwebtoken");
|
||||
const MFA = require("./storage/mfa");
|
||||
const Sessions = require("./storage/sessions");
|
||||
const cookieParser = require("cookie-parser");
|
||||
|
||||
const DEFAULT_QUERY_TIMEOUT = 5000;
|
||||
const PID = process.pid;
|
||||
@ -158,6 +159,7 @@ var setHeaders = function (req, res) {
|
||||
|
||||
const Express = require("express");
|
||||
var app = Express();
|
||||
app.use(cookieParser());
|
||||
|
||||
(function () {
|
||||
if (!Env.logFeedback) { return; }
|
||||
@ -187,7 +189,6 @@ const wsProxy = createProxyMiddleware({
|
||||
|
||||
app.use('/cryptpad_websocket', wsProxy);
|
||||
|
||||
|
||||
app.use('/blob', function (req, res, next) {
|
||||
/* Head requests are used to check the size of a blob.
|
||||
Clients can configure a maximum size to download automatically,
|
||||
@ -484,6 +485,13 @@ var makeRouteCache = function (template, cacheName) {
|
||||
};
|
||||
};
|
||||
|
||||
const ssoList = Env.sso && Env.sso.enabled && Array.isArray(Env.sso.list) &&
|
||||
Env.sso.list.map(function (obj) { return obj.name; });
|
||||
const ssoCfg = ssoList.length ? {
|
||||
force: (Env.sso && Env.sso.enforced && 1) || 0,
|
||||
password: (Env.sso && Env.sso.cpPassword && 1) || 0,
|
||||
list: ssoList
|
||||
} : false;
|
||||
var serveConfig = makeRouteCache(function () {
|
||||
return [
|
||||
'define(function(){',
|
||||
@ -510,6 +518,7 @@ var serveConfig = makeRouteCache(function () {
|
||||
shouldUpdateNode: Env.shouldUpdateNode || undefined,
|
||||
listMyInstance: Env.listMyInstance,
|
||||
accounts_api: Env.accounts_api,
|
||||
sso: ssoCfg
|
||||
}, null, '\t'),
|
||||
'});'
|
||||
].join(';\n');
|
||||
|
||||
@ -42,5 +42,12 @@ if (!isPositiveNumber(config.premiumUploadSize) || config.premiumUploadSize < co
|
||||
delete config.premiumUploadSize;
|
||||
}
|
||||
|
||||
config.sso = {};
|
||||
try {
|
||||
config.sso = require("../config/sso");
|
||||
} catch (e) {
|
||||
console.log("SSO config not found");
|
||||
}
|
||||
|
||||
module.exports = config;
|
||||
|
||||
|
||||
67
lib/plugins/sso/oidc.js
Normal file
67
lib/plugins/sso/oidc.js
Normal file
@ -0,0 +1,67 @@
|
||||
const OID = require('openid-client');
|
||||
|
||||
const TYPE = 'oidc';
|
||||
|
||||
const getClient = (cfg, cb) => {
|
||||
OID.Issuer.discover(cfg.url).then((issuer) => { // XXX Only once for all users?
|
||||
console.log('Discovered issuer %s %O', issuer.issuer);
|
||||
const client = new issuer.Client({
|
||||
client_id: cfg.client_id,
|
||||
client_secret: cfg.client_secret,
|
||||
redirect_uris: ['http://localhost:3000/ssoauth'], // XXX Use httpUnsafeOrigin or...
|
||||
response_types: ['code'],
|
||||
});
|
||||
cb(void 0, client);
|
||||
}, (err) => {
|
||||
cb(err);
|
||||
});
|
||||
};
|
||||
module.exports = {
|
||||
type: TYPE,
|
||||
checkConfig: (cfg) => {
|
||||
return cfg.url && cfg.client_id && cfg.client_secret;
|
||||
},
|
||||
auth: (Env, cfg, cb) => {
|
||||
getClient(cfg, (err, client) => {
|
||||
if (err) { return void cb ('E_OIDC_CONNECT'); }
|
||||
|
||||
const generators = OID.generators;
|
||||
const code_verifier = generators.codeVerifier();
|
||||
// store the code_verifier in your framework's session mechanism, if it is a cookie based solution
|
||||
// it should be httpOnly (not readable by javascript) and encrypted.
|
||||
|
||||
console.log(code_verifier);
|
||||
const code_challenge = generators.codeChallenge(code_verifier);
|
||||
const url = client.authorizationUrl({
|
||||
scope: 'openid email profile',// https://www.googleapis.com/auth/contacts.readonly', // https://www.google.com/m8/feeds
|
||||
resource: 'http://localhost:3000/ssoauth/',
|
||||
access_type: 'offline',
|
||||
code_challenge,
|
||||
code_challenge_method: 'S256',
|
||||
});
|
||||
|
||||
cb(void 0, { url: url, token: code_verifier });
|
||||
});
|
||||
},
|
||||
authCb: (Env, cfg, token, url, cb) => {
|
||||
getClient(cfg, (err, client) => {
|
||||
if (err) { return void cb ('E_OIDC_CONNECT'); }
|
||||
|
||||
const params = client.callbackParams(url);
|
||||
client.callback('http://localhost:3000/ssoauth', params, { code_verifier: token })
|
||||
.then((tokenSet) => {
|
||||
let j = tokenSet;
|
||||
let c = tokenSet.claims();
|
||||
console.log(j, c);
|
||||
cb(void 0, {
|
||||
id: c.sub,
|
||||
idpData: {
|
||||
access_token: j.access_token,
|
||||
refresh_token: j.refresh_token,
|
||||
id_token: j.id_token
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
71
lib/sso-utils.js
Normal file
71
lib/sso-utils.js
Normal file
@ -0,0 +1,71 @@
|
||||
const SSO = require("./storage/sso");
|
||||
const Nacl = require("tweetnacl/nacl-fast");
|
||||
|
||||
const SSOUtils = module.exports;
|
||||
|
||||
// XXX const SAML = require('node-saml'); // https://www.npmjs.com/package/node-saml
|
||||
SSOUtils.TYPES = {
|
||||
oidc: require('./plugins/sso/oidc')
|
||||
};
|
||||
|
||||
|
||||
SSOUtils.deleteRequest = (Env, id) => {
|
||||
SSO.request.delete(Env, id, (err) => {
|
||||
if (!err) { return; }
|
||||
console.log(`Failed to delete SSO request ${id}`);
|
||||
// XXX log?
|
||||
});
|
||||
};
|
||||
SSOUtils.readRequest = (Env, id, cb) => {
|
||||
SSO.request.read(Env, id, cb);
|
||||
};
|
||||
SSOUtils.writeRequest = (Env, data, cb) => {
|
||||
if (!data || !data.id || !data.type) { return void cb ('INVALID_REQUEST'); }
|
||||
const id = data.id;
|
||||
const value = {
|
||||
type: data.type,
|
||||
register: data.register,
|
||||
provider: data.provider,
|
||||
publicKey: data.publicKey,
|
||||
time: +new Date()
|
||||
};
|
||||
|
||||
SSO.request.write(Env, id, JSON.stringify(value), cb);
|
||||
};
|
||||
|
||||
|
||||
SSOUtils.makeUser = (Env, id, cb) => {
|
||||
const seed = Nacl.util.encodeBase64(Nacl.util.randomBytes(24));
|
||||
SSO.User.write(Env, id, JSON.stringify({
|
||||
seed: seed,
|
||||
password: false // XXX maybe we don't need that flag, we can check if the block exists later
|
||||
}), (err) => {
|
||||
if (err) { return void cb(err); }
|
||||
cb(void 0, { seed });
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
SSOUtils.makeSession = (Env, publicKey, provider, ssoData, cb) => {
|
||||
const sessionId = Sessions.randomId();
|
||||
// XXX If we already have an OTP session, recover it
|
||||
Sessions.write(Env, publicKey, sessionId, JSON.stringify({
|
||||
sso: {
|
||||
provider: provider,
|
||||
data: ssoData
|
||||
}
|
||||
}), function (err) {
|
||||
if (err) {
|
||||
Env.Log.error("SSO_SESSION_WRITE", {
|
||||
error: Util.serializeError(err),
|
||||
publicKey: publicKey,
|
||||
sessionId: sessionId,
|
||||
});
|
||||
return void cb("SESSION_WRITE_ERROR");
|
||||
}
|
||||
cb(void 0, {
|
||||
bearer: sessionId,
|
||||
});
|
||||
});
|
||||
|
||||
};
|
||||
57
lib/storage/sso.js
Normal file
57
lib/storage/sso.js
Normal file
@ -0,0 +1,57 @@
|
||||
const Basic = require("./basic");
|
||||
const Path = require("node:path");
|
||||
const Util = require("../common-util");
|
||||
|
||||
const SSO = module.exports;
|
||||
/* This module manages storage related to Single Sign-On (SSO) settings.
|
||||
|
||||
A first part (sso-requests) contains temporary files for sso authentication with a remote service
|
||||
and a second part (sso users) is a database of accounts registered via SSO.
|
||||
|
||||
The path for requests is based on the "authentication request" token depending on the type of SSO.
|
||||
The path for the user database is based on their persistent identifier (id) from the SSO.
|
||||
*/
|
||||
|
||||
var pathFromId = function (Env, id, subPath) {
|
||||
if (!id || typeof(id) !== 'string') { return; }
|
||||
id = Util.escapeKeyCharacters(id);
|
||||
return Path.join(Env.paths.base, subPath, id.slice(0, 2), `${id}.json`);
|
||||
};
|
||||
var reqPathFromId = function (Env, id) {
|
||||
return pathFromId(Env, id, 'sso_request');
|
||||
};
|
||||
var userPathFromId = function (Env, id) {
|
||||
return pathFromId(Env, id, 'sso');
|
||||
};
|
||||
|
||||
const Req = SSO.request = {};
|
||||
|
||||
Req.read = function (Env, id, cb) {
|
||||
var path = reqPathFromId(Env, id);
|
||||
Basic.read(Env, path, cb);
|
||||
};
|
||||
Req.write = function (Env, id, data, cb) {
|
||||
var path = reqPathFromId(Env, id);
|
||||
console.log(path);
|
||||
Basic.write(Env, path, data, cb);
|
||||
};
|
||||
Req.delete = function (Env, id, cb) {
|
||||
var path = reqPathFromId(Env, id);
|
||||
Basic.delete(Env, path, cb);
|
||||
};
|
||||
|
||||
|
||||
const User = SSO.user = {};
|
||||
|
||||
User.read = function (Env, id, cb) {
|
||||
var path = userPathFromId(Env, id);
|
||||
Basic.read(Env, path, cb);
|
||||
};
|
||||
User.write = function (Env, id, data, cb) {
|
||||
var path = userPathFromId(Env, id);
|
||||
Basic.write(Env, path, data, cb);
|
||||
};
|
||||
User.delete = function (Env, id, cb) {
|
||||
var path = userPathFromId(Env, id);
|
||||
Basic.delete(Env, path, cb);
|
||||
};
|
||||
250
package-lock.json
generated
250
package-lock.json
generated
@ -12,6 +12,7 @@
|
||||
"@mcrowe/minibloom": "^0.2.0",
|
||||
"chainpad-crypto": "^0.2.5",
|
||||
"chainpad-server": "^5.1.0",
|
||||
"cookie-parser": "^1.4.6",
|
||||
"express": "~4.18.2",
|
||||
"fs-extra": "^7.0.0",
|
||||
"get-folder-size": "^2.0.1",
|
||||
@ -20,6 +21,7 @@
|
||||
"netflux-websocket": "^0.1.20",
|
||||
"notp": "^2.0.3",
|
||||
"nthen": "0.1.8",
|
||||
"openid-client": "^5.4.2",
|
||||
"prompt-confirm": "^2.0.4",
|
||||
"pull-stream": "^3.6.1",
|
||||
"saferphore": "0.0.1",
|
||||
@ -1011,6 +1013,26 @@
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/cookie-parser": {
|
||||
"version": "1.4.6",
|
||||
"resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz",
|
||||
"integrity": "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==",
|
||||
"dependencies": {
|
||||
"cookie": "0.4.1",
|
||||
"cookie-signature": "1.0.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/cookie-parser/node_modules/cookie": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz",
|
||||
"integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/cookie-signature": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
|
||||
@ -1749,25 +1771,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/glob-parent": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
|
||||
"integrity": "sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==",
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
||||
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"is-glob": "^3.1.0",
|
||||
"path-dirname": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/glob-parent/node_modules/is-glob": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
|
||||
"integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"is-extglob": "^2.1.0"
|
||||
"is-glob": "^4.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/glob-to-regexp": {
|
||||
@ -2220,6 +2232,14 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/jose": {
|
||||
"version": "4.14.4",
|
||||
"resolved": "https://registry.npmjs.org/jose/-/jose-4.14.4.tgz",
|
||||
"integrity": "sha512-j8GhLiKmUAh+dsFXlX1aJCbt5KMibuKb+d7j1JaOJG6s2UjX1PQlW+OKB/sD4a/5ZYF4RcmYmLSndOoU3Lt/3g==",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/panva"
|
||||
}
|
||||
},
|
||||
"node_modules/js-yaml": {
|
||||
"version": "3.14.1",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
|
||||
@ -2727,6 +2747,14 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/object-hash": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz",
|
||||
"integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==",
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/object-inspect": {
|
||||
"version": "1.12.2",
|
||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz",
|
||||
@ -2758,6 +2786,14 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/oidc-token-hash": {
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.0.3.tgz",
|
||||
"integrity": "sha512-IF4PcGgzAr6XXSff26Sk/+P4KZFJVuHAJZj3wgO3vX2bMdNVp/QXTP3P7CEm9V1IdG8lDLY3HhiqpsE/nOwpPw==",
|
||||
"engines": {
|
||||
"node": "^10.13.0 || >=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/on-finished": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
|
||||
@ -2778,6 +2814,20 @@
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"node_modules/openid-client": {
|
||||
"version": "5.4.2",
|
||||
"resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.4.2.tgz",
|
||||
"integrity": "sha512-lIhsdPvJ2RneBm3nGBBhQchpe3Uka//xf7WPHTIglery8gnckvW7Bd9IaQzekzXJvWthCMyi/xVEyGW0RFPytw==",
|
||||
"dependencies": {
|
||||
"jose": "^4.14.1",
|
||||
"lru-cache": "^6.0.0",
|
||||
"object-hash": "^2.2.0",
|
||||
"oidc-token-hash": "^5.0.3"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/panva"
|
||||
}
|
||||
},
|
||||
"node_modules/parse-json": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
|
||||
@ -2808,12 +2858,6 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/path-dirname": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz",
|
||||
"integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/path-is-absolute": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||
@ -3039,17 +3083,6 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prompt-choices/node_modules/set-value": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/set-value/-/set-value-3.0.3.tgz",
|
||||
"integrity": "sha512-Xsn/XSatoVOGBbp5hs3UylFDs5Bi9i+ArpVJKdHPniZHoEgRniXTqHWrWrGQ0PbEClVT6WtfnBwR8CAHC9sveg==",
|
||||
"dependencies": {
|
||||
"is-plain-object": "^2.0.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prompt-choices/node_modules/shallow-clone": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz",
|
||||
@ -3391,39 +3424,19 @@
|
||||
}
|
||||
},
|
||||
"node_modules/set-value": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",
|
||||
"integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==",
|
||||
"dev": true,
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/set-value/-/set-value-4.0.1.tgz",
|
||||
"integrity": "sha512-ayATicCYPVnlNpFmjq2/VmVwhoCQA9+13j8qWp044fmFE3IFphosPtRM+0CJ5xoIx5Uy52fCcwg3XeH2pHbbPQ==",
|
||||
"funding": [
|
||||
"https://github.com/sponsors/jonschlinkert",
|
||||
"https://paypal.me/jonathanschlinkert",
|
||||
"https://jonschlinkert.dev/sponsor"
|
||||
],
|
||||
"dependencies": {
|
||||
"extend-shallow": "^2.0.1",
|
||||
"is-extendable": "^0.1.1",
|
||||
"is-plain-object": "^2.0.3",
|
||||
"split-string": "^3.0.1"
|
||||
"is-plain-object": "^2.0.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/set-value/node_modules/extend-shallow": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
|
||||
"integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"is-extendable": "^0.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/set-value/node_modules/is-extendable": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
|
||||
"integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
"node": ">=11.0"
|
||||
}
|
||||
},
|
||||
"node_modules/setprototypeof": {
|
||||
@ -4738,7 +4751,7 @@
|
||||
"get-value": "^2.0.6",
|
||||
"has-value": "^1.0.0",
|
||||
"isobject": "^3.0.1",
|
||||
"set-value": "^2.0.0",
|
||||
"set-value": "4.0.1",
|
||||
"to-object-path": "^0.3.0",
|
||||
"union-value": "^1.0.0",
|
||||
"unset-value": "^1.0.0"
|
||||
@ -4980,6 +4993,22 @@
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
|
||||
"integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw=="
|
||||
},
|
||||
"cookie-parser": {
|
||||
"version": "1.4.6",
|
||||
"resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz",
|
||||
"integrity": "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==",
|
||||
"requires": {
|
||||
"cookie": "0.4.1",
|
||||
"cookie-signature": "1.0.6"
|
||||
},
|
||||
"dependencies": {
|
||||
"cookie": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz",
|
||||
"integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"cookie-signature": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
|
||||
@ -5387,7 +5416,7 @@
|
||||
"requires": {
|
||||
"@mrmlnc/readdir-enhanced": "^2.2.1",
|
||||
"@nodelib/fs.stat": "^1.1.2",
|
||||
"glob-parent": "^3.1.0",
|
||||
"glob-parent": "5.1.2",
|
||||
"is-glob": "^4.0.0",
|
||||
"merge2": "^1.2.3",
|
||||
"micromatch": "^3.1.10"
|
||||
@ -5556,24 +5585,12 @@
|
||||
}
|
||||
},
|
||||
"glob-parent": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
|
||||
"integrity": "sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==",
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
||||
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-glob": "^3.1.0",
|
||||
"path-dirname": "^1.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"is-glob": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
|
||||
"integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-extglob": "^2.1.0"
|
||||
}
|
||||
}
|
||||
"is-glob": "^4.0.1"
|
||||
}
|
||||
},
|
||||
"glob-to-regexp": {
|
||||
@ -5907,6 +5924,11 @@
|
||||
"resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
|
||||
"integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8="
|
||||
},
|
||||
"jose": {
|
||||
"version": "4.14.4",
|
||||
"resolved": "https://registry.npmjs.org/jose/-/jose-4.14.4.tgz",
|
||||
"integrity": "sha512-j8GhLiKmUAh+dsFXlX1aJCbt5KMibuKb+d7j1JaOJG6s2UjX1PQlW+OKB/sD4a/5ZYF4RcmYmLSndOoU3Lt/3g=="
|
||||
},
|
||||
"js-yaml": {
|
||||
"version": "3.14.1",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
|
||||
@ -6309,6 +6331,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"object-hash": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz",
|
||||
"integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw=="
|
||||
},
|
||||
"object-inspect": {
|
||||
"version": "1.12.2",
|
||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz",
|
||||
@ -6331,6 +6358,11 @@
|
||||
"isobject": "^3.0.1"
|
||||
}
|
||||
},
|
||||
"oidc-token-hash": {
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.0.3.tgz",
|
||||
"integrity": "sha512-IF4PcGgzAr6XXSff26Sk/+P4KZFJVuHAJZj3wgO3vX2bMdNVp/QXTP3P7CEm9V1IdG8lDLY3HhiqpsE/nOwpPw=="
|
||||
},
|
||||
"on-finished": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
|
||||
@ -6348,6 +6380,17 @@
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"openid-client": {
|
||||
"version": "5.4.2",
|
||||
"resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.4.2.tgz",
|
||||
"integrity": "sha512-lIhsdPvJ2RneBm3nGBBhQchpe3Uka//xf7WPHTIglery8gnckvW7Bd9IaQzekzXJvWthCMyi/xVEyGW0RFPytw==",
|
||||
"requires": {
|
||||
"jose": "^4.14.1",
|
||||
"lru-cache": "^6.0.0",
|
||||
"object-hash": "^2.2.0",
|
||||
"oidc-token-hash": "^5.0.3"
|
||||
}
|
||||
},
|
||||
"parse-json": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
|
||||
@ -6369,12 +6412,6 @@
|
||||
"integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=",
|
||||
"dev": true
|
||||
},
|
||||
"path-dirname": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz",
|
||||
"integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=",
|
||||
"dev": true
|
||||
},
|
||||
"path-is-absolute": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||
@ -6528,7 +6565,7 @@
|
||||
"log-utils": "^0.2.1",
|
||||
"pointer-symbol": "^1.0.0",
|
||||
"radio-symbol": "^2.0.0",
|
||||
"set-value": "^3.0.0",
|
||||
"set-value": "4.0.1",
|
||||
"strip-color": "^0.1.0",
|
||||
"terminal-paginator": "^2.0.2",
|
||||
"toggle-array": "^1.0.1"
|
||||
@ -6549,14 +6586,6 @@
|
||||
"resolved": "https://registry.npmjs.org/is-number/-/is-number-6.0.0.tgz",
|
||||
"integrity": "sha512-Wu1VHeILBK8KAWJUAiSZQX94GmOE45Rg6/538fKwiloUu21KncEkYGPqob2oSZ5mUT73vLGrHQjKw3KMPwfDzg=="
|
||||
},
|
||||
"set-value": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/set-value/-/set-value-3.0.3.tgz",
|
||||
"integrity": "sha512-Xsn/XSatoVOGBbp5hs3UylFDs5Bi9i+ArpVJKdHPniZHoEgRniXTqHWrWrGQ0PbEClVT6WtfnBwR8CAHC9sveg==",
|
||||
"requires": {
|
||||
"is-plain-object": "^2.0.4"
|
||||
}
|
||||
},
|
||||
"shallow-clone": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz",
|
||||
@ -6836,32 +6865,11 @@
|
||||
}
|
||||
},
|
||||
"set-value": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",
|
||||
"integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==",
|
||||
"dev": true,
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/set-value/-/set-value-4.0.1.tgz",
|
||||
"integrity": "sha512-ayATicCYPVnlNpFmjq2/VmVwhoCQA9+13j8qWp044fmFE3IFphosPtRM+0CJ5xoIx5Uy52fCcwg3XeH2pHbbPQ==",
|
||||
"requires": {
|
||||
"extend-shallow": "^2.0.1",
|
||||
"is-extendable": "^0.1.1",
|
||||
"is-plain-object": "^2.0.3",
|
||||
"split-string": "^3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"extend-shallow": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
|
||||
"integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-extendable": "^0.1.0"
|
||||
}
|
||||
},
|
||||
"is-extendable": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
|
||||
"integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=",
|
||||
"dev": true
|
||||
}
|
||||
"is-plain-object": "^2.0.4"
|
||||
}
|
||||
},
|
||||
"setprototypeof": {
|
||||
@ -7351,7 +7359,7 @@
|
||||
"arr-union": "^3.1.0",
|
||||
"get-value": "^2.0.6",
|
||||
"is-extendable": "^0.1.1",
|
||||
"set-value": "^2.0.1"
|
||||
"set-value": "4.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"is-extendable": {
|
||||
|
||||
@ -15,6 +15,7 @@
|
||||
"@mcrowe/minibloom": "^0.2.0",
|
||||
"chainpad-crypto": "^0.2.5",
|
||||
"chainpad-server": "^5.1.0",
|
||||
"cookie-parser": "^1.4.6",
|
||||
"express": "~4.18.2",
|
||||
"fs-extra": "^7.0.0",
|
||||
"get-folder-size": "^2.0.1",
|
||||
@ -23,6 +24,7 @@
|
||||
"netflux-websocket": "^0.1.20",
|
||||
"notp": "^2.0.3",
|
||||
"nthen": "0.1.8",
|
||||
"openid-client": "^5.4.2",
|
||||
"prompt-confirm": "^2.0.4",
|
||||
"pull-stream": "^3.6.1",
|
||||
"saferphore": "0.0.1",
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
define([
|
||||
'/api/config',
|
||||
'jquery',
|
||||
'/customize/login.js',
|
||||
'/common/cryptpad-common.js',
|
||||
@ -14,7 +15,7 @@ define([
|
||||
'/customize/pages.js',
|
||||
|
||||
'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
|
||||
], function ($, Login, Cryptpad, /*Test,*/ Cred, UI, Util, Realtime, Constants, Feedback, LocalStore, h, Pages) {
|
||||
], function (Config, $, Login, Cryptpad, /*Test,*/ Cred, UI, Util, Realtime, Constants, Feedback, LocalStore, h, Pages) {
|
||||
if (window.top !== window) { return; }
|
||||
var Messages = Cryptpad.Messages;
|
||||
$(function () {
|
||||
@ -50,6 +51,30 @@ define([
|
||||
var br = function () { return h('br'); };
|
||||
Messages.register_nameTooLong = "Usernames must be shorter than {0} characters"; // XXX
|
||||
|
||||
if (Config.sso) {
|
||||
// TODO
|
||||
// Config.sso.force => no legacy login allowed
|
||||
// Config.sso.password => cp password required or forbidden
|
||||
// Config.sso.list => list of configured identity providers
|
||||
var $sso = $('div.cp-register-sso');
|
||||
var list = Config.sso.list.map(function (name) {
|
||||
var b = h('button.btn.btn-secondary', name);
|
||||
var $b = $(b).click(function () {
|
||||
console.log('sso register click:', name);
|
||||
$b.prop('disabled', 'disabled');
|
||||
Login.ssoRegister(name, function (err, data) {
|
||||
console.error(err, data);
|
||||
if (data.url) {
|
||||
window.location.href = data.url;
|
||||
}
|
||||
});
|
||||
// XXX Server command
|
||||
});
|
||||
return b;
|
||||
});
|
||||
$sso.append(list);
|
||||
}
|
||||
|
||||
var registerClick = function () {
|
||||
var uname = $uname.val().trim();
|
||||
// trim whitespace surrounding the username since it is otherwise included in key derivation
|
||||
|
||||
14
www/ssoauth/index.html
Normal file
14
www/ssoauth/index.html
Normal file
@ -0,0 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<!-- If this file is not called customize.dist/src/template.html, it is generated -->
|
||||
<head>
|
||||
<title data-localization="main_title">CryptPad: Collaboration suite, encrypted and open-source</title>
|
||||
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
|
||||
<link rel="icon" type="image/png" href="/customize/favicon/main-favicon.png" id="favicon"/>
|
||||
<script async data-bootload="/ssoauth/main.js" data-main="/common/boot.js?ver=1.0" src="/bower_components/requirejs/require.js?ver=2.3.5"></script>
|
||||
</head>
|
||||
<body class="html">
|
||||
<noscript></noscript>
|
||||
|
||||
|
||||
3
www/ssoauth/main.js
Normal file
3
www/ssoauth/main.js
Normal file
@ -0,0 +1,3 @@
|
||||
define(['/customize/login.js'], function (Login) {
|
||||
Login.ssoRegisterCb();
|
||||
});
|
||||
Loading…
Reference in New Issue
Block a user