> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dr.green/llms.txt
> Use this file to discover all available pages before exploring further.

# Auth

# Auth (Holder Sign-In)

> **Endpoint reference: ⚠️ Mostly informational for store builders.**
> The auth endpoints below are how *holders* authenticate to the Dr Green DAPP UI to generate API keys. As a store builder, you don't call these directly — you receive the resulting `apiKey` + `secretKey` pair from the holder. This page exists so you understand where the keys come from, and so you can build a holder-onboarding flow if you offer one in your store.

***

## The flow at a glance

```
[Holder's browser]
    │
    │ 1. POST /api/v1/auth/nonce  →  receives a nonce
    │ 2. Browser prompts wallet to sign:
    │       message = "Welcome to Dr Green!\n\nNonce: <nonce>"
    │       (SIWE-style — the exact message format is set by the DAPP UI)
    │ 3. POST /api/v1/auth/dapp/signIn { walletAddress, signature, nonce }
    │       →  receives a JWT
    │ 4. JWT is now used for /dapp/* (UI flows) and /keys (key management)
    │ 5. POST /keys (JWT auth)
    │       →  receives { apiKey, secretKey } pair
    │ 6. Holder hands the pair to a store developer (you)
    │
[Store backend]
    │
    │ 7. From here onwards, store uses { apiKey, secretKey } on every /dapp/* call
    │    (no JWT needed)
```

***

## Endpoints

| Method | Path                | Auth | Description                                     |
| ------ | ------------------- | ---- | ----------------------------------------------- |
| `POST` | `/auth/nonce`       | None | Generate a nonce for wallet signing             |
| `POST` | `/auth/dapp/signIn` | None | Exchange a wallet signature for a JWT           |
| `POST` | `/auth/signIn`      | None | Admin / staff login (NOT for holders or stores) |
| `GET`  | `/auth/verifyEmail` | None | Email verification callback (out of band)       |
| `GET`  | `/auth/resendEmail` | None | Re-send verification email                      |

> ⚠️ **`POST /auth/signIn` is for Dr Green admin/staff accounts** (`SUPERADMIN`, `ADMIN`, `SUBADMIN`, `MANAGER` roles). Holders (`USER` role) use `/auth/dapp/signIn` instead. Don't confuse the two.

***

## `POST /auth/nonce` — get a nonce

Returns a single-use nonce string that must be included in the message the holder's wallet signs.

### Request body

```json theme={null}
{
  "walletAddress": "0x553374bd6e81f952821e37f4b8e208a81164fb2e"
}
```

| Field           | Type   | Required | Notes                       |
| --------------- | ------ | -------- | --------------------------- |
| `walletAddress` | string | ✅        | Lowercased Ethereum address |

### No request signing required

This is an **unauthenticated** endpoint. No `x-auth-apikey` or `x-auth-signature` headers needed.

### Response

`201 Created` with the nonce.

🔒 Exact response shape not yet captured live — likely `{ data: { nonce: "abc123..." } }` based on the rest of the API's envelope conventions.

### Nonce semantics

* **Single-use.** Once consumed by `/auth/dapp/signIn`, the same nonce can't be reused.
* **Time-bound.** Likely expires after a short window (5–15 minutes is typical). Confirm with backend if precise.
* **Per-wallet.** Issued for one specific `walletAddress` and only valid for that address's signature.

***

## `POST /auth/dapp/signIn` — wallet sign-in

Exchange a wallet-signed nonce for a JWT.

### Request body

Per `SignInDto`:

```json theme={null}
{
  "walletAddress": "0x553374bd6e81f952821e37f4b8e208a81164fb2e",
  "nonce": "<from /auth/nonce>",
  "signature": "0x...<wallet signature of the message>"
}
```

| Field           | Type   | Required | Notes                                                                   |
| --------------- | ------ | -------- | ----------------------------------------------------------------------- |
| `walletAddress` | string | ✅        | Same as the nonce was issued for                                        |
| `nonce`         | string | ✅        | The exact value returned by `/auth/nonce`                               |
| `signature`     | string | ✅        | The hex-encoded ECDSA signature from the wallet (`personal_sign` style) |

### Response

`201 Created` with a JWT.

🔒 Exact response shape pending live capture. Conventional shape:

```json theme={null}
{
  "success": true,
  "statusCode": 201,
  "message": "Success",
  "data": {
    "token": "eyJhbGciOiJIUzI1NiIs...",
    "user": {
      "id": "...",
      "walletAddress": "0x...",
      "role": "USER",
      "primaryNft": { "tokenId": 56 }
    }
  }
}
```

### What the holder signs

The exact message the wallet signs is determined by the DAPP UI. Based on backend code patterns and SIWE conventions, the message is approximately:

```
Welcome to Dr Green!

Sign this message to authenticate.

Nonce: <nonce-from-/auth/nonce>
```

> 🔒 **The exact text** including line breaks and punctuation is not formally documented and must match byte-for-byte what the backend reconstructs. If you build a holder-facing wallet-signin flow in your store, **don't reverse-engineer the message format** — instead, either:
>
> 1. Direct the holder to the DAPP UI to authenticate and generate keys (recommended), or
> 2. Coordinate with Dr Green to expose the canonical message format for re-implementation

For most store integrations, option 1 is the right call — let Dr Green handle wallet auth, and consume the resulting keys.

***

## When a store builder might care

Most stores never call these endpoints. Two situations where you would:

### Situation 1: White-label holder onboarding

If your store offers "log in with your Dr Green wallet" as a holder-facing flow (e.g. for a self-serve store provisioning UI), you'd implement the full nonce → sign → JWT flow client-side. This is unusual; most stores use Dr Green's DAPP UI for this.

### Situation 2: Staff dashboard with multiple holders

If you operate stores for multiple holders and want a unified ops dashboard, the staff log into your backend, but the *holder data* is fetched per-holder using their stored API keys. You don't authenticate as the holder — you have their long-lived API key pair.

***

## Token lifetime and refresh

🔒 JWT lifetime and refresh-token mechanics are **not yet captured live**. Standard NestJS Passport JWT defaults are typically 1h–24h, with no refresh by default. If your store needs to maintain a long-lived JWT session (rather than just using API keys), confirm the lifetime and refresh policy with the Dr Green backend team.

For store-to-API integrations, **don't bother with JWTs** — use the API-key + signature auth on every request. It's simpler and doesn't expire.

***

## Email verification (`/auth/verifyEmail`, `/auth/resendEmail`)

These endpoints power the email-verification flow for staff/admin sign-ups (not holders, who authenticate via wallet). Not relevant to store builders. They're listed here only for completeness against the live OpenAPI spec.

***

## Common patterns

### "Are you sure I shouldn't just use the JWT in my store?"

No. JWTs:

* Expire (you'd need to handle refresh)
* Are bearer tokens (anyone with the JWT is the user)
* Aren't designed for long-lived service-to-service auth

API keys with per-request signatures are the right primitive for a store backend. They:

* Don't expire
* Each request is signed individually (compromise of one signature doesn't compromise the key — though compromise of the secret key does, of course)
* Map cleanly to per-NFT scoping

### "Can my store sign in on the holder's behalf to call JWT-only endpoints like `/dapp/users/nfts`?"

Technically yes (if you have the holder's wallet private key, which you should NOT have), but no in practice. JWT-only endpoints (`/user/me`, `/dapp/users/nfts`) are not part of the store-builder integration surface. Ask the holder to use the DAPP UI for those operations. See [NFTs](./nfts.md) for the implications.

***

## See also

* [Authentication](../02-authentication.md) — request signing for `/dapp/*` endpoints
* [Keys](./keys.md) — managing API key pairs after sign-in
* [NFTs](./nfts.md) — JWT-only NFT endpoints
