mirror of
https://github.com/better-auth/better-auth.git
synced 2026-10-19 11:24:57 -05:00
docs(device-authorization): validate demo clients and use Map lookups
The demo accepts only registered device client IDs, so the approval page can show their registered name. Both examples look names up in a Map, so a client ID such as __proto__ falls back to the unrecognized label.
This commit is contained in:
@@ -8,14 +8,7 @@ import { Button } from "@/components/ui/button";
|
||||
import { Card } from "@/components/ui/card";
|
||||
import { useSessionQuery } from "@/data/user/session-query";
|
||||
import { authClient } from "@/lib/auth-client";
|
||||
|
||||
/**
|
||||
* Client IDs this app registered, mapped to names users recognize. A device
|
||||
* chooses its own `client_id`, so never present an unknown ID as a trusted name.
|
||||
*/
|
||||
const KNOWN_CLIENTS: Record<string, string> = {
|
||||
"demo-cli": "Better Auth demo CLI",
|
||||
};
|
||||
import { deviceClients } from "@/lib/device-clients";
|
||||
|
||||
export default function Page() {
|
||||
const router = useRouter();
|
||||
@@ -113,7 +106,8 @@ export default function Page() {
|
||||
{request ? (
|
||||
<>
|
||||
<p>
|
||||
{KNOWN_CLIENTS[request.client_id] ?? "Unrecognized client"}
|
||||
{deviceClients.get(request.client_id) ??
|
||||
"Unrecognized client"}
|
||||
</p>
|
||||
<p className="font-mono text-xs text-muted-foreground">
|
||||
ID sent by the device: {request.client_id}
|
||||
|
||||
@@ -28,6 +28,7 @@ import {
|
||||
import { MysqlDialect } from "kysely";
|
||||
import { createPool } from "mysql2/promise";
|
||||
import { Stripe } from "stripe";
|
||||
import { deviceClients } from "./device-clients.ts";
|
||||
import {
|
||||
createSCIMDemoPlugin,
|
||||
isSCIMDemoEmployeePortalEnabled,
|
||||
@@ -397,6 +398,7 @@ const authOptions = {
|
||||
}),
|
||||
...scimDemoPlugins,
|
||||
deviceAuthorization({
|
||||
validateClient: (clientId) => deviceClients.has(clientId),
|
||||
expiresIn: "3min",
|
||||
interval: "5s",
|
||||
}),
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* Device clients this app registered, mapped to names users recognize.
|
||||
* The server accepts only these IDs, so the approval page can trust the name.
|
||||
*/
|
||||
export const deviceClients = new Map<string, string>([
|
||||
["demo-cli", "Better Auth demo CLI"],
|
||||
]);
|
||||
@@ -457,15 +457,14 @@ Users must be authenticated to approve or deny device authorization requests:
|
||||
|
||||
#### Example Approval Page
|
||||
|
||||
`GET /device` returns `client_id` and `scope` only to the signed-in user who claimed the code. `client_id` is the identifier the device sent, so show a name from your own client registry rather than the raw value. Keep Approve disabled until the request details load.
|
||||
`GET /device` returns `client_id` and `scope` only to the signed-in user who claimed the code. `client_id` is the identifier the device sent, so show a name from your own client registry rather than the raw value, and accept only registered IDs in `validateClient`. Keep Approve disabled until the request details load.
|
||||
|
||||
Avoid approval pages that show only the user code, or that enable Approve before the client and scopes appear. On such a page the user cannot tell a request from their own device apart from one started by someone else, for example a code sent to them in a message. The risk is higher when `validateClient` is not configured, because anyone can then start a request.
|
||||
|
||||
```tsx title="app/device/approve/page.tsx"
|
||||
// Client IDs you registered, mapped to names your users recognize.
|
||||
const knownClients: Record<string, string> = {
|
||||
"acme-cli": "Acme CLI",
|
||||
};
|
||||
// Accept only these IDs in `validateClient` so the name can be trusted.
|
||||
const knownClients = new Map([["acme-cli", "Acme CLI"]]);
|
||||
|
||||
export default function DeviceApprovalPage() {
|
||||
const { user } = useAuth(); // Must be authenticated
|
||||
@@ -532,7 +531,7 @@ export default function DeviceApprovalPage() {
|
||||
<h2>Device Authorization Request</h2>
|
||||
<p>
|
||||
Client: {request?.client_id
|
||||
? knownClients[request.client_id] ?? `Unrecognized client (${request.client_id})`
|
||||
? knownClients.get(request.client_id) ?? `Unrecognized client (${request.client_id})`
|
||||
: "Loading..."}
|
||||
</p>
|
||||
<p>Scopes: {request?.scope || "None"}</p>
|
||||
|
||||
Reference in New Issue
Block a user