first commit
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
# schemas/routes/forgot-password
|
||||
|
||||
## Purpose
|
||||
|
||||
Schemas for `POST /auth/forgot-password`. The body schema accepts a
|
||||
single email and the response is `{ sent: true }`. The response shape
|
||||
is deliberately uninformative — leaking whether the email exists in the
|
||||
user table is a known enumeration vector, so the route returns the same
|
||||
"sent" token whether or not it actually emitted an email.
|
||||
|
||||
## Features
|
||||
|
||||
- `ForgotPasswordBodySchema` — `{ email: string (RFC-5322) }`.
|
||||
- `ForgotPasswordResponseSchema` — `{ sent: literal(true) }`.
|
||||
- Inferred companions `ForgotPasswordBody` and `ForgotPasswordResponse`.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { ForgotPasswordBodySchema } from "@suaveplan/api-plugin-auth/schemas/routes";
|
||||
|
||||
const body = ForgotPasswordBodySchema.parse(await c.req.json());
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
Combine with a rate-limit middleware so the route can't be used to
|
||||
probe email addresses by timing the response. The schema keeps the
|
||||
response shape opaque even when the request is rate-limited:
|
||||
|
||||
```ts
|
||||
import { POST, successResponse } from "@suaveplan/api-openapi";
|
||||
import {
|
||||
ForgotPasswordBodySchema,
|
||||
ForgotPasswordResponseSchema,
|
||||
} from "@suaveplan/api-plugin-auth/schemas/routes";
|
||||
|
||||
const forgotRoute = POST("/auth/forgot-password")
|
||||
.tags("auth")
|
||||
.summary("Send a password-reset email")
|
||||
.body(ForgotPasswordBodySchema)
|
||||
.response(successResponse(ForgotPasswordResponseSchema))
|
||||
.build();
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `ForgotPasswordBodySchema`
|
||||
|
||||
Required: `email: string (email)`. Inferred type `ForgotPasswordBody`.
|
||||
|
||||
### `ForgotPasswordResponseSchema`
|
||||
|
||||
`{ sent: true }`. Inferred type `ForgotPasswordResponse`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The schema rejects malformed emails at the boundary so Better-Auth's
|
||||
email delivery layer never has to handle garbage input.
|
||||
- The response always returns `sent: true` regardless of whether the
|
||||
email exists in the user table — the canonical pattern for avoiding
|
||||
account enumeration through forgot-password endpoints.
|
||||
- The schema does not include locale or template selection; those are
|
||||
Better-Auth configuration concerns, not request-body concerns.
|
||||
- Operators that want different responses for known vs unknown emails
|
||||
(e.g. throttling differently) should configure rate limiting at the
|
||||
network edge rather than altering the schema — the constant-shape
|
||||
response is the privacy guarantee the schema exists to enforce.
|
||||
- The route mounts under the configured `routePrefix`, so the fully
|
||||
qualified path is typically `/auth/forgot-password` when the
|
||||
default prefix is used.
|
||||
106
docs/api-stack/api/api-plugin-auth/src/schemas/routes/oauth.md
Normal file
106
docs/api-stack/api/api-plugin-auth/src/schemas/routes/oauth.md
Normal file
@@ -0,0 +1,106 @@
|
||||
# schemas/routes/oauth
|
||||
|
||||
## Purpose
|
||||
|
||||
Schemas for the OAuth pair `GET /auth/oauth/:provider/start` and
|
||||
`GET /auth/oauth/:provider/callback`. The start endpoint initiates an
|
||||
OAuth flow (typically redirecting to the provider's authorization
|
||||
endpoint); the callback receives the provider's `code` + `state` and
|
||||
finishes the flow.
|
||||
|
||||
## Features
|
||||
|
||||
- `OAuthStartParamsSchema` — `{ provider: string (min 1) }` for the
|
||||
path parameter.
|
||||
- `OAuthStartQuerySchema` — `{ redirectTo?: string (url) }` so callers
|
||||
can request a specific post-login redirect.
|
||||
- `OAuthStartResponseSchema` — `{ redirectTo: string (url) }`; the
|
||||
body shape used when the response is a 200 + JSON pointer rather than
|
||||
a direct 302.
|
||||
- `OAuthCallbackParamsSchema` + `OAuthCallbackQuerySchema` — `{ code,
|
||||
state }` from the provider.
|
||||
- `OAuthCallbackResponseSchema` — `{ redirectTo: string (url) }` for
|
||||
the same 200 + JSON pointer variant.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { OAuthStartQuerySchema, OAuthCallbackQuerySchema } from "@suaveplan/api-plugin-auth/schemas/routes";
|
||||
|
||||
const startQuery = OAuthStartQuerySchema.parse({ redirectTo: "https://example.test/" });
|
||||
const callbackQuery = OAuthCallbackQuerySchema.parse({ code: "abc", state: "xyz" });
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
Use the params + query + response shapes inside a `RouteBuilder` chain
|
||||
so the OpenAPI doc advertises a fully-typed OAuth flow including the
|
||||
required `state` field on the callback:
|
||||
|
||||
```ts
|
||||
import { GET, successResponse } from "@suaveplan/api-openapi";
|
||||
import {
|
||||
OAuthCallbackParamsSchema,
|
||||
OAuthCallbackQuerySchema,
|
||||
OAuthCallbackResponseSchema,
|
||||
OAuthStartParamsSchema,
|
||||
OAuthStartQuerySchema,
|
||||
OAuthStartResponseSchema,
|
||||
} from "@suaveplan/api-plugin-auth/schemas/routes";
|
||||
|
||||
const startRoute = GET("/auth/oauth/{provider}/start")
|
||||
.tags("auth", "oauth")
|
||||
.summary("Begin OAuth flow with the named provider")
|
||||
.params(OAuthStartParamsSchema)
|
||||
.query(OAuthStartQuerySchema)
|
||||
.response(successResponse(OAuthStartResponseSchema))
|
||||
.build();
|
||||
|
||||
const callbackRoute = GET("/auth/oauth/{provider}/callback")
|
||||
.tags("auth", "oauth")
|
||||
.summary("OAuth callback — exchange code for session")
|
||||
.params(OAuthCallbackParamsSchema)
|
||||
.query(OAuthCallbackQuerySchema)
|
||||
.response(successResponse(OAuthCallbackResponseSchema))
|
||||
.build();
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `OAuthStartParamsSchema`
|
||||
|
||||
`{ provider: string (min 1) }`. Inferred type `OAuthStartParams`.
|
||||
|
||||
### `OAuthStartQuerySchema`
|
||||
|
||||
`{ redirectTo?: string (url) }`. Inferred type `OAuthStartQuery`.
|
||||
|
||||
### `OAuthStartResponseSchema`
|
||||
|
||||
`{ redirectTo: string (url) }`.
|
||||
|
||||
### `OAuthCallbackParamsSchema`
|
||||
|
||||
`{ provider: string (min 1) }`. Inferred type `OAuthCallbackParams`.
|
||||
|
||||
### `OAuthCallbackQuerySchema`
|
||||
|
||||
`{ code: string (min 1); state: string (min 1) }`. Inferred type
|
||||
`OAuthCallbackQuery`.
|
||||
|
||||
### `OAuthCallbackResponseSchema`
|
||||
|
||||
`{ redirectTo: string (url) }`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The OAuth pair is documented as 200-with-JSON-redirect in OpenAPI for
|
||||
ergonomics — Better-Auth's runtime handler may return a 302 directly,
|
||||
which is compatible with the OpenAPI doc because consumers reading
|
||||
the spec see the "redirectTo" pointer either way.
|
||||
- The callback's `state` field is required (Better-Auth always emits
|
||||
it) so CSRF protection is enforced at the schema layer; callers that
|
||||
drop `state` fail Zod validation before Better-Auth ever runs.
|
||||
- Provider validation is structural — the route accepts any provider
|
||||
string; Better-Auth's configured providers map decides whether the
|
||||
call succeeds at runtime.
|
||||
@@ -0,0 +1,77 @@
|
||||
# schemas/routes/reset-password
|
||||
|
||||
## Purpose
|
||||
|
||||
Schemas for `POST /auth/reset-password`. The body accepts the
|
||||
single-use token previously emailed to the user plus the new password;
|
||||
the response is `{ success: true }`. Better-Auth's downstream handler
|
||||
validates the token, hashes the new password, and clears the
|
||||
verification row.
|
||||
|
||||
## Features
|
||||
|
||||
- `ResetPasswordBodySchema` — `{ token: string (min 1), password:
|
||||
string (min 8) }`.
|
||||
- `ResetPasswordResponseSchema` — `{ success: literal(true) }`.
|
||||
- Inferred companions `ResetPasswordBody` and `ResetPasswordResponse`.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { ResetPasswordBodySchema } from "@suaveplan/api-plugin-auth/schemas/routes";
|
||||
|
||||
const body = ResetPasswordBodySchema.parse(await c.req.json());
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
Combine the schema with the standard error response so the OpenAPI
|
||||
document advertises the 400 (invalid token) and 422 (password too weak)
|
||||
cases consumers need to handle:
|
||||
|
||||
```ts
|
||||
import { POST, successResponse } from "@suaveplan/api-openapi";
|
||||
import {
|
||||
ResetPasswordBodySchema,
|
||||
ResetPasswordResponseSchema,
|
||||
} from "@suaveplan/api-plugin-auth/schemas/routes";
|
||||
|
||||
const resetRoute = POST("/auth/reset-password")
|
||||
.tags("auth")
|
||||
.summary("Apply a password reset using a single-use token")
|
||||
.body(ResetPasswordBodySchema)
|
||||
.response(successResponse(ResetPasswordResponseSchema))
|
||||
.build();
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `ResetPasswordBodySchema`
|
||||
|
||||
Required: `token: string (min 1)`, `password: string (min 8)`. Inferred
|
||||
type `ResetPasswordBody`.
|
||||
|
||||
### `ResetPasswordResponseSchema`
|
||||
|
||||
`{ success: true }`. Inferred type `ResetPasswordResponse`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The 8-character minimum on `password` matches Better-Auth's default
|
||||
password policy. Stricter policies are configured downstream; the
|
||||
schema is a floor, not a ceiling.
|
||||
- The token is opaque from the schema's perspective — its structure
|
||||
(HMAC, JWT, or random base64) is a Better-Auth implementation detail
|
||||
and the schema only ensures the field is non-empty.
|
||||
- The response is a literal `true` so the typed return narrows
|
||||
correctly on the client side.
|
||||
- Better-Auth's downstream handler enforces single-use semantics —
|
||||
the schema does not need to track whether the token was previously
|
||||
redeemed. Replaying a consumed token surfaces a 400 from
|
||||
Better-Auth, which the host's error-handler middleware translates
|
||||
into the standard error envelope.
|
||||
- Hashing the new password happens inside Better-Auth using its
|
||||
configured hashing strategy (bcrypt, argon2, scrypt). The schema
|
||||
only checks length; the policy layer (zxcvbn, breached-password
|
||||
checks) is configured upstream and runs before this route is
|
||||
invoked.
|
||||
@@ -0,0 +1,64 @@
|
||||
# schemas/routes/session
|
||||
|
||||
## Purpose
|
||||
|
||||
Response schema for `GET /auth/session`. Returns the current
|
||||
authenticated `Principal` and the matching Better-Auth session row, or
|
||||
`null` for both fields when the request is anonymous. The endpoint is
|
||||
the canonical "who am I" call for client SDKs and SSR hydration.
|
||||
|
||||
## Features
|
||||
|
||||
- `GetSessionResponseSchema` — `{ user: PrincipalSchema | null;
|
||||
session: SessionSchema | null }` so the response carries the exact
|
||||
same wire shape as `c.get("user")` plus the session row.
|
||||
- Anonymous callers see `{ user: null, session: null }` — a 200 with
|
||||
null fields rather than a 401, so client SDKs can probe identity
|
||||
without error handling.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { GetSessionResponseSchema } from "@suaveplan/api-plugin-auth/schemas/routes";
|
||||
|
||||
const parsed = GetSessionResponseSchema.parse({ user: null, session: null });
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
Pair the schema with a route definition that lists both anonymous and
|
||||
authenticated cases in the OpenAPI document so consumers know the
|
||||
endpoint never 401s:
|
||||
|
||||
```ts
|
||||
import { GET, successResponse } from "@suaveplan/api-openapi";
|
||||
import {
|
||||
GetSessionResponseSchema,
|
||||
} from "@suaveplan/api-plugin-auth/schemas/routes";
|
||||
|
||||
const sessionRoute = GET("/auth/session")
|
||||
.tags("auth")
|
||||
.summary("Resolve the current session, if any")
|
||||
.response(successResponse(GetSessionResponseSchema))
|
||||
.build();
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `GetSessionResponseSchema`
|
||||
|
||||
`{ user: PrincipalSchema | null; session: SessionSchema | null }`.
|
||||
Inferred type `GetSessionResponse`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- Returning `null` for the anonymous case keeps the endpoint cacheable
|
||||
per cookie — a 401 response would force the CDN to either skip caching
|
||||
or treat the page as private. Some host setups place this endpoint
|
||||
behind a Service-Worker cache.
|
||||
- The schema mirrors the runtime shape installed by the session
|
||||
middleware so consumers can use the same type to read either the HTTP
|
||||
response body or `c.get("user")` / `c.get("session")`.
|
||||
- Better-Auth's session row carries plugin-extended fields (org
|
||||
membership, impersonation) — those are tolerated via the loose schema
|
||||
shape on `SessionSchema` so the API is forward-compatible.
|
||||
@@ -0,0 +1,73 @@
|
||||
# schemas/routes/sign-in
|
||||
|
||||
## Purpose
|
||||
|
||||
Request/response Zod schemas for `POST /auth/sign-in`. The body schema
|
||||
validates email + password credential input; the response describes
|
||||
the user + session pair Better-Auth returns on a successful sign-in.
|
||||
These schemas form one half of the canonical credential lifecycle —
|
||||
sign-up and sign-in share the same response shape so client code can
|
||||
treat the two endpoints uniformly.
|
||||
|
||||
## Features
|
||||
|
||||
- `SignInBodySchema` — `email` (RFC-5322), `password` (min 1, since
|
||||
Better-Auth performs the policy check downstream and the schema only
|
||||
guards against empty strings here).
|
||||
- `SignInResponseSchema` — `{ user: PrincipalSchema; session:
|
||||
SessionSchema }` matching the wire shape Better-Auth produces.
|
||||
- Inferred companions `SignInBody` and `SignInResponse`.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { SignInBodySchema } from "@suaveplan/api-plugin-auth/schemas/routes";
|
||||
|
||||
const body = SignInBodySchema.parse(await c.req.json());
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
Combine the schema with the `requireAuth` middleware downstream so
|
||||
consumers can build a `/sign-in-then-call-private-route` flow with full
|
||||
type inference:
|
||||
|
||||
```ts
|
||||
import { POST, successResponse } from "@suaveplan/api-openapi";
|
||||
import {
|
||||
SignInBodySchema,
|
||||
SignInResponseSchema,
|
||||
} from "@suaveplan/api-plugin-auth/schemas/routes";
|
||||
|
||||
const signInRoute = POST("/auth/sign-in")
|
||||
.tags("auth")
|
||||
.summary("Email + password sign-in")
|
||||
.body(SignInBodySchema)
|
||||
.response(successResponse(SignInResponseSchema))
|
||||
.build();
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `SignInBodySchema`
|
||||
|
||||
Required: `email: string (email)`, `password: string (min 1)`. Inferred
|
||||
type `SignInBody`.
|
||||
|
||||
### `SignInResponseSchema`
|
||||
|
||||
`{ user: PrincipalSchema; session: SessionSchema }`. Inferred type
|
||||
`SignInResponse`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The `password` field only requires a non-empty string — Better-Auth's
|
||||
policy layer enforces strength rules with full error reporting.
|
||||
Duplicating policy at the schema layer would force the client SDK to
|
||||
re-implement Better-Auth's exact rules.
|
||||
- The response intentionally mirrors `SignUpResponseSchema` so consumer
|
||||
code can use one type for both the post-sign-up and post-sign-in
|
||||
redirect handlers — a deliberate ergonomics choice.
|
||||
- The schema does not include rate-limit headers or CSRF tokens; those
|
||||
are handled by `@suaveplan/api`'s middleware chain, not the route
|
||||
schema itself.
|
||||
@@ -0,0 +1,65 @@
|
||||
# schemas/routes/sign-out
|
||||
|
||||
## Purpose
|
||||
|
||||
Schemas for `POST /auth/sign-out`. The body is optional (Better-Auth
|
||||
identifies the session from the cookie or `Authorization` header) and
|
||||
the response is a single `{ success: true }` literal so client code
|
||||
can branch on success without parsing additional fields.
|
||||
|
||||
## Features
|
||||
|
||||
- `SignOutBodySchema` — optional empty object; accepts both `undefined`
|
||||
and `{}` so clients can send a body or skip it.
|
||||
- `SignOutResponseSchema` — `{ success: literal(true) }` so the
|
||||
response type is `{ success: true }` rather than `{ success: boolean }`.
|
||||
- Inferred companion `SignOutResponse`.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { SignOutResponseSchema } from "@suaveplan/api-plugin-auth/schemas/routes";
|
||||
|
||||
const parsed = SignOutResponseSchema.parse({ success: true });
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
Pair the schema with the `requireAuth` middleware so the OpenAPI
|
||||
document advertises a 401 response for anonymous callers and a 200
|
||||
response with the typed body for authenticated ones:
|
||||
|
||||
```ts
|
||||
import { POST, successResponse } from "@suaveplan/api-openapi";
|
||||
import {
|
||||
SignOutResponseSchema,
|
||||
} from "@suaveplan/api-plugin-auth/schemas/routes";
|
||||
|
||||
const signOutRoute = POST("/auth/sign-out")
|
||||
.tags("auth")
|
||||
.summary("End the current session")
|
||||
.response(successResponse(SignOutResponseSchema))
|
||||
.build();
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `SignOutBodySchema`
|
||||
|
||||
Optional empty object. Accepts `undefined` or `{}`.
|
||||
|
||||
### `SignOutResponseSchema`
|
||||
|
||||
`{ success: true }` literal. Inferred type `SignOutResponse`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The body schema is `optional` rather than absent so the OpenAPI
|
||||
document still advertises the route as accepting a JSON body. Clients
|
||||
may send `{}` to follow the route shape or omit the body entirely.
|
||||
- The response is a literal `true` rather than `boolean` so the typed
|
||||
return value at the client side narrows correctly — `if
|
||||
(response.success) { … }` is statically known to execute.
|
||||
- Better-Auth's downstream cookie clearing is a side effect of the
|
||||
handler, not part of the schema. The response only describes the JSON
|
||||
body so the schema stays orthogonal to the cookie machinery.
|
||||
@@ -0,0 +1,77 @@
|
||||
# schemas/routes/sign-up
|
||||
|
||||
## Purpose
|
||||
|
||||
Request/response Zod schemas for `POST /auth/sign-up`. The body schema
|
||||
constrains the email + password + optional name input; the response
|
||||
schema describes the typed user + session pair Better-Auth returns on
|
||||
a successful sign-up. Sibling plugins import these schemas directly so
|
||||
client SDK generators and downstream tests share one canonical wire
|
||||
shape.
|
||||
|
||||
## Features
|
||||
|
||||
- `SignUpBodySchema` — `email` (RFC-5322), `password` (min 8 chars),
|
||||
optional `name` (min 1 char).
|
||||
- `SignUpResponseSchema` — Better-Auth user + session payload typed
|
||||
via `PrincipalSchema` and `SessionSchema` from `../principal.ts` so
|
||||
the response carries the same wire-level user identity that
|
||||
`c.get("user")` produces on every other route.
|
||||
- Inferred TypeScript companions `SignUpBody` and `SignUpResponse`.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { SignUpBodySchema } from "@suaveplan/api-plugin-auth/schemas/routes";
|
||||
|
||||
const body = SignUpBodySchema.parse(await c.req.json());
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
Use the schemas inside a `RouteBuilder` chain so the OpenAPI document
|
||||
carries both the request body and the success response shapes:
|
||||
|
||||
```ts
|
||||
import { POST, successResponse } from "@suaveplan/api-openapi";
|
||||
import {
|
||||
SignUpBodySchema,
|
||||
SignUpResponseSchema,
|
||||
} from "@suaveplan/api-plugin-auth/schemas/routes";
|
||||
|
||||
const signUpRoute = POST("/auth/sign-up")
|
||||
.tags("auth")
|
||||
.summary("Email + password sign-up")
|
||||
.body(SignUpBodySchema)
|
||||
.response(successResponse(SignUpResponseSchema))
|
||||
.build();
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `SignUpBodySchema`
|
||||
|
||||
Required: `email: string (email)`, `password: string (min 8)`.
|
||||
Optional: `name: string (min 1)`.
|
||||
|
||||
### `SignUpResponseSchema`
|
||||
|
||||
`{ user: PrincipalSchema; session: SessionSchema }`. Inferred type
|
||||
`SignUpResponse`.
|
||||
|
||||
### `SignUpBody`
|
||||
|
||||
`z.infer<typeof SignUpBodySchema>`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The 8-character minimum on `password` matches Better-Auth's default
|
||||
password policy; consumers can layer stricter policies via Better-Auth
|
||||
configuration without altering the schema.
|
||||
- The response shape uses the shared `PrincipalSchema` / `SessionSchema`
|
||||
rather than a route-local one — this is how the contract that
|
||||
`c.get("user")` returns the same `Principal` everywhere stays a
|
||||
single-source-of-truth thing.
|
||||
- Sensitive fields (`password`) are intentionally excluded from the
|
||||
response schema so the OpenAPI document never advertises a password
|
||||
surface to consumers.
|
||||
@@ -0,0 +1,72 @@
|
||||
# schemas/routes/verify-email
|
||||
|
||||
## Purpose
|
||||
|
||||
Schemas for `POST /auth/verify-email`. The body carries the
|
||||
single-use verification token (typically emailed from a sign-up
|
||||
event); the response is `{ verified: true }`. The route is idempotent
|
||||
in Better-Auth — submitting an already-verified token returns `verified:
|
||||
true` rather than failing.
|
||||
|
||||
## Features
|
||||
|
||||
- `VerifyEmailBodySchema` — `{ token: string (min 1) }`.
|
||||
- `VerifyEmailResponseSchema` — `{ verified: literal(true) }`.
|
||||
- Inferred companions `VerifyEmailBody` and `VerifyEmailResponse`.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { VerifyEmailBodySchema } from "@suaveplan/api-plugin-auth/schemas/routes";
|
||||
|
||||
const body = VerifyEmailBodySchema.parse(await c.req.json());
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
Wire the route into the host so the OpenAPI document advertises the 400
|
||||
case (invalid / expired token) and the 200 case (verified). The schema
|
||||
covers the 200 case; standard error responses come from
|
||||
`StandardResponses`:
|
||||
|
||||
```ts
|
||||
import { POST, successResponse } from "@suaveplan/api-openapi";
|
||||
import {
|
||||
VerifyEmailBodySchema,
|
||||
VerifyEmailResponseSchema,
|
||||
} from "@suaveplan/api-plugin-auth/schemas/routes";
|
||||
|
||||
const verifyRoute = POST("/auth/verify-email")
|
||||
.tags("auth")
|
||||
.summary("Verify an email address using a single-use token")
|
||||
.body(VerifyEmailBodySchema)
|
||||
.response(successResponse(VerifyEmailResponseSchema))
|
||||
.build();
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `VerifyEmailBodySchema`
|
||||
|
||||
Required: `token: string (min 1)`. Inferred type `VerifyEmailBody`.
|
||||
|
||||
### `VerifyEmailResponseSchema`
|
||||
|
||||
`{ verified: true }`. Inferred type `VerifyEmailResponse`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The token is opaque to the schema — Better-Auth's downstream handler
|
||||
validates structure, expiry, and single-use semantics.
|
||||
- The response is the literal `true` so client code can branch on
|
||||
success without a wider type comparison.
|
||||
- Repeated successful verification calls return the same `verified:
|
||||
true` shape — the schema is intentionally idempotent so client retry
|
||||
loops do not need a special-case "already verified" code.
|
||||
- Token expiry is handled by Better-Auth's verification flow — the
|
||||
schema does not impose a TTL; expired tokens return a 400 from the
|
||||
Better-Auth handler, which the host's error-handler middleware
|
||||
translates to the standard error envelope.
|
||||
- The route is mounted under the configured `routePrefix` so the
|
||||
fully-qualified path is typically `/auth/verify-email` (or the
|
||||
caller's custom prefix when wiring the plugin).
|
||||
Reference in New Issue
Block a user