fix(oidc): fix QR code login issues caused by OIDC

In implementing OIDC it became necessary to introduce the separation of
"GUI Tokens" and "Session Tokens". This breaks QR login because Puter
does not set the HTTP-only session cookie when logging in with this
flow.

Add a middelware to WebServerService to detect QR Code logins and set
the appropriate HTTP-only session cookie.
This commit is contained in:
KernelDeimos
2026-02-19 16:13:44 -05:00
parent 8923bdac95
commit e2068e7b9c
3 changed files with 48 additions and 1 deletions
@@ -337,6 +337,34 @@ class WebServerService extends BaseService {
next();
});
// When the user visits the main origin (not api/dav subdomain) with ?auth_token=<GUI token>
// (e.g. QR login), set the HTTP-only session cookie so user-protected endpoints work.
app.use(async (req, res, next) => {
const has_subdomain = req.hostname.slice(0, -1 * (config.domain.length + 1)) !== '';
if ( has_subdomain ) return next();
const token = req.query?.auth_token;
if ( !token || typeof token !== 'string' ) return next();
try {
const svc_auth = req.services.get('auth');
const cleanToken = token.replace('Bearer ', '').trim();
const actor = await svc_auth.authenticate_from_token(cleanToken);
const session_token = svc_auth.create_session_token_for_session(
actor.type.user,
actor.type.session,
);
res.cookie(config.cookie_name, session_token, {
sameSite: 'none',
secure: true,
httpOnly: true,
});
} catch ( _e ) {
// Invalid or expired token; do not set cookie
}
next();
});
// Measure data transfer amounts
app.use(measure());
@@ -25,7 +25,7 @@ const { Context } = require('../../util/context');
module.exports = {
route: '/change-username',
methods: ['POST'],
handler: async (req, res, next) => {
handler: async (req, res, _next) => {
const user = req.user;
const new_username = req.body.new_username;
@@ -355,6 +355,25 @@ class AuthService extends BaseService {
}, this.global_config.jwt_secret);
}
/**
* Creates a session token (hasHttpPowers) for an existing session.
* Used when the client authenticated with a GUI token (e.g. QR login via
* ?auth_token=) so we can set the HTTP-only cookie and allow user-protected
* endpoints (change password, email, username, etc.) to work.
*
* @param {*} user - User object (must have .uuid).
* @param {string} session_uuid - Existing session UUID.
* @returns {string} JWT session token.
*/
create_session_token_for_session (user, session_uuid) {
return this.modules.jwt.sign({
type: 'session',
version: '0.0.0',
uuid: session_uuid,
user_uid: user.uuid,
}, this.global_config.jwt_secret);
}
/**
* This method checks if the provided session token is valid and returns the associated user and token.
* If the token is not a valid session token or it does not exist in the database, it returns an empty object.