Store Google avatar bytes and slim the session JWT to id only.

Download profile images on login into picturebinary, return them as base64 from /auth/me, and resolve sessions via getById.
This commit is contained in:
seb
2026-08-05 11:12:36 +02:00
parent c78237a1dd
commit fc2b136c46
12 changed files with 133 additions and 41 deletions

View File

@@ -3,6 +3,7 @@ import { OAuth2Client } from 'google-auth-library';
import env from '../../../config/env.js';
import * as db from '../../../db/googleAuth.js';
import { clearCachedUser } from '../../middleware/auth.js';
import { downloadPicture, toPictureBuffer } from '../../../util/pictureBinary.js';
const googleClient = new OAuth2Client(env.googleClientId);
@@ -18,7 +19,10 @@ export function register(app, basePath = '') {
* post:
* tags: [Auth]
* summary: Login with Google ID token
* description: Verifies a Google ID token, checks allowlist (or creates entry when `IMPLICIT_CREATE=true`), and sets an HTTP-only `token` cookie.
* description: |
* Verifies a Google ID token, checks allowlist (or creates entry when `IMPLICIT_CREATE=true`),
* stores Google `sub` and profile image bytes, and sets an HTTP-only `token` cookie.
* `user.picture` is the profile image as base64 (same as GET /auth/me).
* security: []
* x-markdown: auth/login.md
* requestBody:
@@ -109,19 +113,38 @@ export function register(app, basePath = '') {
return res.status(403).json({ error: 'Email is not authorized' });
}
const pictureUrl = typeof payload.picture === 'string' ? payload.picture : null;
const sub = typeof payload.sub === 'string' ? payload.sub : null;
const existingBinary = toPictureBuffer(allowed.pictureBinary);
let pictureBinary = null;
if (pictureUrl) {
if (pictureUrl === allowed.picture && existingBinary) {
pictureBinary = existingBinary;
} else {
try {
pictureBinary = await downloadPicture(pictureUrl);
} catch (err) {
console.warn(`Profile picture download failed (${email}):`, err.message);
pictureBinary = pictureUrl === allowed.picture ? existingBinary : null;
}
}
}
await db.setProfile(allowed.id, {
picture: payload.picture ?? null,
sub: payload.sub ?? null,
picture: pictureUrl,
pictureBinary,
sub,
});
clearCachedUser(email);
clearCachedUser(allowed.id);
const user = {
id: allowed.id,
email: allowed.email,
selectedCompanyId: allowed.selectedCompanyId,
...(payload.picture && { picture: payload.picture }),
...(pictureBinary && { picture: pictureBinary.toString('base64') }),
};
const token = jwt.sign(user, env.jwtSecret, { expiresIn: '1d' });
const token = jwt.sign({ id: user.id }, env.jwtSecret, { expiresIn: '1d' });
res.cookie('token', token, {
httpOnly: true,