diff --git a/dashboard/src/js/client.js b/dashboard/src/js/client.js index 4a48a5ecc..de3016805 100644 --- a/dashboard/src/js/client.js +++ b/dashboard/src/js/client.js @@ -1929,7 +1929,7 @@ angular.module('Application').service('Client', ['$http', '$interval', '$timeout }); }; - Client.prototype.addOidcClient = function (id, name, secret, loginRedirectUri, logoutRedirectUri, tokenSignatureAlgorithm, callback) { + Client.prototype.addOidcClient = function (id, name, secret, loginRedirectUri, tokenSignatureAlgorithm, callback) { var data = { id: id, name: name, @@ -1938,8 +1938,6 @@ angular.module('Application').service('Client', ['$http', '$interval', '$timeout tokenSignatureAlgorithm: tokenSignatureAlgorithm }; - if (logoutRedirectUri) data.logoutRedirectUri = logoutRedirectUri; - post('/api/v1/oidc/clients', data, null, function (error, data, status) { if (error) return callback(error); if (status !== 201) return callback(new ClientError(status, data)); @@ -1948,7 +1946,7 @@ angular.module('Application').service('Client', ['$http', '$interval', '$timeout }); }; - Client.prototype.updateOidcClient = function (id, name, secret, loginRedirectUri, logoutRedirectUri, tokenSignatureAlgorithm, callback) { + Client.prototype.updateOidcClient = function (id, name, secret, loginRedirectUri, tokenSignatureAlgorithm, callback) { var data = { secret: secret, name: name, @@ -1956,8 +1954,6 @@ angular.module('Application').service('Client', ['$http', '$interval', '$timeout tokenSignatureAlgorithm, tokenSignatureAlgorithm }; - if (logoutRedirectUri) data.logoutRedirectUri = logoutRedirectUri; - post('/api/v1/oidc/clients/' + id, data, null, function (error, data, status) { if (error) return callback(error); if (status !== 201) return callback(new ClientError(status, data)); diff --git a/dashboard/src/views/oidc.html b/dashboard/src/views/oidc.html index aca0da2e3..46f14c39b 100644 --- a/dashboard/src/views/oidc.html +++ b/dashboard/src/views/oidc.html @@ -30,10 +30,6 @@ -
- - -
@@ -77,10 +73,7 @@
-
- - -
+
diff --git a/dashboard/src/views/oidc.js b/dashboard/src/views/oidc.js index 69d6087e0..fc23fb685 100644 --- a/dashboard/src/views/oidc.js +++ b/dashboard/src/views/oidc.js @@ -25,7 +25,6 @@ angular.module('Application').controller('OidcController', ['$scope', '$location name: '', secret: '', loginRedirectUri: '', - logoutRedirectUri: '', tokenSignatureAlgorithm: '', show: function () { @@ -33,7 +32,6 @@ angular.module('Application').controller('OidcController', ['$scope', '$location $scope.clientAdd.secret = ''; $scope.clientAdd.name = ''; $scope.clientAdd.loginRedirectUri = ''; - $scope.clientAdd.logoutRedirectUri = ''; $scope.clientAdd.tokenSignatureAlgorithm = 'RS256'; $scope.clientAdd.busy = false; $scope.clientAdd.error = null; @@ -46,7 +44,7 @@ angular.module('Application').controller('OidcController', ['$scope', '$location $scope.clientAdd.busy = true; $scope.clientAdd.error = {}; - Client.addOidcClient($scope.clientAdd.id, $scope.clientAdd.name, $scope.clientAdd.secret, $scope.clientAdd.loginRedirectUri, $scope.clientAdd.logoutRedirectUri, $scope.clientAdd.tokenSignatureAlgorithm, function (error) { + Client.addOidcClient($scope.clientAdd.id, $scope.clientAdd.name, $scope.clientAdd.secret, $scope.clientAdd.loginRedirectUri, $scope.clientAdd.tokenSignatureAlgorithm, function (error) { if (error) { if (error.statusCode === 409) { $scope.clientAdd.error.id = 'Client ID already exists'; @@ -75,7 +73,6 @@ angular.module('Application').controller('OidcController', ['$scope', '$location name: '', secret: '', loginRedirectUri: '', - logoutRedirectUri: '', tokenSignatureAlgorithm: '', show: function (client) { @@ -83,7 +80,6 @@ angular.module('Application').controller('OidcController', ['$scope', '$location $scope.clientEdit.name = client.name; $scope.clientEdit.secret = client.secret; $scope.clientEdit.loginRedirectUri = client.loginRedirectUri; - $scope.clientEdit.logoutRedirectUri = client.logoutRedirectUri; $scope.clientEdit.tokenSignatureAlgorithm = client.tokenSignatureAlgorithm; $scope.clientEdit.busy = false; $scope.clientEdit.error = null; @@ -96,7 +92,7 @@ angular.module('Application').controller('OidcController', ['$scope', '$location $scope.clientEdit.busy = true; $scope.clientEdit.error = {}; - Client.updateOidcClient($scope.clientEdit.id, $scope.clientEdit.name, $scope.clientEdit.secret, $scope.clientEdit.loginRedirectUri, $scope.clientEdit.logoutRedirectUri, $scope.clientEdit.tokenSignatureAlgorithm, function (error) { + Client.updateOidcClient($scope.clientEdit.id, $scope.clientEdit.name, $scope.clientEdit.secret, $scope.clientEdit.loginRedirectUri, $scope.clientEdit.tokenSignatureAlgorithm, function (error) { if (error) { console.error('Unable to edit openid client.', error); diff --git a/migrations/20230720112331-oidc-clients-drop-logoutRedirectUri.js b/migrations/20230720112331-oidc-clients-drop-logoutRedirectUri.js new file mode 100644 index 000000000..83144e465 --- /dev/null +++ b/migrations/20230720112331-oidc-clients-drop-logoutRedirectUri.js @@ -0,0 +1,9 @@ +'use strict'; + +exports.up = async function(db) { + await db.runSql('ALTER TABLE oidcClients DROP COLUMN logoutRedirectUri'); +}; + +exports.down = async function(db) { + await db.runSql('ALTER TABLE oidcClients ADD COLUMN logoutRedirectUri VARCHAR(256) NOT NULL'); +}; diff --git a/migrations/schema.sql b/migrations/schema.sql index f16a9762c..31c3daa25 100644 --- a/migrations/schema.sql +++ b/migrations/schema.sql @@ -319,6 +319,5 @@ CREATE TABLE IF NOT EXISTS oidcClients( appId VARCHAR(128) DEFAULT "", name VARCHAR(128) DEFAULT "", loginRedirectUri VARCHAR(256) DEFAULT "", - logoutRedirectUri VARCHAR(256) DEFAULT "", tokenSignatureAlgorithm VARCHAR(128) DEFAULT "", PRIMARY KEY(id)); diff --git a/src/oidc.js b/src/oidc.js index 32bf5095f..b98e83679 100644 --- a/src/oidc.js +++ b/src/oidc.js @@ -38,7 +38,7 @@ const assert = require('assert'), util = require('util'); const OIDC_CLIENTS_TABLE_NAME = 'oidcClients'; -const OIDC_CLIENTS_FIELDS = [ 'id', 'secret', 'name', 'appId', 'loginRedirectUri', 'logoutRedirectUri', 'tokenSignatureAlgorithm' ]; +const OIDC_CLIENTS_FIELDS = [ 'id', 'secret', 'name', 'appId', 'loginRedirectUri', 'tokenSignatureAlgorithm' ]; const ROUTE_PREFIX = '/openid'; const DEFAULT_TOKEN_SIGNATURE_ALGORITHM='RS256'; @@ -63,13 +63,12 @@ async function clientsAdd(id, data) { assert.strictEqual(typeof id, 'string'); assert.strictEqual(typeof data.secret, 'string'); assert.strictEqual(typeof data.loginRedirectUri, 'string'); - assert.strictEqual(typeof data.logoutRedirectUri, 'string'); assert.strictEqual(typeof data.name, 'string'); assert.strictEqual(typeof data.appId, 'string'); assert(data.tokenSignatureAlgorithm === 'RS256' || data.tokenSignatureAlgorithm === 'EdDSA'); - const query = `INSERT INTO ${OIDC_CLIENTS_TABLE_NAME} (id, secret, name, appId, loginRedirectUri, logoutRedirectUri, tokenSignatureAlgorithm) VALUES (?, ?, ?, ?, ?, ?, ?)`; - const args = [ id, data.secret, data.name, data.appId, data.loginRedirectUri, data.logoutRedirectUri, data.tokenSignatureAlgorithm ]; + const query = `INSERT INTO ${OIDC_CLIENTS_TABLE_NAME} (id, secret, name, appId, loginRedirectUri, tokenSignatureAlgorithm) VALUES (?, ?, ?, ?, ?, ?)`; + const args = [ id, data.secret, data.name, data.appId, data.loginRedirectUri, data.tokenSignatureAlgorithm ]; const [error] = await safe(database.query(query, args)); if (error && error.code === 'ER_DUP_ENTRY') throw new BoxError(BoxError.ALREADY_EXISTS, 'client already exists'); @@ -109,12 +108,11 @@ async function clientsUpdate(id, data) { assert.strictEqual(typeof id, 'string'); assert.strictEqual(typeof data.secret, 'string'); assert.strictEqual(typeof data.loginRedirectUri, 'string'); - assert.strictEqual(typeof data.logoutRedirectUri, 'string'); assert.strictEqual(typeof data.name, 'string'); assert.strictEqual(typeof data.appId, 'string'); assert(data.tokenSignatureAlgorithm === 'RS256' || data.tokenSignatureAlgorithm === 'EdDSA'); - const result = await database.query(`UPDATE ${OIDC_CLIENTS_TABLE_NAME} SET secret=?, name=?, appId=?, loginRedirectUri=?, logoutRedirectUri=?, tokenSignatureAlgorithm=? WHERE id = ?`, [ data.secret, data.name, data.appId, data.loginRedirectUri, data.logoutRedirectUri, data.tokenSignatureAlgorithm, id]); + const result = await database.query(`UPDATE ${OIDC_CLIENTS_TABLE_NAME} SET secret=?, name=?, appId=?, loginRedirectUri=?, tokenSignatureAlgorithm=? WHERE id = ?`, [ data.secret, data.name, data.appId, data.loginRedirectUri, data.tokenSignatureAlgorithm, id]); if (result.affectedRows !== 1) throw new BoxError(BoxError.NOT_FOUND, 'client not found'); } @@ -289,15 +287,11 @@ class CloudronAdapter { return null; } - // prefix login and logout redirect uris with app.fqdn if it is just a path without a schema + // prefix login redirect uris with app.fqdn if it is just a path without a schema // native callbacks for apps have custom schema like app.immich:/ tmp.redirect_uris = client.loginRedirectUri.split(',').map(s => s.trim()).map(s => url.parse(s).protocol ? s : `https://${app.fqdn}${s}`); - - if (client.logoutRedirectUri) tmp.post_logout_redirect_uris = [ url.parse(client.logoutRedirectUri).protocol ? client.logoutRedirectUri : `https://${app.fqdn}${client.logoutRedirectUri}` ]; } else { tmp.redirect_uris = client.loginRedirectUri.split(',').map(s => s.trim()); - - if (client.logoutRedirectUri) tmp.post_logout_redirect_uris = [ client.logoutRedirectUri ]; } return tmp; @@ -657,26 +651,6 @@ async function claims(userId, use, scope) { return claims; } -// @param form - form source (id="op.logoutForm") to be embedded in the page and submitted by the End-User -async function logoutSource(ctx, form) { - const data = { - host: settings.dashboardFqdn(), - form - }; - - ctx.body = ejs.render(fs.readFileSync(path.join(__dirname, 'oidc_templates/logout.ejs'), 'utf8'), data, {}); -} - -// this is called if client has not specified a post_logout_redirect_uri -async function postLogoutSuccessSource(ctx) { - // const client = ctx.oidc.client || {}; // client is defined if the user chose to stay logged in with the OP - const data = { - dashboardOrigin: settings.dashboardOrigin() - }; - - ctx.body = ejs.render(fs.readFileSync(path.join(__dirname, 'oidc_templates/post_logout.ejs'), 'utf8'), data, {}); -} - async function findAccount(ctx, id) { return { accountId: id, @@ -747,12 +721,7 @@ async function start() { profile: [ 'family_name', 'given_name', 'locale', 'name', 'preferred_username' ] }, features: { - devInteractions: { enabled: false }, - rpInitiatedLogout: { - enabled: false, - logoutSource, - postLogoutSuccessSource - }, + devInteractions: { enabled: false } }, responseTypes: [ 'code', diff --git a/src/oidc_templates/logout.ejs b/src/oidc_templates/logout.ejs deleted file mode 100644 index b3fe3b032..000000000 --- a/src/oidc_templates/logout.ejs +++ /dev/null @@ -1,76 +0,0 @@ - - - - - - - OpenID Logout - - - - - - - - - - - - - - - - - - - - -
-
-
-
-
- -
-

Also log out from the OpenID provider?

-
-
-
-
-
- <%- form %> - - -
-
-
-
-
- - - diff --git a/src/oidc_templates/post_logout.ejs b/src/oidc_templates/post_logout.ejs deleted file mode 100644 index 4a9e4e160..000000000 --- a/src/oidc_templates/post_logout.ejs +++ /dev/null @@ -1,68 +0,0 @@ - - - - - - - Cloudron Logout - - - - - - - - - - - - - - - - - - - - -
-
-
-
-
- -
-

Succesfully logged out

-
-
-
-
-
- - - diff --git a/src/routes/oidc.js b/src/routes/oidc.js index ce5f8c9a8..390c8377f 100644 --- a/src/routes/oidc.js +++ b/src/routes/oidc.js @@ -28,7 +28,6 @@ async function add(req, res, next) { if (typeof req.body.secret !== 'string' || !req.body.secret) return next(new HttpError(400, 'secret must be non-empty string')); if (typeof req.body.loginRedirectUri !== 'string' || !req.body.loginRedirectUri) return next(new HttpError(400, 'loginRedirectUri must be non-empty string')); if (req.body.tokenSignatureAlgorithm !== 'EdDSA' && req.body.tokenSignatureAlgorithm !== 'RS256') return next(new HttpError(400, 'tokenSignatureAlgorithm must be either EdDSA or RS256')); - if ('logoutRedirectUri' in req.body && (typeof req.body.logoutRedirectUri !== 'string' || !req.body.logoutRedirectUri)) return next(new HttpError(400, 'logoutRedirectUri must be non-empty string if provided')); // clients with appId are internal only if (req.body.appId) return next(new HttpError(400, 'appId cannot be specified')); @@ -38,8 +37,7 @@ async function add(req, res, next) { name: req.body.name, appId: '', // always empty for custom clients tokenSignatureAlgorithm: req.body.tokenSignatureAlgorithm, - loginRedirectUri: req.body.loginRedirectUri, - logoutRedirectUri: req.body.logoutRedirectUri || '' + loginRedirectUri: req.body.loginRedirectUri }; const [error] = await safe(oidc.clients.add(req.body.id, data)); @@ -67,7 +65,6 @@ async function update(req, res, next) { if (typeof req.body.secret !== 'string' || !req.body.secret) return next(new HttpError(400, 'secret must be non-empty string')); if (typeof req.body.loginRedirectUri !== 'string' || !req.body.loginRedirectUri) return next(new HttpError(400, 'loginRedirectUri must be non-empty string')); if (req.body.tokenSignatureAlgorithm !== 'EdDSA' && req.body.tokenSignatureAlgorithm !== 'RS256') return next(new HttpError(400, 'tokenSignatureAlgorithm must be either EdDSA or RS256')); - if ('logoutRedirectUri' in req.body && (typeof req.body.logoutRedirectUri !== 'string' || !req.body.logoutRedirectUri)) return next(new HttpError(400, 'logoutRedirectUri must be non-empty string if provided')); const [error, client] = await safe(oidc.clients.get(req.params.clientId)); if (error) return next(BoxError.toHttpError(error)); @@ -79,8 +76,7 @@ async function update(req, res, next) { name: req.body.name, appId: '', // always empty for custom clients tokenSignatureAlgorithm: req.body.tokenSignatureAlgorithm, - loginRedirectUri: req.body.loginRedirectUri, - logoutRedirectUri: req.body.logoutRedirectUri || '' + loginRedirectUri: req.body.loginRedirectUri }; const [updateError] = await safe(oidc.clients.update(req.params.clientId, data));