Skip to content

Fix email app auth and send routes#7346

Open
moishadzh-code wants to merge 1 commit into
BasedHardware:mainfrom
moishadzh-code:fix-email-app-routing
Open

Fix email app auth and send routes#7346
moishadzh-code wants to merge 1 commit into
BasedHardware:mainfrom
moishadzh-code:fix-email-app-routing

Conversation

@moishadzh-code
Copy link
Copy Markdown

@moishadzh-code moishadzh-code commented May 17, 2026

Summary

  • mount the email app OAuth routes under /api/auth
  • wire the email routes to the existing user lookup and Gmail send helpers
  • require authenticated callers for /api/email/draft and /api/email/send and derive the user from the verified JWT
  • update the email app login links to point at the mounted OAuth routes

Related to #2315

Tests

  • node --check plugins/apps-js/server.js
  • node --check plugins/apps-js/email/src/routes/email.js
  • node --check plugins/apps-js/email/src/routes/auth.js
  • git diff --check

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 17, 2026

Greptile Summary

This PR mounts the email app OAuth routes under /api/auth, populates app.locals with getAuthenticatedUser and sendEmail helpers, and imports the auth router so that the previously broken /api/email/draft and /api/email/send endpoints can resolve their shared dependencies.

  • Auth routes wired: authRouter is now mounted at /api/auth, activating the Google OAuth login/callback, token refresh, and logout flow for the first time.
  • Shared app locals set: getAuthenticatedUser (Supabase user lookup) and sendEmail (wrapping emailUtils.draftEmail) are attached to app.locals so the email routes can resolve them at request time instead of returning "Email service functions not available".
  • Identity wrapper added: app.locals.sendEmail is wrapped in an unnecessary arrow function that passes all arguments unchanged to the underlying sendEmailWithGmail.

Confidence Score: 2/5

Not safe to merge as-is — two auth/security concerns must be addressed before the email send path is exposed publicly.

The PR activates two previously unreachable code paths. The POST /api/email/send and POST /api/email/draft endpoints now resolve their app.locals dependencies but still accept a plain userId from the request body with no proof of ownership, allowing any caller to send email on behalf of any known Omi user. The OAuth callback, now mounted at /api/auth/callback, includes the JWT in the redirect URL where it will be recorded in browser history and server logs.

plugins/apps-js/server.js (authorization gap activated by app.locals wiring) and plugins/apps-js/email/src/routes/auth.js (JWT-in-URL pattern now reachable).

Security Review

  • Missing caller authorization on email send/draft (plugins/apps-js/email/src/routes/email.js, POST /api/email/send and POST /api/email/draft): getAuthenticatedUser performs a user lookup by Omi UID but does not authenticate the caller. Any party supplying a known UID gains full access to that user's Gmail tokens. This path was previously broken (app.locals unpopulated); this PR makes it reachable.
  • JWT token leaked in OAuth redirect URL (plugins/apps-js/email/src/routes/auth.js, /api/auth/callback): Signed JWT appended as a URL query parameter on both the success and partial-auth redirect paths. The route is newly mounted by this PR. Tokens in query strings appear in browser history, server logs, and Referer headers.

Important Files Changed

Filename Overview
plugins/apps-js/server.js Mounts auth router and wires app.locals; activates previously non-functional email endpoints that accept userId from request body without token-based authorization.

Sequence Diagram

sequenceDiagram
    participant Client
    participant Server as Express Server
    participant AuthRouter as /api/auth (authRouter)
    participant EmailRouter as /api/email (emailRouter)
    participant AppLocals as app.locals
    participant Supabase
    participant Gmail

    Note over Server: server.js wires app.locals on startup
    Server->>AppLocals: "locals.getAuthenticatedUser = getAuthenticatedUser"
    Server->>AppLocals: "locals.sendEmail = sendEmailWithGmail wrapper"

    Client->>AuthRouter: GET /api/auth/login/:omiuid
    AuthRouter->>Supabase: Store state in Redis
    AuthRouter-->>Client: Redirect to Google OAuth

    Client->>AuthRouter: "GET /api/auth/callback?code=...&state=..."
    AuthRouter->>Supabase: Validate state, upsert user
    AuthRouter-->>Client: Redirect with JWT token in URL + httpOnly cookie

    Client->>EmailRouter: "POST /api/email/send {userId, recipientEmail, ...}"
    EmailRouter->>AppLocals: req.app.locals.getAuthenticatedUser(userId)
    AppLocals->>Supabase: Lookup user by omiuid
    Supabase-->>AppLocals: user record with Gmail tokens
    AppLocals-->>EmailRouter: user
    EmailRouter->>AppLocals: req.app.locals.sendEmail(...)
    AppLocals->>Gmail: gmail.users.messages.send
    Gmail-->>AppLocals: message id
    AppLocals-->>EmailRouter: result
    EmailRouter-->>Client: 200 OK
Loading

Reviews (1): Last reviewed commit: "Fix email app auth and send routes" | Re-trigger Greptile

Comment thread plugins/apps-js/server.js Outdated
Comment on lines 56 to 59
return sendEmailWithGmail(recipientEmail, subject, content, user, options);
};

// Rate limiting middleware
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 security No authorization on send/draft endpoints

getAuthenticatedUser(userId) performs a plain Supabase lookup — it confirms the user exists but does not verify that the caller is that user. Now that app.locals is wired up, POST /api/email/send and POST /api/email/draft are fully functional. Any caller who supplies a valid Omi UID in the request body (userId) will be granted access to that user's Gmail credentials and can send email on their behalf. There is no Bearer token, API key, HMAC signature, or any other proof of ownership checked on these two endpoints.

Comment thread plugins/apps-js/server.js Outdated
Comment on lines +55 to +57
app.locals.sendEmail = (recipientEmail, subject, content, user, options = {}) => {
return sendEmailWithGmail(recipientEmail, subject, content, user, options);
};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 The arrow-function wrapper passes all arguments through to sendEmailWithGmail unchanged, so it can be replaced with a direct assignment.

Suggested change
app.locals.sendEmail = (recipientEmail, subject, content, user, options = {}) => {
return sendEmailWithGmail(recipientEmail, subject, content, user, options);
};
app.locals.sendEmail = sendEmailWithGmail;

@moishadzh-code moishadzh-code force-pushed the fix-email-app-routing branch from 5e28db6 to 8fb8069 Compare May 17, 2026 13:15
@moishadzh-code moishadzh-code force-pushed the fix-email-app-routing branch from 8fb8069 to 872c8a1 Compare May 17, 2026 13:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant