OAuth & Google Login
Setup Overview
Teenybase supports three OAuth-related flows, all configured through DatabaseSettings:
- Google One Tap (JWT token via form POST) — simplest, no redirects
- OAuth2 Authorization Code (redirect flow) — Google, Discord, LinkedIn, etc.
- Bearer Token Login — for tokens obtained client-side (e.g., Firebase, Auth0)
All three create a session and return a JWT + refresh token. Flows 1 and 2 can also set an auth cookie.
1. Google One Tap Login
Uses Google's Sign In With Google HTML API. Google sends a JWT credential directly to your backend via form POST.
Config
// teenybase.ts
const settings: DatabaseSettings = {
appUrl: 'https://myapp.com',
jwtSecret: '$JWT_SECRET',
authProviders: [
{ name: 'google', clientId: '$GOOGLE_CLIENT_ID' }
],
authCookie: { // optional: set token as cookie
name: 'auth_token',
httpOnly: true,
secure: true,
sameSite: 'Lax',
path: '/',
maxAge: 60 * 60 * 24 * 7, // 7 days
},
tables: [{
name: 'users',
// ... fields with auth extension
}],
}Frontend
Login URI
<div id="g_id_onload"
data-client_id="YOUR_GOOGLE_CLIENT_ID"
data-login_uri="https://api.myapp.com/api/v1/table/users/auth/google-login"
data-auto_prompt="true">
</div>
<script src="https://accounts.google.com/gsi/client" async></script>Google posts credential + g_csrf_token as application/x-www-form-urlencoded to your endpoint. Teenybase verifies the CSRF cookie, decodes the JWT credential using the Google provider in authProviders, finds/creates the user, and returns the session. If authCookie is configured, the token is also set as a cookie.
Endpoint: POST /api/v1/table/{auth_table}/auth/google-login
SPA
For SPAs, use the Google JS API to get the credential client-side, then send it to the login-token endpoint as a Bearer token:
google.accounts.id.initialize({
client_id: 'YOUR_GOOGLE_CLIENT_ID',
callback: async (response) => {
const res = await fetch('/api/v1/table/users/auth/login-token', {
method: 'POST',
headers: { 'Authorization': `Bearer ${response.credential}` },
})
const { token, refresh_token, record } = await res.json()
localStorage.setItem('auth_token', token)
localStorage.setItem('refresh_token', refresh_token)
},
})
google.accounts.id.prompt() // show One Tap UIThen use the stored token for subsequent requests:
fetch('/api/v1/table/posts', {
headers: { 'Authorization': `Bearer ${localStorage.getItem('auth_token')}` },
})2. OAuth2 Redirect Flow
For standard OAuth2 authorization code flow with any provider.
Config
const settings: DatabaseSettings = {
// ... base config + authCookie (recommended for redirect flow)
authProviders: [
{
name: 'google', // preset: URLs + scopes auto-filled
clientId: '$GOOGLE_CLIENT_ID',
clientSecret: '$GOOGLE_CLIENT_SECRET',
redirectUrl: 'https://myapp.com/dashboard', // where to land after login
},
{
name: 'discord', // preset available
clientId: '$DISCORD_CLIENT_ID',
clientSecret: '$DISCORD_CLIENT_SECRET',
},
{
name: 'custom-provider', // fully manual
authorizeUrl: 'https://provider.com/oauth/authorize',
tokenUrl: 'https://provider.com/oauth/token',
userinfoUrl: 'https://provider.com/api/userinfo',
clientId: '$CUSTOM_CLIENT_ID',
clientSecret: '$CUSTOM_CLIENT_SECRET',
scopes: ['openid', 'email'],
mapping: { email: 'email_address', avatar: 'photo_url' },
},
],
}Built-in presets: google, github, discord, linkedin (auto-fill URLs, scopes, field mappings).
Frontend
Just link to the authorize endpoint:
<a href="https://api.myapp.com/api/v1/table/users/auth/oauth/google">
Sign in with Google
</a>
<!-- Pass a redirect URL to land somewhere specific after login -->
<a href="https://api.myapp.com/api/v1/table/users/auth/oauth/google?redirect=/settings">
Sign in with Google
</a>Flow: User clicks link -> redirected to provider -> authenticates -> redirected back to callback -> session created + cookie set -> redirected to redirect param (or redirectUrl config, or appUrl).
Redirect URL validation
The redirect parameter is validated to prevent open redirect attacks:
- Relative paths (e.g.,
/settings) are resolved againstappUrl(e.g.,https://myapp.com/settings). - No
allowedRedirectUrlsconfigured (default): only URLs matchingappUrlhostname are allowed. allowedRedirectUrlsconfigured: only exact matches against that list are allowed.- Rejected URLs: silently fall back to
provider.redirectUrl, thenappUrl.
// Example: restrict redirects to specific URLs
{
appUrl: 'https://myapp.com',
allowedRedirectUrls: [
'https://myapp.com/dashboard',
'https://myapp.com/settings',
'https://staging.myapp.com/dashboard',
],
}Endpoints:
GET /api/v1/table/{auth_table}/auth/oauth/{provider}— starts the flowGET /api/v1/table/{auth_table}/auth/oauth/{provider}/callback— handles the callback (register this as the redirect URI in your provider's dashboard)
Custom Exchange Handlers
For providers with non-standard flows (GitHub private emails, Facebook Graph API, X/Twitter PKCE), register a custom handler:
import { TableAuthExtension } from 'teenybase/worker'
TableAuthExtension.oauthExchangeHandlers['github'] = async ({ c, code, callbackUrl, clientId, clientSecret }) => {
// Exchange code, fetch /user + /user/emails, etc.
return { email: 'user@example.com', name: 'User', username: 'user123' }
}The framework handles CSRF, session creation, cookies, and redirect. The handler only does token exchange + user info.
3. Bearer Token Login
For tokens obtained on the client side (e.g., from Firebase Auth, Auth0, or a mobile SDK).
// Frontend: send the external JWT as a Bearer token
const res = await fetch('/api/v1/table/users/auth/login-token', {
method: 'POST',
headers: { 'Authorization': `Bearer ${externalJwtToken}` },
})
const { token, refresh_token, record } = await res.json()Teenybase verifies the token against authProviders, finds/creates the user, and returns a session.
Cookie-Based Auth
When authCookie is configured, teenybase reads the token from the cookie on every request (after checking the Authorization header). Cookies are set automatically on OAuth redirect flows (Google One Tap, OAuth callback).
For JSON endpoints (login-password, sign-up), the token is returned in the response body — your app sets the cookie:
// After calling login-password or sign-up, set the cookie server-side (SSR)
const { token } = await response.json()
setCookie('auth_token', token, { httpOnly: true, secure: true, sameSite: 'Lax', path: '/' })
// Subsequent requests — cookie is sent automatically by the browser
const res = await fetch('/api/v1/table/posts', { credentials: 'include' })Logout (clears cookie + invalidates session):
- Table-level:
POST /api/v1/table/{auth_table}/auth/logout - Global (cookie only):
POST /api/v1/auth/logout
See also: Configuration Reference | Frontend Guide | Troubleshooting