diff --git a/src/backend/src/data/hardcoded-permissions.js b/src/backend/src/data/hardcoded-permissions.js
index fd09e20d8..12643f8ee 100644
--- a/src/backend/src/data/hardcoded-permissions.js
+++ b/src/backend/src/data/hardcoded-permissions.js
@@ -106,6 +106,8 @@ const hardcoded_user_group_permissions = {
'local-terminal:access': {},
},
'b7220104-7905-4985-b996-649fdcdb3c8f': {
+ 'driver': {},
+ 'service': {},
'service:hello-world:ii:hello-world': policy_perm('temp.es'),
'service:puter-kvstore:ii:puter-kvstore': policy_perm('temp.kv'),
'driver:puter-kvstore': policy_perm('temp.kv'),
@@ -119,6 +121,8 @@ const hardcoded_user_group_permissions = {
'service:es\\Csubdomain:ii:crud-q': policy_perm('user.es'),
},
'78b1b1dd-c959-44d2-b02c-8735671f9997': {
+ 'driver': {},
+ 'service': {},
'service:hello-world:ii:hello-world': policy_perm('user.es'),
'service:puter-kvstore:ii:puter-kvstore': policy_perm('user.kv'),
'driver:puter-kvstore': policy_perm('user.kv'),
diff --git a/src/backend/src/routers/auth/delete-own-user.js b/src/backend/src/routers/auth/delete-own-user.js
deleted file mode 100644
index 6145b3912..000000000
--- a/src/backend/src/routers/auth/delete-own-user.js
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2024-present Puter Technologies Inc.
- *
- * This file is part of Puter.
- *
- * Puter is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published
- * by the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-const eggspress = require('../../api/eggspress');
-const { deleteUser, invalidate_cached_user } = require('../../helpers');
-
-const config = require('../../config');
-
-module.exports = eggspress('/delete-own-user', {
- subdomain: 'api',
- auth: true,
- allowedMethods: ['POST'],
-}, async (req, res) => {
- const bcrypt = require('bcrypt');
-
- const validate_request = async () => {
- const user = req.user;
-
- // `user` should always have a value, but this is checked
- // any way in case the auth middleware is broken.
- if ( ! user ) return false;
-
- // temporary users don't require password verification
- if ( !user.email && !user.password ) {
- return true;
- }
-
- if ( ! req.body.password ) return false;
- if ( !user || !user.password ) return false;
- if ( ! await bcrypt.compare(req.body.password, req.user.password) ) {
- return false;
- }
- return true;
- };
-
- if ( ! await validate_request() ) {
- return res.status(400).send({ success: false });
- }
-
- res.clearCookie(config.cookie_name);
-
- await deleteUser(req.user.id);
- invalidate_cached_user(req.user);
-
- return res.send({ success: true });
-});
diff --git a/src/backend/src/routers/user-protected/delete-own-user.js b/src/backend/src/routers/user-protected/delete-own-user.js
new file mode 100644
index 000000000..470ebdd3a
--- /dev/null
+++ b/src/backend/src/routers/user-protected/delete-own-user.js
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2026-present Puter Technologies Inc.
+ *
+ * This file is part of Puter.
+ *
+ * Puter is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+const config = require('../../config');
+const { deleteUser, invalidate_cached_user } = require('../../helpers');
+
+const REVALIDATION_COOKIE_NAME = 'puter_revalidation';
+
+module.exports = {
+ route: '/delete-own-user',
+ methods: ['POST'],
+ handler: async (req, res) => {
+ res.clearCookie(config.cookie_name);
+ res.clearCookie(REVALIDATION_COOKIE_NAME);
+
+ await deleteUser(req.user.id);
+ invalidate_cached_user(req.user);
+
+ return res.send({ success: true });
+ },
+};
diff --git a/src/backend/src/services/PuterAPIService.js b/src/backend/src/services/PuterAPIService.js
index b98f0342e..15e5b3653 100644
--- a/src/backend/src/services/PuterAPIService.js
+++ b/src/backend/src/services/PuterAPIService.js
@@ -53,7 +53,6 @@ class PuterAPIService extends BaseService {
app.use(require('../routers/auth/app-uid-from-origin'));
app.use(require('../routers/auth/create-access-token'));
app.use(require('../routers/auth/revoke-access-token'));
- app.use(require('../routers/auth/delete-own-user'));
app.use(require('../routers/auth/configure-2fa'));
app.use(require('../routers/drivers/call'));
app.use(require('../routers/drivers/list-interfaces'));
diff --git a/src/backend/src/services/web/UserProtectedEndpointsService.js b/src/backend/src/services/web/UserProtectedEndpointsService.js
index 1288eb004..7e67f0e46 100644
--- a/src/backend/src/services/web/UserProtectedEndpointsService.js
+++ b/src/backend/src/services/web/UserProtectedEndpointsService.js
@@ -109,9 +109,10 @@ class UserProtectedEndpointsService extends BaseService {
next();
});
- // Do not allow temporary users
+ // Do not allow temporary users (except for delete-own-user, which allows them)
router.use(async (req, res, next) => {
if ( req.method === 'OPTIONS' ) return next();
+ if ( req.path === '/delete-own-user' ) return next();
if ( req.user.password === null && req.user.email === null ) {
return APIError.create('temporary_account').write(res);
@@ -122,6 +123,7 @@ class UserProtectedEndpointsService extends BaseService {
/**
* Middleware to validate identity: either password (bcrypt) or a valid OIDC revalidation cookie.
* OIDC-only accounts (user.password === null) must use revalidation; password accounts may use either.
+ * Temporary users (no password, no email) are allowed only for delete-own-user.
*/
router.use(async (req, res, next) => {
if ( req.method === 'OPTIONS' ) return next();
@@ -129,6 +131,10 @@ class UserProtectedEndpointsService extends BaseService {
const user = await get_user({ id: req.user.id, force: true });
const revalidationCookie = req.cookies && req.cookies[REVALIDATION_COOKIE_NAME];
+ if ( user.password === null && user.email === null ) {
+ return next();
+ }
+
if ( req.body.password ) {
if ( user.password === null ) {
return (APIError.create('oidc_revalidation_required', null, await this.#revalidateUrlFields(req, user))).write(res);
@@ -168,6 +174,8 @@ class UserProtectedEndpointsService extends BaseService {
Endpoint(require('../../routers/user-protected/change-username.js')).attach(router);
Endpoint(require('../../routers/user-protected/disable-2fa.js')).attach(router);
+
+ Endpoint(require('../../routers/user-protected/delete-own-user.js')).attach(router);
}
}
diff --git a/src/gui/src/UI/Settings/UIWindowFinalizeUserDeletion.js b/src/gui/src/UI/Settings/UIWindowFinalizeUserDeletion.js
index 88333d75a..fbf7c397a 100644
--- a/src/gui/src/UI/Settings/UIWindowFinalizeUserDeletion.js
+++ b/src/gui/src/UI/Settings/UIWindowFinalizeUserDeletion.js
@@ -17,11 +17,13 @@
* along with this program. If not, see .
*/
+import { openRevalidatePopup } from '../../util/openid.js';
import UIWindow from '../UIWindow.js';
async function UIWindowFinalizeUserDeletion (options) {
return new Promise(async (resolve) => {
options = options ?? {};
+ const oidc_only = !!(window.user && window.user.oidc_only);
let h = '';
@@ -39,6 +41,22 @@ async function UIWindowFinalizeUserDeletion (options) {
h += ``;
h += '';
}
+ // OIDC-only: revalidate via popup (no password)
+ else if ( oidc_only ) {
+ h += '
';
+ h += '
×
';
+ h += ``;
+ h += `
${i18n('confirm_delete_user')}
`;
+ h += '
';
+ h += '';
+ h += '';
+ h += '
';
+ h += '';
+ h += '';
+ h += ``;
+ h += ``;
+ h += '
';
+ }
// otherwise ask for password
else {
h += '