diff --git a/src/oidcclients.js b/src/oidcclients.js
index a5ca42191..0536474e2 100644
--- a/src/oidcclients.js
+++ b/src/oidcclients.js
@@ -6,6 +6,14 @@ exports = module.exports = {
del,
update,
list,
+
+ validateId,
+
+ // token client ids. we categorize them so we can have different restrictions based on the client
+ ID_WEBADMIN: 'cid-webadmin', // dashboard
+ ID_DEVELOPMENT: 'cid-development', // dashboard development
+ ID_CLI: 'cid-cli', // cloudron cli
+ ID_SDK: 'cid-sdk', // created by user via dashboard
};
const assert = require('assert'),
@@ -13,13 +21,21 @@ const assert = require('assert'),
dashboard = require('./dashboard.js'),
database = require('./database.js'),
hat = require('./hat.js'),
- safe = require('safetydance'),
- tokens = require('./tokens.js');
+ safe = require('safetydance');
const OIDC_CLIENTS_TABLE_NAME = 'oidcClients';
const OIDC_CLIENTS_FIELDS = [ 'id', 'secret', 'name', 'appId', 'loginRedirectUri', 'tokenSignatureAlgorithm' ];
const DEFAULT_TOKEN_SIGNATURE_ALGORITHM='RS256';
+function validateId(type) {
+ assert.strictEqual(typeof type, 'string');
+
+ const types = [ exports.ID_WEBADMIN, exports.ID_SDK, exports.ID_DEVELOPMENT, exports.ID_CLI ];
+ if (types.indexOf(type) === -1) return new BoxError(BoxError.BAD_FIELD, `type must be one of ${types.join(',')}`);
+
+ return null;
+}
+
function postProcess(result) {
assert.strictEqual(typeof result, 'object');
@@ -50,19 +66,19 @@ async function add(data) {
async function get(id) {
assert.strictEqual(typeof id, 'string');
- if (id === tokens.ID_WEBADMIN) {
+ if (id === exports.ID_WEBADMIN) {
const { fqdn:dashboardFqdn } = await dashboard.getLocation();
return {
- id: tokens.ID_WEBADMIN,
+ id: exports.ID_WEBADMIN,
secret: 'notused',
application_type: 'web',
response_types: ['code', 'code token'],
grant_types: ['authorization_code', 'implicit'],
loginRedirectUri: `https://${dashboardFqdn}/authcallback.html`
};
- } else if (id === tokens.ID_DEVELOPMENT) {
+ } else if (id === exports.ID_DEVELOPMENT) {
return {
- id: tokens.ID_DEVELOPMENT,
+ id: exports.ID_DEVELOPMENT,
secret: 'notused',
application_type: 'native', // have to use native here to support plaintext http, this however makes it impossible to skip consent screen
response_types: ['code', 'code token'],
diff --git a/src/oidcserver.js b/src/oidcserver.js
index 97fe18e21..3405697f9 100644
--- a/src/oidcserver.js
+++ b/src/oidcserver.js
@@ -154,7 +154,7 @@ class StorageAdapter {
if (this.name === 'Client') {
debug('upsert: this should not happen as it is stored in our db');
- } else if (this.name === 'AccessToken' && (payload.clientId === tokens.ID_WEBADMIN || payload.clientId === tokens.ID_DEVELOPMENT)) {
+ } else if (this.name === 'AccessToken' && (payload.clientId === oidcClients.ID_WEBADMIN || payload.clientId === oidcClients.ID_DEVELOPMENT)) {
const expires = Date.now() + constants.DEFAULT_TOKEN_EXPIRATION_MSECS;
const [error] = await safe(tokens.add({ clientId: payload.clientId, identifier: payload.accountId, expires, accessToken: id, allowedIpRanges: '' }));
@@ -313,7 +313,7 @@ async function renderInteractionPage(req, res) {
ICON_URL: '/api/v1/cloudron/avatar',
NAME: client?.name || await branding.getCloudronName(),
FOOTER: marked.parse(await branding.renderFooter()),
- NOTE: (client.id === tokens.ID_WEBADMIN && constants.DEMO) ? '
This is a demo. Username and password is "cloudron"
' : ''
+ NOTE: (client.id === oidcClients.ID_WEBADMIN && constants.DEMO) ? 'This is a demo. Username and password is "cloudron"
' : ''
};
if (app) {
@@ -691,7 +691,7 @@ async function start() {
if (grantId) {
return await ctx.oidc.provider.Grant.find(grantId);
- } else if (ctx.oidc.client.clientId === tokens.ID_WEBADMIN || ctx.oidc.client.clientId === tokens.ID_DEVELOPMENT) {
+ } else if (ctx.oidc.client.clientId === oidcClients.ID_WEBADMIN || ctx.oidc.client.clientId === oidcClients.ID_DEVELOPMENT) {
const grant = new ctx.oidc.provider.Grant({
clientId: ctx.oidc.client.clientId,
accountId: ctx.oidc.session.accountId,
diff --git a/src/provision.js b/src/provision.js
index cdccc7c09..62df2e202 100644
--- a/src/provision.js
+++ b/src/provision.js
@@ -22,6 +22,7 @@ const appstore = require('./appstore.js'),
mail = require('./mail.js'),
mailServer = require('./mailserver.js'),
network = require('./network.js'),
+ oidcClients = require('./oidcclients.js'),
platform = require('./platform.js'),
reverseProxy = require('./reverseproxy.js'),
safe = require('safetydance'),
@@ -155,7 +156,7 @@ async function activate(username, password, email, displayName, ip, auditSource)
if (error && error.reason === BoxError.ALREADY_EXISTS) throw new BoxError(BoxError.CONFLICT, 'Already activated');
if (error) throw error;
- const token = { clientId: tokens.ID_WEBADMIN, identifier: ownerId, allowedIpRanges: '', expires: Date.now() + constants.DEFAULT_TOKEN_EXPIRATION_MSECS };
+ const token = { clientId: oidcClients.ID_WEBADMIN, identifier: ownerId, allowedIpRanges: '', expires: Date.now() + constants.DEFAULT_TOKEN_EXPIRATION_MSECS };
const result = await tokens.add(token);
await eventlog.add(eventlog.ACTION_ACTIVATE, auditSource, {});
diff --git a/src/routes/auth.js b/src/routes/auth.js
index c4ebf9b97..7b01cfade 100644
--- a/src/routes/auth.js
+++ b/src/routes/auth.js
@@ -19,6 +19,7 @@ const assert = require('assert'),
eventlog = require('../eventlog.js'),
HttpError = require('connect-lastmile').HttpError,
HttpSuccess = require('connect-lastmile').HttpSuccess,
+ oidcClients = require('../oidcclients.js'),
safe = require('safetydance'),
speakeasy = require('speakeasy'),
tokens = require('../tokens.js'),
@@ -29,18 +30,18 @@ async function login(req, res, next) {
if ('type' in req.body && typeof req.body.type !== 'string') return next(new HttpError(400, 'type must be a string'));
- const type = req.body.type || tokens.ID_WEBADMIN;
+ const type = req.body.type || oidcClients.ID_WEBADMIN;
const ip = req.headers['x-forwarded-for'] || req.socket.remoteAddress || null;
const userAgent = req.headers['user-agent'] || '';
- const tokenTypeError = tokens.validateTokenType(type);
+ const tokenTypeError = oidcClients.validateId(type);
if (tokenTypeError) return next(new HttpError(400, tokenTypeError.message));
const [error, token] = await safe(tokens.add({ clientId: type, identifier: req.user.id, allowedIpRanges: '', expires: Date.now() + constants.DEFAULT_TOKEN_EXPIRATION_MSECS }));
if (error) return next(new HttpError(500, error));
const auditSource = AuditSource.fromRequest(req);
- await eventlog.add(req.user.ghost ? eventlog.ACTION_USER_LOGIN_GHOST : eventlog.ACTION_USER_LOGIN, auditSource, { userId: req.user.id, user: users.removePrivateFields(req.user), type, appId: tokens.ID_CLI });
+ await eventlog.add(req.user.ghost ? eventlog.ACTION_USER_LOGIN_GHOST : eventlog.ACTION_USER_LOGIN, auditSource, { userId: req.user.id, user: users.removePrivateFields(req.user), type, appId: oidcClients.ID_CLI });
await safe(users.notifyLoginLocation(req.user, ip, userAgent, auditSource), { debug });
next(new HttpSuccess(200, token));
@@ -90,7 +91,7 @@ async function passwordReset(req, res, next) {
if (error && error.reason === BoxError.BAD_FIELD) return next(new HttpError(400, error.message));
if (error) return next(BoxError.toHttpError(error));
- const [addError, result] = await safe(tokens.add({ clientId: tokens.ID_WEBADMIN, identifier: userObject.id, allowedIpRanges: '', expires: Date.now() + constants.DEFAULT_TOKEN_EXPIRATION_MSECS }));
+ const [addError, result] = await safe(tokens.add({ clientId: oidcClients.ID_WEBADMIN, identifier: userObject.id, allowedIpRanges: '', expires: Date.now() + constants.DEFAULT_TOKEN_EXPIRATION_MSECS }));
if (addError) return next(BoxError.toHttpError(addError));
next(new HttpSuccess(202, { accessToken: result.accessToken }));
diff --git a/src/routes/test/common.js b/src/routes/test/common.js
index 22f82f540..38fd9a9f3 100644
--- a/src/routes/test/common.js
+++ b/src/routes/test/common.js
@@ -9,6 +9,7 @@ const apps = require('../../apps.js'),
fs = require('fs'),
mailer = require('../../mailer.js'),
nock = require('nock'),
+ oidcClients = require('../../oidcclients.js'),
oidcServer = require('../../oidcserver.js'),
safe = require('safetydance'),
server = require('../../server.js'),
@@ -155,7 +156,7 @@ async function setup() {
expect(response.status).to.equal(201);
admin.id = response.body.id;
// HACK to get a token for second user (passwords are generated and the user should have gotten a password setup link...)
- const token1 = await tokens.add({ identifier: admin.id, clientId: tokens.ID_WEBADMIN, expires: Date.now() + (60 * 60 * 1000), name: 'fromtest', allowedIpRanges: '' });
+ const token1 = await tokens.add({ identifier: admin.id, clientId: oidcClients.ID_WEBADMIN, expires: Date.now() + (60 * 60 * 1000), name: 'fromtest', allowedIpRanges: '' });
admin.token = token1.accessToken;
// create user
@@ -165,7 +166,7 @@ async function setup() {
expect(response.status).to.equal(201);
user.id = response.body.id;
// HACK to get a token for second user (passwords are generated and the user should have gotten a password setup link...)
- const token2 = await tokens.add({ identifier: user.id, clientId: tokens.ID_WEBADMIN, expires: Date.now() + (60 * 60 * 1000), name: 'fromtest', allowedIpRanges: '' });
+ const token2 = await tokens.add({ identifier: user.id, clientId: oidcClients.ID_WEBADMIN, expires: Date.now() + (60 * 60 * 1000), name: 'fromtest', allowedIpRanges: '' });
user.token = token2.accessToken;
// create app object
diff --git a/src/routes/tokens.js b/src/routes/tokens.js
index c5b75fd48..e3fd998c8 100644
--- a/src/routes/tokens.js
+++ b/src/routes/tokens.js
@@ -12,6 +12,7 @@ const assert = require('assert'),
BoxError = require('../boxerror.js'),
HttpError = require('connect-lastmile').HttpError,
HttpSuccess = require('connect-lastmile').HttpSuccess,
+ oidcClients = require('../oidcclients.js'),
safe = require('safetydance'),
tokens = require('../tokens.js');
@@ -59,7 +60,7 @@ async function add(req, res, next) {
const scope = req.body.scope || null;
const allowedIpRanges = req.body.allowedIpRanges || '';
- const [error, result] = await safe(tokens.add({ clientId: tokens.ID_SDK, identifier: req.user.id, expires: expiresAt, name: req.body.name, scope, allowedIpRanges }));
+ const [error, result] = await safe(tokens.add({ clientId: oidcClients.ID_SDK, identifier: req.user.id, expires: expiresAt, name: req.body.name, scope, allowedIpRanges }));
if (error) return next(BoxError.toHttpError(error));
next(new HttpSuccess(201, result));
diff --git a/src/test/tokens-test.js b/src/test/tokens-test.js
index 34cb5c8b4..421e9cd75 100644
--- a/src/test/tokens-test.js
+++ b/src/test/tokens-test.js
@@ -9,6 +9,7 @@
const BoxError = require('../boxerror.js'),
common = require('./common.js'),
expect = require('expect.js'),
+ oidcClients = require('../oidcclients.js'),
safe = require('safetydance'),
tokens = require('../tokens.js');
@@ -155,7 +156,7 @@ describe('Tokens', function () {
const token1 = {
name: 'token1',
identifier: 'user1',
- clientId: tokens.ID_WEBADMIN,
+ clientId: oidcClients.ID_WEBADMIN,
expires: Number.MAX_SAFE_INTEGER,
lastUsedTime: null,
scope: { '*': 'rw' },
@@ -164,7 +165,7 @@ describe('Tokens', function () {
const token2 = {
name: 'token2',
identifier: 'user1',
- clientId: tokens.ID_SDK,
+ clientId: oidcClients.ID_SDK,
expires: Date.now(),
lastUsedTime: null,
allowedIpRanges: '#this'
@@ -173,11 +174,11 @@ describe('Tokens', function () {
await tokens.add(token1);
await tokens.add(token2);
- await tokens.delByUserIdAndType('user2', tokens.ID_WEBADMIN);
+ await tokens.delByUserIdAndType('user2', oidcClients.ID_WEBADMIN);
let result = await tokens.listByUserId('user1');
expect(result.length).to.be(2); // should not have deleted user1 tokens
- await tokens.delByUserIdAndType('user1', tokens.ID_WEBADMIN);
+ await tokens.delByUserIdAndType('user1', oidcClients.ID_WEBADMIN);
result = await tokens.listByUserId('user1');
expect(result.length).to.be(1);
expect(result[0].name).to.be(token2.name);
diff --git a/src/test/user-directory-test.js b/src/test/user-directory-test.js
index 679ca9732..4aad4c42a 100644
--- a/src/test/user-directory-test.js
+++ b/src/test/user-directory-test.js
@@ -7,6 +7,7 @@
const common = require('./common.js'),
expect = require('expect.js'),
+ oidcClients = require('../oidcclients.js'),
tokens = require('../tokens.js'),
userDirectory = require('../user-directory.js');
@@ -24,7 +25,7 @@ describe('User Directory', function () {
});
it('can set default profile config', async function () {
- await tokens.add({ name: 'token1', identifier: admin.id, clientId: tokens.ID_WEBADMIN, expires: Number.MAX_SAFE_INTEGER, lastUsedTime: null, allowedIpRanges: '' });
+ await tokens.add({ name: 'token1', identifier: admin.id, clientId: oidcClients.ID_WEBADMIN, expires: Number.MAX_SAFE_INTEGER, lastUsedTime: null, allowedIpRanges: '' });
let result = await tokens.listByUserId(admin.id);
expect(result.length).to.be(1); // just confirm the token was really added!
diff --git a/src/tokens.js b/src/tokens.js
index c99d115eb..1b79e0b8c 100644
--- a/src/tokens.js
+++ b/src/tokens.js
@@ -13,18 +13,10 @@ exports = module.exports = {
listByUserId,
getByAccessToken,
- validateTokenType,
-
hasScope,
isIpAllowedSync,
- // token client ids. we categorize them so we can have different restrictions based on the client
- ID_WEBADMIN: 'cid-webadmin', // dashboard
- ID_DEVELOPMENT: 'cid-development', // dashboard development
- ID_CLI: 'cid-cli', // cloudron cli
- ID_SDK: 'cid-sdk', // created by user via dashboard
-
SCOPES: ['*']//, 'apps', 'domains'],
};
@@ -57,15 +49,6 @@ function validateTokenName(name) {
return null;
}
-function validateTokenType(type) {
- assert.strictEqual(typeof type, 'string');
-
- const types = [ exports.ID_WEBADMIN, exports.ID_SDK, exports.ID_DEVELOPMENT, exports.ID_CLI ];
- if (types.indexOf(type) === -1) return new BoxError(BoxError.BAD_FIELD, `type must be one of ${types.join(',')}`);
-
- return null;
-}
-
function validateScope(scope) {
assert.strictEqual(typeof scope, 'object');
diff --git a/src/user-directory.js b/src/user-directory.js
index 3cd60109e..d4098a11e 100644
--- a/src/user-directory.js
+++ b/src/user-directory.js
@@ -10,6 +10,7 @@ const assert = require('assert'),
constants = require('./constants.js'),
debug = require('debug')('box:user-directory'),
eventlog = require('./eventlog.js'),
+ oidcClients = require('./oidcclients.js'),
oidcServer = require('./oidcserver.js'),
settings = require('./settings.js'),
tokens = require('./tokens.js'),
@@ -41,7 +42,7 @@ async function setProfileConfig(profileConfig, options, auditSource) {
if (user.twoFactorAuthenticationEnabled) continue;
if (options.persistUserIdSessions === user.id) continue; // do not logout the API caller
- await tokens.delByUserIdAndType(user.id, tokens.ID_WEBADMIN);
+ await tokens.delByUserIdAndType(user.id, oidcClients.ID_WEBADMIN);
await oidcServer.revokeByUserId(user.id);
}
}
diff --git a/src/users.js b/src/users.js
index 23c5d0391..3dc58d91a 100644
--- a/src/users.js
+++ b/src/users.js
@@ -91,7 +91,7 @@ const appPasswords = require('./apppasswords.js'),
mailer = require('./mailer.js'),
mysql = require('mysql'),
notifications = require('./notifications'),
- paths = require('./paths.js'),
+ oidcClients = require('./oidcclients.js'),
qrcode = require('qrcode'),
safe = require('safetydance'),
settings = require('./settings.js'),
@@ -876,7 +876,7 @@ async function setupAccount(user, data, auditSource) {
await setPassword(user, data.password, auditSource);
- const token = { clientId: tokens.ID_WEBADMIN, identifier: user.id, expires: Date.now() + constants.DEFAULT_TOKEN_EXPIRATION_MSECS, allowedIpRanges: '' };
+ const token = { clientId: oidcClients.ID_WEBADMIN, identifier: user.id, expires: Date.now() + constants.DEFAULT_TOKEN_EXPIRATION_MSECS, allowedIpRanges: '' };
const result = await tokens.add(token);
return result.accessToken;
}