Skip to content

Commit 6f0f194

Browse files
antfuclaude
andauthored
feat: add multiple auth modes (#229)
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 124606f commit 6f0f194

38 files changed

+1168
-183
lines changed

alias.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,12 @@ export const alias = {
1616
'@vitejs/devtools-kit/constants': r('kit/src/constants.ts'),
1717
'@vitejs/devtools-kit/utils/events': r('kit/src/utils/events.ts'),
1818
'@vitejs/devtools-kit/utils/nanoid': r('kit/src/utils/nanoid.ts'),
19+
'@vitejs/devtools-kit/utils/human-id': r('kit/src/utils/human-id.ts'),
1920
'@vitejs/devtools-kit/utils/shared-state': r('kit/src/utils/shared-state.ts'),
2021
'@vitejs/devtools-kit': r('kit/src/index.ts'),
2122
'@vitejs/devtools-rolldown': r('rolldown/src/index.ts'),
2223
'@vitejs/devtools-self-inspect': r('self-inspect/src/index.ts'),
24+
'@vitejs/devtools/internal': r('core/src/internal.ts'),
2325
'@vitejs/devtools/client/inject': r('core/src/client/inject/index.ts'),
2426
'@vitejs/devtools/client/webcomponents': r('core/src/client/webcomponents/index.ts'),
2527
'@vitejs/devtools': r('core/src/index.ts'),

packages/core/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
"./client/webcomponents": "./dist/client/webcomponents.js",
2828
"./config": "./dist/config.js",
2929
"./dirs": "./dist/dirs.js",
30+
"./internal": "./dist/internal.js",
3031
"./package.json": "./package.json"
3132
},
3233
"types": "./dist/index.d.ts",
@@ -59,7 +60,7 @@
5960
"birpc": "catalog:deps",
6061
"cac": "catalog:deps",
6162
"h3": "catalog:deps",
62-
"immer": "catalog:deps",
63+
"immer": "catalog:inlined",
6364
"launch-editor": "catalog:deps",
6465
"mlly": "catalog:deps",
6566
"obug": "catalog:deps",
@@ -77,6 +78,7 @@
7778
"@xterm/addon-fit": "catalog:frontend",
7879
"@xterm/xterm": "catalog:frontend",
7980
"dompurify": "catalog:frontend",
81+
"human-id": "catalog:inlined",
8082
"tsdown": "catalog:build",
8183
"typescript": "catalog:devtools",
8284
"unplugin-vue": "catalog:build",

packages/core/src/client/webcomponents/.generated/css.ts

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

packages/core/src/client/webcomponents/components/dock/Dock.vue

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,14 @@ function onPointerDown(e: PointerEvent) {
7070
const isRpcTrusted = ref(context.rpc.isTrusted)
7171
context.rpc.events.on('rpc:is-trusted:updated', (isTrusted) => {
7272
isRpcTrusted.value = isTrusted
73-
if (isTrusted && context.docks.selected?.id === BUILTIN_ENTRY_CLIENT_AUTH_NOTICE.id)
73+
if (isTrusted && context.docks.selected?.id === BUILTIN_ENTRY_CLIENT_AUTH_NOTICE.id) {
7474
context.docks.switchEntry(null)
75+
}
76+
else if (!isTrusted) {
77+
// On revocation: close current tab and panel
78+
context.docks.switchEntry(null)
79+
context.panel.store.open = false
80+
}
7581
})
7682
7783
const groupedEntries = computed(() => context.docks.groupedEntries)

packages/core/src/client/webcomponents/components/dock/DockEmbedded.vue

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<script setup lang="ts">
22
import type { DocksContext } from '@vitejs/devtools-kit/client'
33
import { useEventListener } from '@vueuse/core'
4-
import { onUnmounted } from 'vue'
4+
import { onUnmounted, ref } from 'vue'
55
import { sharedStateToRef } from '../../state/docks'
66
import { closeDockPopup, useIsDockPopupOpen } from '../../state/popup'
77
import ToastOverlay from '../display/ToastOverlay.vue'
@@ -17,6 +17,12 @@ const props = defineProps<{
1717
const isDockPopupOpen = useIsDockPopupOpen()
1818
const settings = sharedStateToRef(props.context.docks.settings)
1919
20+
// Force float mode when unauthorized, regardless of store setting
21+
const isRpcTrusted = ref(props.context.rpc.isTrusted)
22+
props.context.rpc.events.on('rpc:is-trusted:updated', (isTrusted) => {
23+
isRpcTrusted.value = isTrusted
24+
})
25+
2026
// Close the dock when clicking outside of it
2127
useEventListener(window, 'mousedown', (e: MouseEvent) => {
2228
if (!settings.value.closeOnOutsideClick)
@@ -44,7 +50,7 @@ onUnmounted(() => {
4450

4551
<template>
4652
<template v-if="!isDockPopupOpen">
47-
<template v-if="context.panel.store.mode === 'edge'">
53+
<template v-if="isRpcTrusted && context.panel.store.mode === 'edge'">
4854
<DockEdge :context />
4955
</template>
5056
<template v-else>

packages/core/src/client/webcomponents/components/dock/DockStandalone.vue

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ const persistedDoms = markRaw(new PersistedDomViewsManager(viewsContainer))
2020
const isRpcTrusted = ref(context.rpc.isTrusted)
2121
context.rpc.events.on('rpc:is-trusted:updated', (isTrusted) => {
2222
isRpcTrusted.value = isTrusted
23+
if (!isTrusted) {
24+
context.docks.switchEntry(null)
25+
}
2326
})
2427
2528
watch(

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

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,28 @@
11
<script setup lang="ts">
22
import type { DocksContext } from '@vitejs/devtools-kit/client'
3+
import { ref } from 'vue'
34
import VitePlus from '../icons/VitePlus.vue'
45
5-
defineProps<{
6+
const props = defineProps<{
67
context: DocksContext
78
}>()
9+
10+
const tokenInput = ref('')
11+
12+
function submitToken() {
13+
const value = tokenInput.value.trim()
14+
if (!value)
15+
return
16+
props.context.rpc.requestTrustWithToken(value)
17+
}
818
</script>
919

1020
<template>
1121
<div class="w-full h-full flex flex-col items-center justify-center p20">
1222
<div class="max-w-150 flex flex-col items-center justify-center gap-2">
1323
<VitePlus class="w-20 h-20" />
1424
<h1 class="text-2xl font-bold text-violet mb2">
15-
Vite DevTools is Unauthorized
25+
Vite DevTools needs Authorization
1626
</h1>
1727
<p class="op75">
1828
Vite DevTools offers advanced features that can access your server, view your filesystem, and execute commands.
@@ -23,6 +33,24 @@ defineProps<{
2333
<p class="font-bold bg-green:5 p1 px3 rounded mt8 text-green">
2434
Check your terminal for the authorization prompt and come back.
2535
</p>
36+
<div class="mt6 op50">
37+
or
38+
</div>
39+
<form class="mt2 flex items-center gap-2" @submit.prevent="submitToken">
40+
<input
41+
v-model="tokenInput"
42+
type="text"
43+
placeholder="Enter auth token"
44+
class="px3 py1.5 rounded border border-base bg-transparent text-sm outline-none focus:border-violet"
45+
>
46+
<button
47+
type="submit"
48+
class="px3 py1.5 rounded bg-violet text-white text-sm hover:op80 disabled:op40"
49+
:disabled="!tokenInput.trim()"
50+
>
51+
Authorize
52+
</button>
53+
</form>
2654
</div>
2755
</div>
2856
</template>

packages/core/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export { createDevToolsContext } from './node/context'
2+
export type { DevToolsInternalContext, InternalAnonymousAuthStorage } from './node/context-internal'
23
export { DevTools } from './node/plugins'
34
export { createDevToolsMiddleware } from './node/server'

packages/core/src/internal.ts

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

0 commit comments

Comments
 (0)