# supabase-oauth-popup

[![npm version](https://img.shields.io/npm/v/supabase-oauth-popup.svg)](https://www.npmjs.com/package/supabase-oauth-popup)
[![Supabase](https://img.shields.io/badge/Supabase-v2-3ECF8E)](https://supabase.com/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![bundle size](https://img.shields.io/bundlephobia/minzip/supabase-oauth-popup)](https://bundlephobia.com/package/supabase-oauth-popup)

Ensure a Supabase session via OAuth in a **popup** (Google/GitHub/…) with a full-page redirect fallback.

<!--

ChatGPT: https://chatgpt.com/c/68edbe11-fac4-8323-8bf3-90fe25ea5e67

-->

## Installation

Use with a bundler:

```js
import ensureSupabaseSession from "supabase-oauth-popup";
```

Use via CDN (ESM import map):

```html
<script type="importmap">
{
  "imports": {
    "supabase-oauth-popup": "https://cdn.jsdelivr.net/npm/supabase-oauth-popup@1/dist/index.js"
  }
}
</script>
```

Install locally with npm:

```bash
npm install supabase-oauth-popup
```

…then import via an import map in your HTML (no bundler):

```html
<script type="importmap">
{
  "imports": {
    "supabase-oauth-popup": "./node_modules/supabase-oauth-popup/dist/index.js"
  }
}
</script>
```

## Usage

```ts
import { createClient } from "@supabase/supabase-js";
import ensureSupabaseSession from "supabase-oauth-popup";

const supabase = createClient(
  "https://YOUR-PROJECT.supabase.co",
  "YOUR_PUBLIC_ANON_KEY",
  {
    auth: {
      detectSessionInUrl: true,
      persistSession: true,
      autoRefreshToken: true,
    },
  },
);

// Trigger from a user gesture to avoid popup blockers
document.getElementById("login")!.addEventListener("click", async () => {
  const session = await ensureSupabaseSession(supabase, { provider: "google" });
  // Example query (enforce access with your RLS policies)
  const { data, error } = await supabase.from("demos").select("*");
  console.log(data, error);
});
```

## API

```ts
async function ensureSupabaseSession(
  client: SupabaseClient,
  options?: {
    provider?: Provider; // default: "google"
    redirectTo?: string; // default: location.origin + location.pathname
    popupFeatures?: string; // default: "width=480,height=640,menubar=no,toolbar=no"
    timeoutMs?: number; // default: 120_000
    scopes?: string; // e.g. "email profile openid"
    queryParams?: Record<string, string>; // e.g. { access_type: "offline", prompt: "consent" }
    onSignedIn?: (session: Session) => void;
  },
): Promise<Session>;
```

**Behavior**

1. If a session already exists, it’s returned immediately.
2. Otherwise, the function requests an OAuth URL with `skipBrowserRedirect: true`.
3. It attempts to open a popup; if blocked (no user gesture), it falls back to a full-page redirect.
4. When the provider completes, Supabase stores the session and **broadcasts** `SIGNED_IN` to same-origin tabs; the opener resolves.
5. If broadcasts are delayed, the optional `postMessage` backup (above) resolves it quickly.

## Popup blocking & timeouts

- **Popup blockers**: Always call from a **click** or other user gesture. If blocked, this library automatically **redirects** the current page.
- **Timeout**: Defaults to **120s** to avoid zombie popups/listeners. If your users routinely need longer (e.g., 2FA), raise `timeoutMs`.

## Security & RLS

Client queries should rely on **Row Level Security**:

```sql
alter table demos enable row level security;

create policy "read own rows"
on demos for select
using (user_id = auth.uid());
```

## Development

```bash
git clone https://github.com/sanand0/supabase-oauth-popup.git
cd supabase-oauth-popup

npm install
npm run lint && npm run build && npm test
```

To publish:

```bash
npm login
npm publish --access public
```

## Release notes

- [1.0.0](https://npmjs.com/package/supabase-oauth-popup/v/1.0.0): 14 Oct 2025. Initial release

## License

[MIT](LICENSE)
