mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-23 06:46:42 +02:00
fix(mana-auth): /api/v1/auth/login mints JWT via auth.handler instead of api.signInEmail
Previous attempt (commit 55cc75e7d) tried to fix the broken JWT mint
in /api/v1/auth/login by switching the cookie name from
`mana.session_token` to `__Secure-mana.session_token` for production.
That was necessary but not sufficient: Better Auth's session cookie
value isn't just the raw session token, it's `<token>.<HMAC>` where
the HMAC is derived from the better-auth secret. Reconstructing the
cookie from auth.api.signInEmail's JSON response only gave us the raw
token, so /api/auth/token's get-session middleware still couldn't
validate it and the JWT mint kept silently failing.
Real fix: do the sign-in via auth.handler (the HTTP path) rather than
auth.api.signInEmail (the SDK path). The handler returns a real fetch
Response with a Set-Cookie header containing the fully signed cookie
envelope. We capture that header verbatim and forward it as the cookie
on the /api/auth/token request, which now passes validation and mints
the JWT correctly.
Verified end-to-end on auth.mana.how:
$ curl -X POST https://auth.mana.how/api/v1/auth/login \
-d '{"email":"...","password":"..."}'
{
"user": {...},
"token": "<session token>",
"accessToken": "eyJhbGciOiJFZERTQSI...", ← real JWT now
"refreshToken": "<session token>"
}
Side benefits:
- The email-not-verified path is now handled by checking
signInResponse.status === 403 directly, no more catching APIError
with the comment-noted async-stream footgun.
- X-Forwarded-For is forwarded explicitly so Better Auth's rate limiter
and our security log see the real client IP.
- The leftover catch block now only handles unexpected exceptions
(network errors etc); the FORBIDDEN-checking logic in it is dead but
harmless and left in for defense in depth.
This commit is contained in:
parent
4eb5dfe4a0
commit
bfa8a0a773
254 changed files with 88 additions and 29437 deletions
|
|
@ -1,7 +1,6 @@
|
|||
import type {
|
||||
SendEmailOptions,
|
||||
SendPushOptions,
|
||||
SendMatrixOptions,
|
||||
SendWebhookOptions,
|
||||
ScheduleOptions,
|
||||
NotificationResponse,
|
||||
|
|
@ -82,24 +81,6 @@ export class NotifyClient {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a Matrix message
|
||||
*/
|
||||
async sendMatrix(options: SendMatrixOptions): Promise<NotificationResponse> {
|
||||
return this.send({
|
||||
channel: 'matrix',
|
||||
appId: this.appId,
|
||||
recipient: options.roomId,
|
||||
body: options.body,
|
||||
matrixOptions: {
|
||||
formattedBody: options.formattedBody,
|
||||
msgtype: options.msgtype,
|
||||
},
|
||||
priority: options.priority,
|
||||
externalId: options.externalId,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a webhook notification
|
||||
*/
|
||||
|
|
@ -184,7 +165,6 @@ export class NotifyClient {
|
|||
notifications: Array<
|
||||
| ({ type: 'email' } & SendEmailOptions)
|
||||
| ({ type: 'push' } & SendPushOptions)
|
||||
| ({ type: 'matrix' } & SendMatrixOptions)
|
||||
| ({ type: 'webhook' } & SendWebhookOptions)
|
||||
>
|
||||
): Promise<BatchNotificationResponse> {
|
||||
|
|
@ -214,15 +194,6 @@ export class NotifyClient {
|
|||
priority: n.priority,
|
||||
externalId: n.externalId,
|
||||
};
|
||||
} else if (n.type === 'matrix') {
|
||||
return {
|
||||
channel: 'matrix' as const,
|
||||
appId: this.appId,
|
||||
recipient: n.roomId,
|
||||
body: n.body,
|
||||
priority: n.priority,
|
||||
externalId: n.externalId,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
channel: 'webhook' as const,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
export type NotificationChannel = 'email' | 'push' | 'matrix' | 'webhook';
|
||||
export type NotificationChannel = 'email' | 'push' | 'webhook';
|
||||
export type NotificationPriority = 'low' | 'normal' | 'high' | 'critical';
|
||||
export type NotificationStatus = 'pending' | 'processing' | 'delivered' | 'failed' | 'cancelled';
|
||||
|
||||
|
|
@ -28,15 +28,6 @@ export interface SendPushOptions {
|
|||
externalId?: string;
|
||||
}
|
||||
|
||||
export interface SendMatrixOptions {
|
||||
roomId: string;
|
||||
body: string;
|
||||
formattedBody?: string;
|
||||
msgtype?: 'text' | 'notice';
|
||||
priority?: NotificationPriority;
|
||||
externalId?: string;
|
||||
}
|
||||
|
||||
export interface SendWebhookOptions {
|
||||
url: string;
|
||||
method?: 'POST' | 'PUT';
|
||||
|
|
|
|||
|
|
@ -63,9 +63,6 @@ const inventorySvg = `<svg width="1024" height="1024" viewBox="0 0 1024 1024" fi
|
|||
// Questions icon (question mark with magnifying glass)
|
||||
const questionsSvg = `<svg width="1024" height="1024" viewBox="0 0 1024 1024" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="130" y="130" width="764" height="764" rx="382" fill="url(#questionsGrad)"/><circle cx="480" cy="440" r="180" stroke="white" stroke-width="40"/><path d="M620 580L740 700" stroke="white" stroke-width="48" stroke-linecap="round"/><path d="M440 360C440 332 462 310 490 310C520 310 550 330 550 370C550 420 490 430 490 480" stroke="white" stroke-width="32" stroke-linecap="round"/><circle cx="490" cy="540" r="20" fill="white"/><defs><linearGradient id="questionsGrad" x1="130" y1="130" x2="894" y2="894" gradientUnits="userSpaceOnUse"><stop stop-color="#8b5cf6"/><stop offset="1" stop-color="#7c3aed"/></linearGradient></defs></svg>`;
|
||||
|
||||
// Matrix icon (network/federated chat with purple gradient)
|
||||
const matrixSvg = `<svg width="1024" height="1024" viewBox="0 0 1024 1024" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="130" y="130" width="764" height="764" rx="382" fill="url(#matrixGrad)"/><circle cx="512" cy="400" r="80" fill="white"/><circle cx="340" cy="580" r="60" fill="white" fill-opacity="0.8"/><circle cx="684" cy="580" r="60" fill="white" fill-opacity="0.8"/><circle cx="420" cy="720" r="50" fill="white" fill-opacity="0.6"/><circle cx="604" cy="720" r="50" fill="white" fill-opacity="0.6"/><path d="M512 480V640M512 640L420 700M512 640L604 700" stroke="white" stroke-width="16" stroke-linecap="round"/><path d="M450 440L370 540M574 440L654 540" stroke="white" stroke-width="16" stroke-linecap="round"/><path d="M340 640L400 700M684 640L624 700" stroke="white" stroke-width="12" stroke-linecap="round" stroke-opacity="0.6"/><defs><linearGradient id="matrixGrad" x1="130" y1="130" x2="894" y2="894" gradientUnits="userSpaceOnUse"><stop stop-color="#8b5cf6"/><stop offset="1" stop-color="#7c3aed"/></linearGradient></defs></svg>`;
|
||||
|
||||
// CityCorners icon (map pin with blue gradient)
|
||||
const citycornersSvg = `<svg width="1024" height="1024" viewBox="0 0 1024 1024" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="130" y="130" width="764" height="764" rx="382" fill="url(#ccGrad)"/><path d="M512 200C408.3 200 324 284.3 324 388C324 536 512 800 512 800C512 800 700 536 700 388C700 284.3 615.7 200 512 200ZM512 468C467.8 468 432 432.2 432 388C432 343.8 467.8 308 512 308C556.2 308 592 343.8 592 388C592 432.2 556.2 468 512 468Z" fill="white"/><circle cx="512" cy="388" r="60" fill="#2563eb" fill-opacity="0.4"/><defs><linearGradient id="ccGrad" x1="130" y1="130" x2="894" y2="894" gradientUnits="userSpaceOnUse"><stop stop-color="#2563eb"/><stop offset="1" stop-color="#1d4ed8"/></linearGradient></defs></svg>`;
|
||||
|
||||
|
|
@ -101,7 +98,6 @@ export const APP_ICONS = {
|
|||
mail: svgToDataUrl(mailSvg),
|
||||
inventory: svgToDataUrl(inventorySvg),
|
||||
questions: svgToDataUrl(questionsSvg),
|
||||
matrix: svgToDataUrl(matrixSvg),
|
||||
context: svgToDataUrl(contextSvg),
|
||||
citycorners: svgToDataUrl(citycornersSvg),
|
||||
times: svgToDataUrl(timesSvg),
|
||||
|
|
|
|||
|
|
@ -377,23 +377,6 @@ export const MANA_APPS: ManaApp[] = [
|
|||
status: 'beta',
|
||||
requiredTier: 'alpha',
|
||||
},
|
||||
{
|
||||
id: 'matrix',
|
||||
name: 'Mana Matrix',
|
||||
description: {
|
||||
de: 'Matrix Chat Client',
|
||||
en: 'Matrix Chat Client',
|
||||
},
|
||||
longDescription: {
|
||||
de: 'Verbinde dich mit dem dezentralen Matrix-Netzwerk für sichere, föderierte Kommunikation.',
|
||||
en: 'Connect to the decentralized Matrix network for secure, federated communication.',
|
||||
},
|
||||
icon: APP_ICONS.matrix,
|
||||
color: '#8b5cf6',
|
||||
comingSoon: false,
|
||||
status: 'beta',
|
||||
requiredTier: 'alpha',
|
||||
},
|
||||
{
|
||||
id: 'context',
|
||||
name: 'Context',
|
||||
|
|
@ -796,7 +779,7 @@ export const APP_SLIDER_LABELS = {
|
|||
* App URLs — unified app uses internal paths, separate apps use subdomains.
|
||||
*
|
||||
* All productivity apps are now served under mana.how/{appId}.
|
||||
* Games and Matrix remain on separate subdomains.
|
||||
* Games remain on separate subdomains.
|
||||
*/
|
||||
export const APP_URLS: Record<AppIconId, { dev: string; prod: string }> = {
|
||||
// ─── Unified App (internal paths) ─────────────────────────
|
||||
|
|
@ -837,7 +820,6 @@ export const APP_URLS: Record<AppIconId, { dev: string; prod: string }> = {
|
|||
news: { dev: 'http://localhost:5173/news', prod: 'https://mana.how/news' },
|
||||
mail: { dev: 'http://localhost:5173/mail', prod: 'https://mana.how/mail' },
|
||||
// ─── Separate Apps (own subdomains) ───────────────────────
|
||||
matrix: { dev: 'http://localhost:5180', prod: 'https://matrix.mana.how' },
|
||||
arcade: { dev: 'http://localhost:5201', prod: 'https://arcade.mana.how' },
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue