Skip to content

Commit 76b4530

Browse files
antfuclaude
andcommitted
feat(self-inspect): add auth tokens management tab
- Unify wording: clientAuthPasswords → clientAuthTokens, "password" → "token" in UI - Add revokeAuthToken() utility that removes token from storage and notifies all connected clients using that token via auth:revoked broadcast event - One token can be used by multiple clients; revoking disconnects all of them - Client-side handler in rpc-ws.ts listens for auth:revoked and shows auth notice - Self-inspect revoke RPC now uses the shared revokeAuthToken utility Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 7308af3 commit 76b4530

File tree

12 files changed

+75
-26
lines changed

12 files changed

+75
-26
lines changed

packages/core/src/client/webcomponents/components/views-builtin/ViewBuiltinClientAuthNotice.vue

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ defineProps<{
77
context: DocksContext
88
}>()
99
10-
const passwordInput = ref('')
10+
const tokenInput = ref('')
1111
12-
function submitPassword() {
13-
const value = passwordInput.value.trim()
12+
function submitToken() {
13+
const value = tokenInput.value.trim()
1414
if (!value)
1515
return
1616
localStorage.setItem('__VITE_DEVTOOLS_CONNECTION_AUTH_ID__', value)
@@ -37,17 +37,17 @@ function submitPassword() {
3737
<div class="mt6 op50">
3838
or
3939
</div>
40-
<form class="mt2 flex items-center gap-2" @submit.prevent="submitPassword">
40+
<form class="mt2 flex items-center gap-2" @submit.prevent="submitToken">
4141
<input
42-
v-model="passwordInput"
42+
v-model="tokenInput"
4343
type="text"
44-
placeholder="Enter auth password"
44+
placeholder="Enter auth token"
4545
class="px3 py1.5 rounded border border-base bg-transparent text-sm outline-none focus:border-violet"
4646
>
4747
<button
4848
type="submit"
4949
class="px3 py1.5 rounded bg-violet text-white text-sm hover:op80 disabled:op40"
50-
:disabled="!passwordInput.trim()"
50+
:disabled="!tokenInput.trim()"
5151
>
5252
Authorize
5353
</button>

packages/core/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
export { revokeAuthToken } from './node/auth-revoke'
12
export { createDevToolsContext } from './node/context'
23
export { getInternalContext } from './node/context-internal'
34
export type { DevToolsInternalContext, InternalAnonymousAuthStorage } from './node/context-internal'
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import type { DevToolsNodeContext } from '@vitejs/devtools-kit'
2+
import type { RpcFunctionsHost } from './host-functions'
3+
import { getInternalContext } from './context-internal'
4+
5+
/**
6+
* Revoke an auth token: remove from storage and notify all connected clients
7+
* using this token that they are no longer trusted.
8+
*/
9+
export async function revokeAuthToken(context: DevToolsNodeContext, token: string): Promise<void> {
10+
const internal = getInternalContext(context)
11+
const storage = internal.storage.auth
12+
13+
// Remove from persistent storage
14+
storage.mutate((state) => {
15+
delete state.trusted[token]
16+
})
17+
18+
const rpcHost = context.rpc as unknown as RpcFunctionsHost
19+
if (!rpcHost._rpcGroup)
20+
return
21+
22+
// Collect affected session IDs before modifying meta
23+
const affectedSessionIds = new Set<string>()
24+
for (const client of rpcHost._rpcGroup.clients) {
25+
if (client.$meta.clientAuthId === token) {
26+
affectedSessionIds.add(client.$meta.id)
27+
client.$meta.isTrusted = false
28+
client.$meta.clientAuthId = undefined!
29+
}
30+
}
31+
32+
if (affectedSessionIds.size === 0)
33+
return
34+
35+
// Notify affected clients
36+
await rpcHost.broadcast({
37+
method: 'devtoolskit:internal:auth:revoked',
38+
args: [],
39+
filter: client => affectedSessionIds.has(client.$meta.id),
40+
})
41+
}

packages/core/src/node/config.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,12 @@ export interface DevToolsConfig extends Partial<StartOptions> {
1414
*/
1515
clientAuth?: boolean
1616
/**
17-
* Pre-configured auth passwords that are automatically trusted.
17+
* Pre-configured auth tokens that are automatically trusted.
1818
*
19-
* Clients connecting with an auth ID matching one of these passwords
19+
* Clients connecting with an auth token matching one of these
2020
* will be auto-approved without a terminal prompt.
2121
*/
22-
clientAuthPasswords?: string[]
22+
clientAuthTokens?: string[]
2323
}
2424

2525
export interface ResolvedDevToolsConfig {
@@ -36,7 +36,7 @@ export function normalizeDevToolsConfig(
3636
config: {
3737
...(isObject(config) ? config : {}),
3838
clientAuth: isObject(config) ? (config.clientAuth ?? true) : true,
39-
clientAuthPasswords: isObject(config) ? (config.clientAuthPasswords ?? []) : [],
39+
clientAuthTokens: isObject(config) ? (config.clientAuthTokens ?? []) : [],
4040
host: isObject(config) ? (config.host ?? host) : host,
4141
},
4242
}

packages/core/src/node/rpc/anonymous/auth.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,9 @@ export const anonymousAuth = defineRpcFunction({
3838
}
3939
}
4040

41-
// Auto-approve if authId matches a configured password
42-
const passwords = (context.viteConfig.devtools?.config as any)?.clientAuthPasswords as string[] ?? []
43-
if (passwords.includes(query.authId)) {
41+
// Auto-approve if authId matches a configured auth token
42+
const tokens = (context.viteConfig.devtools?.config as any)?.clientAuthTokens as string[] ?? []
43+
if (tokens.includes(query.authId)) {
4444
storage.mutate((state) => {
4545
state.trusted[query.authId] = {
4646
authId: query.authId,

packages/core/src/node/rpc/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ declare module '@vitejs/devtools-kit' {
6565

6666
// @keep-sorted
6767
export interface DevToolsRpcClientFunctions {
68-
68+
'devtoolskit:internal:auth:revoked': () => Promise<void>
6969
'devtoolskit:internal:logs:updated': () => Promise<void>
7070
'devtoolskit:internal:rpc:client-state:patch': (key: string, patches: SharedStatePatch[], syncId: string) => Promise<void>
7171
'devtoolskit:internal:rpc:client-state:updated': (key: string, fullState: any, syncId: string) => Promise<void>

packages/core/src/node/ws.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ export async function createWsServer(options: CreateWsServerOptions) {
5454
meta.isTrusted = true
5555
meta.clientAuthId = authId
5656
}
57-
else if (authId && ((context.viteConfig.devtools?.config as any)?.clientAuthPasswords ?? []).includes(authId)) {
57+
else if (authId && ((context.viteConfig.devtools?.config as any)?.clientAuthTokens ?? []).includes(authId)) {
5858
meta.isTrusted = true
5959
meta.clientAuthId = authId
6060
}

packages/kit/src/client/rpc-ws.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,16 @@ export function createWsRpcClientMode(
5151
},
5252
)
5353

54+
// Handle server-initiated auth revocation
55+
clientRpc.register({
56+
name: 'devtoolskit:internal:auth:revoked',
57+
type: 'event',
58+
handler: () => {
59+
isTrusted = false
60+
events.emit('rpc:is-trusted:updated', false)
61+
},
62+
})
63+
5464
async function requestTrust() {
5565
if (isTrusted)
5666
return true

packages/self-inspect/src/app/components/AuthTokensList.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ function formatDate(timestamp: number): string {
3535
<thead>
3636
<tr border="b base" text-left>
3737
<th px2 py1 font-medium op60 text-xs>
38-
Auth ID
38+
Auth Token
3939
</th>
4040
<th px2 py1 font-medium op60 text-xs>
4141
User Agent

packages/self-inspect/src/node/rpc/functions/revoke-auth-token.ts

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,13 @@
1-
import { getInternalContext } from '@vitejs/devtools'
1+
import { revokeAuthToken } from '@vitejs/devtools'
22
import { defineRpcFunction } from '@vitejs/devtools-kit'
33

4-
export const revokeAuthToken = defineRpcFunction({
4+
export const revokeAuthTokenRpc = defineRpcFunction({
55
name: 'devtoolskit:self-inspect:revoke-auth-token',
66
type: 'action',
77
setup: (context) => {
8-
const internal = getInternalContext(context)
9-
const storage = internal.storage.auth
108
return {
119
handler: async (authId: string) => {
12-
storage.mutate((state) => {
13-
delete state.trusted[authId]
14-
})
10+
await revokeAuthToken(context, authId)
1511
},
1612
}
1713
},

0 commit comments

Comments
 (0)