OAuth callbacks

Hi everyone,

I’m building an app on Teable and have run into issues in its generated authentication code. The OAuth callbacks appear to create a session before the app-specific authorization check, and the generated files also have TypeScript errors.

I’ve prepared a tested repair proposal. I’m trying to understand the supported path for getting a fix like this implemented durably: should it be handled through the app-auth generator, a per-app hook, or another platform mechanism? I want the fix to survive app regeneration and sync, rather than patching generated Preview files.

What’s the right process, and what information should I provide to the platform team? I’ll share app-specific details and the patch privately with support rather than posting them publicly.

Hi @jpoland00, thanks for the detailed write-up. We’ve received your issue report with the patch and verification notes, so you don’t need to send anything else for now.

Short answer: the admission logic doesn’t need a generator change. The durable place for app-specific sign-in rules is lib/auth.ts, which your app already owns.

Which auth files Teable owns

  • Rewritten on every sync, so edits won’t survive: app/auth/*/callback/route.ts, lib/public-url.ts, lib/credentials.ts, lib/request.ts
  • Generated once when login is first enabled, then yours (sync never overwrites them): lib/auth.ts, app/login/page.tsx, app/login/actions.ts, proxy.ts, components/auth/user-menu.tsx

What Teable checks before a session exists

The access mode in Login settings is enforced on Teable’s server before any session token is issued, for every provider. With “Existing users only”, an email that isn’t in the user table is turned away there: no email code is sent, and an OAuth sign-in comes back as an error instead of a code, so there’s nothing for your callback to exchange. The check runs again on every getSession() / requireAuth(), so deleting a user’s row or changing its email ends their session at the next check.

Where rules beyond that go (active flag, duplicate emails, your own login history)

The callback routes are deliberately thin. They call exchangeOAuthCode() from lib/auth.ts, and if it returns { success: false } they redirect to /login without setting a cookie. Every path that sets the session cookie runs through lib/auth.ts:

  • direct OAuth callback: exchangeOAuthCode()
  • popup sign-in (relay in app/login/actions.ts): setSessionFromToken()
  • email code: verifyCode()

Put your admission check and login-history write in those functions, before cookieStore.set(...). When you deny, return a generic error string: the callback copies it into the message query parameter.

Put the same check in getSession() too. A sign-in check doesn’t end a session that was already issued (sessions last up to 7 days), and proxy.ts only checks the cookie’s signature and expiry. Calling requireAuth() from a shared server layout makes every page render re-validate, not just your data actions.

The App Builder agent’s guide tells it not to touch auth files unless asked, so ask it explicitly to edit lib/auth.ts.

The TypeScript errors

Those are ours. All three reproduce with the scaffold’s own settings (TypeScript 5.9.3, strict: false):

  • the callbacks read result.error after checking !result.success, which doesn’t narrow the union when strictNullChecks is off
  • lib/credentials.ts passes a { message?: string } body to serverCodeOf (TS2559)

They don’t affect runtime behavior, and app builds skip type checking. We’ll still fix them in the templates. Those files are platform-managed, so the fix reaches your app on its next sync with nothing for you to do. We’ll reply here when it’s live.

1 Like