alepha
Version:
Easy-to-use modern TypeScript framework for building many kind of applications.
49 lines (46 loc) • 2 kB
text/typescript
import { type Static, z } from "alepha";
import { $entity, db } from "alepha/orm";
/**
* A registered OAuth 2.1 client application.
*
* Rows are created by Dynamic Client Registration (RFC 7591) when an MCP
* client (e.g. Claude) first connects. `source` records who created the
* client; for DCR it is always `"dcr"` and `createdByUserId` is null until
* a user completes an authorization.
*/
export const oauthClientEntity = $entity({
name: "oauth_clients",
schema: z.object({
id: db.primaryKey(z.uuid()),
createdAt: db.createdAt(),
updatedAt: db.updatedAt(),
clientId: z.text({ maxLength: 64 }),
clientName: z.text({ maxLength: 200 }),
redirectUris: db.default(z.array(z.text({ maxLength: 2048 })), []),
scopes: db.default(z.array(z.text({ maxLength: 64 })), []),
realm: z.text({ maxLength: 100 }),
/**
* OAuth client type. `confidential` clients authenticate at the token
* endpoint with a secret; `public` clients rely on PKCE only.
*/
type: db.default(z.enum(["public", "confidential"]), "public"),
/**
* First-party client: skip the consent screen. Consent exists to protect a
* user from THIRD-party apps requesting their data; a trusted client is the
* authorization server's own product (same vendor), so an authenticated
* user is sent straight back with a code — no "App X wants to connect" page.
*/
trusted: db.default(z.boolean(), false),
/**
* Scrypt hash of the client secret (confidential clients only). Null for
* public clients. Verified via `OAuthClientService.verifySecret`.
*/
clientSecretHash: z.text({ maxLength: 256 }).optional(),
source: db.default(z.text({ maxLength: 16 }), "dcr"),
createdByUserId: z.uuid().optional(),
lastUsedAt: z.datetime().optional(),
revokedAt: z.datetime().optional(),
}),
indexes: [{ columns: ["clientId"], unique: true }],
});
export type OAuthClientEntity = Static<typeof oauthClientEntity.schema>;