Milestone v1.0: FamilySync MVP #1
@@ -49,6 +49,15 @@ export async function upsertUser(
|
||||
.limit(1)
|
||||
|
||||
if (existing[0]) {
|
||||
// If the existing row has no displayName but the caller supplies one, update it now.
|
||||
// This corrects rows created before robust claim derivation was in place (BUG 2 fix).
|
||||
if (!existing[0].displayName && displayName) {
|
||||
await db
|
||||
.update(users)
|
||||
.set({ displayName })
|
||||
.where(eq(users.id, existing[0].id))
|
||||
return { ...existing[0], displayName }
|
||||
}
|
||||
return existing[0]
|
||||
}
|
||||
|
||||
|
||||
@@ -65,9 +65,19 @@ async function resolveUserId(c: any): Promise<number | null> {
|
||||
|
||||
const iss = (auth.iss as string | undefined) ?? ''
|
||||
const sub = auth.sub ?? ''
|
||||
const email = typeof auth.email === 'string' ? auth.email : undefined
|
||||
|
||||
const user = await upsertUser(iss, sub, email)
|
||||
// Derive displayName with same preference order as me.ts (name → preferred_username
|
||||
// → email → sub fallback). Both call sites must agree so a write-path upsert does not
|
||||
// overwrite a correctly-derived name with a worse one.
|
||||
const claimStr = (v: unknown): string | undefined =>
|
||||
typeof v === 'string' && v.trim() !== '' ? v.trim() : undefined
|
||||
const displayName =
|
||||
claimStr(auth.name) ??
|
||||
claimStr(auth.preferred_username) ??
|
||||
claimStr(auth.email) ??
|
||||
`Member ${String(sub).slice(0, 8)}`
|
||||
|
||||
const user = await upsertUser(iss, sub, displayName)
|
||||
return user?.id ?? null
|
||||
}
|
||||
|
||||
|
||||
@@ -4,8 +4,9 @@
|
||||
* Flow (normal — OIDC active):
|
||||
* 1. getAuth(c) reads iss + sub from the OIDC session JWT cookie
|
||||
* (validated and refreshed by oidcAuthMiddleware — never reaches here unauthenticated)
|
||||
* 2. upsertUser(iss, sub, email) writes the user row on first visit, returns
|
||||
* the existing row on subsequent visits (idempotent, keyed on iss+sub, D-10)
|
||||
* 2. Derives displayName from OIDC claims (name → preferred_username → email → sub fallback)
|
||||
* then calls upsertUser(iss, sub, displayName) — writes on first visit, corrects a
|
||||
* previously blank displayName on subsequent visits (idempotent, keyed on iss+sub, D-10)
|
||||
* 3. Returns { user: { id, displayName, color } }
|
||||
*
|
||||
* Flow (dev bypass — DEV_AUTH_BYPASS=true, non-production):
|
||||
@@ -48,12 +49,28 @@ meRouter.get('/', async (c) => {
|
||||
return c.json({ error: 'Unauthorized' }, 401)
|
||||
}
|
||||
|
||||
// iss and sub are the stable identity fields; email is a display hint only (D-10)
|
||||
// iss and sub are the stable identity fields — identity is always keyed on iss+sub (D-10).
|
||||
const iss = (auth.iss as string | undefined) ?? ''
|
||||
const sub = auth.sub ?? ''
|
||||
const email = typeof auth.email === 'string' ? auth.email : undefined
|
||||
|
||||
const user = await upsertUser(iss, sub, email)
|
||||
// Derive the best available display name from OIDC claims, in preference order:
|
||||
// 1. name — full name set by the IdP (most human-friendly)
|
||||
// 2. preferred_username — often the login handle; still readable
|
||||
// 3. email — readable but reveals contact info; acceptable fallback
|
||||
// 4. sub — always present; not human-friendly but never blank
|
||||
//
|
||||
// Each candidate is tested defensively — Authelia may omit or blank-out any claim.
|
||||
// Whether Authelia emits name/preferred_username is an operator configuration concern
|
||||
// (e.g. userinfo scope, claim mappings in authelia config) — out of scope here.
|
||||
const claimStr = (v: unknown): string | undefined =>
|
||||
typeof v === 'string' && v.trim() !== '' ? v.trim() : undefined
|
||||
const displayName =
|
||||
claimStr(auth.name) ??
|
||||
claimStr(auth.preferred_username) ??
|
||||
claimStr(auth.email) ??
|
||||
`Member ${String(sub).slice(0, 8)}`
|
||||
|
||||
const user = await upsertUser(iss, sub, displayName)
|
||||
|
||||
if (!user) {
|
||||
return c.json({ error: 'Could not resolve user' }, 500)
|
||||
|
||||
Reference in New Issue
Block a user