# @jonasneves/pip-auth

Opt-in `/auth` slash command and OAuth providers for [`@jonasneves/pip`](../pip-core). Ships GitHub today; designed to take more providers as they're needed.

## Install

```sh
npm install @jonasneves/pip-auth
```

Or pin via jsdelivr in a no-build site:

```html
<script type="module">
import { createPip }                       from 'https://cdn.jsdelivr.net/npm/@jonasneves/pip@2.9.1/pip-core.esm.js';
import { attach, githubProvider }          from 'https://cdn.jsdelivr.net/npm/@jonasneves/pip-auth@0.3.0/pip-auth.esm.js';

const pip = createPip({ /* … */ });
attach(pip, {
  providers: [
    githubProvider({
      clientId: 'Ov23li…',                    // your GitHub OAuth App client ID
      callbackOrigin: 'https://auth.neevs.io',// origin that postMessages the result back
      scope: 'read:user',                     // base scope, always requested
      optionalScopes: {                       // user opts in at /auth time
        gist: 'Manage gists',
        repo: 'View your repositories',
      },
    }),
  ],
});
</script>
```

## What you get

- A `/auth` slash command. With one provider it goes straight to sign-in. With more, `pip.askInChat` shows a button per provider.
- **Optional-scope picker.** When the provider declares `optionalScopes`, /auth shows the powerset of those scopes as buttons (e.g. with two optionals: "Continue (just identity)", "+ A", "+ B", "+ A + B", "Cancel"). User picks the bundle they want; OAuth popup shows the combined scopes. Supports up to ~3 optionals before button count gets unwieldy.
- Signed-in state persisted in `localStorage` (configurable via `storageKey`). Token is verified against `api.github.com/user` on read so a stale token clears itself.
- A returned API for site-side reads:
  ```js
  const auth = attach(pip, { providers: [...] });
  await auth.getStatus();           // null or { provider, username, avatarUrl }
  auth.onChange((status) => { /* ... */ });
  await auth.login(name, extras);   // programmatic sign-in (extras: scope strings)
  await auth.logout();
  ```

## Bring your own callback origin

`githubProvider` opens a popup to `github.com/login/oauth/authorize` with `redirect_uri` set to your `callbackOrigin`. That origin must serve a page that, after exchanging the code for a token, `postMessage`s back:

```js
window.opener.postMessage({ type: 'gh-auth', auth: { token, login, avatar_url } }, opener.origin);
```

A `localStorage.gh-auth-result` write of the same payload is the Safari fallback (Safari cross-origin nullifies `window.opener`).

The default `callbackOrigin` is `https://auth.neevs.io` which already does this. Replace it with your own to avoid depending on it.

## Custom providers

A provider is `{ name, icon?, getStatus(), login(), logout() }`. Anything you can implement in 60 lines plugs in. See `githubProvider` in `pip-auth.esm.js` for the shape.

## Layering

This package depends on `@jonasneves/pip`'s exposed surface (`registerSlash`, `askInChat`, `startTurn`, `setReplyText`) and nothing else from core. pip-core stays unaware of authentication; this package owns the OAuth knowledge. Removing pip-auth at any point leaves pip-core untouched.

## License

MIT.
