reference

OAuth (Browser, Native & ID Token)

Browser redirect OAuth, native app custom URL schemes, and native Apple/Google sign-in without a browser.

Published: September 6, 2026Updated: September 6, 2026

OAuth (browser redirect)

await postbase.auth.signInWithOAuth({
  provider: 'google', // or 'github', 'discord', 'apple', etc.
  options: { redirectTo: 'https://yourapp.com/callback' },
})

OAuth (native apps — custom URL scheme)

For iOS, macOS, or Android apps using an in-app browser (ASWebAuthenticationSession / Chrome Custom Tab), pass your app’s custom URL scheme as redirectTo. The server callback redirects to it instead of an https:// URL, and your app receives the session tokens in the URL.

// Opens the authorize URL — in native environments, open it in an
// ASWebAuthenticationSession or Chrome Custom Tab instead of a browser tab.
const authorizeUrl = await postbase.auth.signInWithOAuth({
  provider: 'github',
  options: { redirectTo: 'com.myapp://auth/callback' },
})

// After the in-app browser calls back to your app URL, parse the session:
const { data, error } = await postbase.auth.handleOAuthCallback({
  url: 'com.myapp://auth/callback?access_token=...&refresh_token=...',
})

Sign in with Apple / Google (native SDK — no browser)

For iOS/macOS apps using ASAuthorizationController (Apple) or GIDSignIn (Google), skip the browser entirely and pass the token straight to Postbase:

// iOS — Apple Sign In (Swift → pass identityToken to your JS layer)
const { data, error } = await postbase.auth.signInWithIdToken({
  provider: 'apple',
  idToken: appleCredential.identityToken, // string JWT from ASAuthorizationAppleIDCredential
  nonce: nonce, // optional — include if you passed a nonce to ASAuthorizationAppleIDRequest
  rememberMe: true, // optional — 30-day refresh token instead of the default 7-day
})

// Android / Web — Google Sign-In
const { data, error } = await postbase.auth.signInWithIdToken({
  provider: 'google',
  idToken: googleCredential.idToken, // string JWT from GIDSignIn / Google Identity Services
  rememberMe: true, // optional
})

// data.session.accessToken, data.session.refreshToken, data.user

Note: For Apple, the provider must be enabled in your Postbase dashboard. The clientId field should contain your Apple Service ID (for web) or comma-separated list of Bundle IDs (for native), matching the aud claim in Apple’s id_token.


Handle OAuth Callback

In browser apps, call this on the page your redirectTo URL points to — it reads window.location.search automatically:

// pages/callback.tsx (or equivalent)
const { data, error } = await postbase.auth.handleOAuthCallback()
// data.session, data.user

For native apps, pass the URL your app scheme received:

const { data, error } = await postbase.auth.handleOAuthCallback({
  url: incomingUrl, // e.g. 'com.myapp://auth/callback?access_token=...'
})

After handleOAuthCallback() resolves on the client, forward the session to your API route and call setSession on a server client so the session is stored as an httpOnly postbase-session cookie. Subsequent SSR requests are authenticated automatically.

// app/auth/callback/page.tsx  (Client Component)
'use client'
import { createBrowserClient } from 'postbasejs/ssr'

const postbase = createBrowserClient(
  process.env.NEXT_PUBLIC_POSTBASE_URL!,
  process.env.NEXT_PUBLIC_POSTBASE_ANON_KEY!,
  { projectId: process.env.NEXT_PUBLIC_POSTBASE_PROJECT_ID! }
)

const { data, error } = await postbase.auth.handleOAuthCallback()
if (data.session) {
  await fetch('/api/auth/callback', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ session: data.session }),
    credentials: 'include',
  })
}
router.push('/dashboard')
// app/api/auth/callback/route.ts  (API Route)
import { cookies } from 'next/headers'
import { createServerClient } from 'postbasejs/ssr'

export async function POST(req: Request) {
  const { session } = await req.json()
  const cookieStore = await cookies()

  const postbase = createServerClient(
    process.env.NEXT_PUBLIC_POSTBASE_URL!,
    process.env.NEXT_PUBLIC_POSTBASE_ANON_KEY!,
    {
      projectId: process.env.NEXT_PUBLIC_POSTBASE_PROJECT_ID!,
      cookies: {
        getAll: () => cookieStore.getAll(),
        setAll: (cs) => cs.forEach(c => cookieStore.set(c.name, c.value, c.options as any)),
      },
    }
  )

  const { error } = await postbase.auth.setSession(session)
  if (error) return Response.json({ error }, { status: 400 })
  return Response.json({ ok: true })
}

The server client writes a postbase-session httpOnly cookie that createServerClient reads on every subsequent request — no manual cookie parsing needed.