---
title: Sign in with sherlock (redirect)
description: The full OAuth 2.1 / OIDC flow for letting another app authenticate against a shebang account.
---

# Sign in with sherlock (redirect)

Send a user to sherlock to sign in, and get them back with tokens — no
password form lives in your app, and a user already signed in to one
shebang-backed app is signed in to every other app on the same sherlock
account.

## 1. Register a client

Dynamic client registration, against the discovery document at
`https://auth.shebang.pro/auth/v1/.well-known/openid-configuration`:

```bash
curl -X POST https://auth.shebang.pro/auth/v1/oauth/clients/register \
  -H "Content-Type: application/json" \
  -d '{
    "client_name": "My app",
    "redirect_uris": ["https://myapp.example.com/callback"],
    "token_endpoint_auth_method": "none"
  }'
```

`token_endpoint_auth_method: "none"` registers a **public** client (a
browser app or CLI, with no secret to protect). Use
`"client_secret_post"` instead for a **confidential** client — a server
that can hold one — and the response includes a `client_secret`.

## 2. Send the user to the authorize URL

With a PKCE challenge (`S256` only — plain isn't supported) and a
`state` value you'll check on the way back:

```
https://auth.shebang.pro/auth/v1/oauth/authorize
  ?response_type=code
  &client_id=<client_id>
  &redirect_uri=<redirect_uri>
  &scope=openid email profile
  &code_challenge=<S256 challenge>
  &code_challenge_method=S256
  &state=<random state>
```

The consent screen the user sees names your app and lists these scopes.

## 3. Handle the callback

At your `redirect_uri` (`?code=...&state=...`), check `state` matches
what you sent, then exchange the code for tokens:

```bash
curl -X POST https://auth.shebang.pro/auth/v1/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  --data-urlencode "grant_type=authorization_code" \
  --data-urlencode "code=<code>" \
  --data-urlencode "redirect_uri=<redirect_uri>" \
  --data-urlencode "client_id=<client_id>" \
  --data-urlencode "code_verifier=<verifier>"
```

You get back `access_token`, `refresh_token`, and `id_token` (a JWT
carrying the user's `email` and `sub`). The `access_token` is a sherlock
user JWT (`aud: "authenticated"`) — pass it as a Bearer token to your
database's Data API, or to `api.shebang.pro`, and either accepts it as
that signed-in user.

## 4. Use the token with supabase-js

So row-level security sees the signed-in user:

```js
const supabase = createClient(api_url, publishable_key, {
  global: { headers: { Authorization: `Bearer ${access_token}` } },
});
```

## 5. Refresh

```bash
curl -X POST https://auth.shebang.pro/auth/v1/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  --data-urlencode "grant_type=refresh_token" \
  --data-urlencode "refresh_token=<refresh_token>" \
  --data-urlencode "client_id=<client_id>"
```

## 6. Log out

Drop the tokens client-side — sherlock sessions end with sign-out-
everywhere, not a per-app revoke.

For a complete, runnable version of this flow, see
[the sign-in-with-sherlock guide](/guides/sign-in-with-sherlock).

## Next

[Account management](/sherlock/account-management).
