diff --git a/src/oidcserver.js b/src/oidcserver.js
index 1263d46cd..a75ad581c 100644
--- a/src/oidcserver.js
+++ b/src/oidcserver.js
@@ -304,86 +304,80 @@ class StorageAdapter {
}
}
+async function renderError(error) {
+ const data = {
+ ICON_URL: '/api/v1/cloudron/avatar',
+ NAME: 'Cloudron',
+ ERROR_MESSAGE: error.error_description || error.error_detail || error.message || 'Internal error',
+ FOOTER: marked.parse(await branding.renderFooter())
+ };
+
+ debug('renderError: %o', error);
+ let html = fs.readFileSync(path.join(__dirname, '/../dashboard/dist/oidc_error.html'), 'utf8');
+ for (const key in data) {
+ html = html.replaceAll(`##${key}##`, data[key]);
+ }
+ return html;
+}
+
async function renderInteractionPage(req, res) {
- try {
- const { uid, prompt, params, session } = await gOidcProvider.interactionDetails(req, res);
+ const { uid, prompt, params, session } = await gOidcProvider.interactionDetails(req, res);
- const client = await oidcClients.get(params.client_id);
+ const client = await oidcClients.get(params.client_id);
+ if (!client) return res.send(await renderError(new Error('Client not found')));
- let app = null;
- if (client.appId) app = await apps.get(client.appId);
+ const app = client.appId ? await apps.get(client.appId) : null;
+ if (client.appId && !app) return res.send(await renderError(new Error('App not found')));
- switch (prompt.name) {
- case 'login': {
- const options = {
- SUBMIT_URL: `${ROUTE_PREFIX}/interaction/${uid}/login`,
- ICON_URL: '/api/v1/cloudron/avatar',
- NAME: client?.name || await branding.getCloudronName(),
- FOOTER: marked.parse(await branding.renderFooter()),
- NOTE: (client.id === oidcClients.ID_WEBADMIN && constants.DEMO) ? '
This is a demo. Username and password is "cloudron"
' : ''
- };
+ res.set('Content-Type', 'text/html');
- if (app) {
- options.NAME = app.label || app.fqdn;
- options.ICON_URL = app.iconUrl;
- }
-
- // great ejs replacement!
- let html = fs.readFileSync(__dirname + '/../dashboard/dist/login.html', 'utf-8');
- Object.keys(options).forEach(key => {
- html = html.replaceAll(`##${key}##`, options[key]);
- });
-
- return res.send(html);
- }
- case 'consent': {
- let hasAccess = false;
-
- const data = {
- ICON_URL: '/api/v1/cloudron/avatar',
- NAME: client?.name || '',
- FOOTER: marked.parse(await branding.renderFooter())
- };
-
- // check if user has access to the app if client refers to an app
- if (app) {
- const user = await users.get(session.accountId);
-
- data.NAME = app.label || app.fqdn;
- data.ICON_URL = app.iconUrl;
- hasAccess = apps.canAccess(app, user);
- } else {
- hasAccess = true;
- }
-
- data.SUBMIT_URL = `${ROUTE_PREFIX}/interaction/${uid}/${hasAccess ? 'confirm' : 'abort'}`;
-
- let html = fs.readFileSync(path.join(__dirname, hasAccess ? '/../dashboard/dist/oidc_interaction_confirm.html' : '/../dashboard/dist/oidc_interaction_abort.html'), 'utf8');
- Object.keys(data).forEach(key => {
- html = html.replaceAll(`##${key}##`, data[key]);
- });
-
- return res.send(html);
- }
- default:
- return undefined;
- }
- } catch (error) {
- debug('route interaction get error', error);
-
- const data = {
+ if (prompt.name === 'login') {
+ const options = {
+ SUBMIT_URL: `${ROUTE_PREFIX}/interaction/${uid}/login`,
ICON_URL: '/api/v1/cloudron/avatar',
- NAME: 'Cloudron',
- ERROR_MESSAGE: error.error_description || 'Internal error',
+ NAME: client.name || await branding.getCloudronName(),
+ FOOTER: marked.parse(await branding.renderFooter()),
+ NOTE: (client.id === oidcClients.ID_WEBADMIN && constants.DEMO) ? 'This is a demo. Username and password is "cloudron"
' : ''
+ };
+
+ if (app) {
+ options.NAME = app.label || app.fqdn;
+ options.ICON_URL = app.iconUrl;
+ }
+
+ let html = fs.readFileSync(__dirname + '/../dashboard/dist/login.html', 'utf-8');
+ for (const key in options) {
+ html = html.replaceAll(`##${key}##`, options[key]);
+ }
+
+ return res.send(html);
+ } else if (prompt.name === 'consent') {
+ let hasAccess = false;
+
+ const options = {
+ ICON_URL: '/api/v1/cloudron/avatar',
+ NAME: client.name || '',
FOOTER: marked.parse(await branding.renderFooter())
};
- let html = fs.readFileSync(path.join(__dirname, '/../dashboard/dist/oidc_error.html'), 'utf8');
- Object.keys(data).forEach(key => {
- html = html.replaceAll(`##${key}##`, data[key]);
- });
+ // check if user has access to the app if client refers to an app
+ if (app) {
+ const user = await users.get(session.accountId);
+
+ options.NAME = app.label || app.fqdn;
+ options.ICON_URL = app.iconUrl;
+ hasAccess = apps.canAccess(app, user);
+ } else {
+ hasAccess = true;
+ }
+
+ options.SUBMIT_URL = `${ROUTE_PREFIX}/interaction/${uid}/${hasAccess ? 'confirm' : 'abort'}`;
+
+ let html = fs.readFileSync(path.join(__dirname, hasAccess ? '/../dashboard/dist/oidc_interaction_confirm.html' : '/../dashboard/dist/oidc_interaction_abort.html'), 'utf8');
+ for (const key in options) {
+ html = html.replaceAll(`##${key}##`, options[key]);
+ }
- res.set('Content-Type', 'text/html');
return res.send(html);
}
}
@@ -401,8 +395,7 @@ async function interactionLogin(req, res, next) {
debug(`interactionLogin: for OpenID client ${clientId} from ${ip}`);
- // This is the auto login via token hack
- if (req.body.autoLoginToken) {
+ if (req.body.autoLoginToken) { // auto login for first admin/owner
if (typeof req.body.autoLoginToken !== 'string') return next(new HttpError(400, 'autoLoginToken must be string if provided'));
const token = await tokens.getByAccessToken(req.body.autoLoginToken);
@@ -575,24 +568,6 @@ async function getClaims(userId/*, use, scope*/) {
return claims;
}
-async function renderError(ctx, out, error) {
- const data = {
- ICON_URL: '/api/v1/cloudron/avatar',
- NAME: 'Cloudron',
- ERROR_MESSAGE: error.error_description || error.error_detail || 'Unknown error',
- FOOTER: marked.parse(await branding.renderFooter())
- };
-
- debug('renderError: %o', error);
- let html = fs.readFileSync(path.join(__dirname, '/../dashboard/dist/oidc_error.html'), 'utf8');
- Object.keys(data).forEach(key => {
- html = html.replaceAll(`##${key}##`, data[key]);
- });
-
- ctx.type = 'html';
- ctx.body = html;
-}
-
async function start() {
assert(gHttpServer === null, 'OIDC server already started');
assert(gOidcProvider === null, 'OIDC provider already started');
@@ -635,16 +610,19 @@ async function start() {
}
const configuration = {
- findAccount: async function(ctx, id) {
+ findAccount: async function (ctx, id) {
return {
accountId: id,
claims: async (use, scope) => await getClaims(id, use, scope)
};
},
- renderError,
+ renderError: async function (ctx, out, error) {
+ ctx.type = 'html';
+ ctx.body = await renderError(error);
+ },
adapter: StorageAdapter,
interactions: {
- url: async function(ctx, interaction) {
+ url: async function (ctx, interaction) {
return `${ROUTE_PREFIX}/interaction/${interaction.uid}`;
}
},