Skip to content

Commit 7aa9b0e

Browse files
committed
Relax redirectUri validation to allow any URI scheme
This allows native apps such as Cursor to redirect to cursor://xyz and use oauth DCR. Https is enforced on non-localhost URIs
1 parent ddf54a5 commit 7aa9b0e

File tree

2 files changed

+16
-16
lines changed

2 files changed

+16
-16
lines changed

src/routes/oauth/authorize.tsx

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,23 @@ import { useIsDark } from '~/hooks/useIsDark'
99
import { BrandContextMenu } from '~/components/BrandContextMenu'
1010

1111
/**
12-
* Validate redirect URI - must be localhost or HTTPS
12+
* Validate redirect URI - block only HTTP to non-localhost
13+
* Allows: HTTPS, localhost (any protocol), custom schemes (cursor://, vscode://, etc.)
1314
* Inlined here to avoid pulling in server-only dependencies
1415
*/
1516
function validateRedirectUri(uri: string): boolean {
1617
try {
1718
const url = new URL(uri)
18-
if (
19-
url.hostname === 'localhost' ||
20-
url.hostname === '127.0.0.1' ||
21-
url.hostname === '[::1]'
22-
) {
23-
return true
19+
// Block only HTTP to non-localhost
20+
if (url.protocol === 'http:') {
21+
return (
22+
url.hostname === 'localhost' ||
23+
url.hostname === '127.0.0.1' ||
24+
url.hostname === '[::1]'
25+
)
2426
}
25-
if (url.protocol === 'https:') {
26-
return true
27-
}
28-
return false
27+
// Allow everything else: HTTPS, custom schemes
28+
return true
2929
} catch {
3030
return false
3131
}
@@ -84,7 +84,7 @@ export const Route = createFileRoute('/oauth/authorize')({
8484
if (!validateRedirectUri(deps.redirect_uri)) {
8585
return {
8686
error: 'invalid_request',
87-
errorDescription: 'redirect_uri must be localhost or HTTPS',
87+
errorDescription: 'HTTP redirect URIs are only allowed for localhost',
8888
}
8989
}
9090

src/routes/oauth/register.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export const Route = createFileRoute('/oauth/register')({
1616
// This is secure because:
1717
// 1. Registration only generates deterministic client IDs
1818
// 2. No secrets are issued (PKCE public clients)
19-
// 3. Redirect URIs are validated for localhost or HTTPS
19+
// 3. Redirect URIs block insecure HTTP to remote hosts
2020
const origin = request.headers.get('Origin')
2121
setResponseHeader('Content-Type', 'application/json')
2222
setResponseHeader('Access-Control-Allow-Origin', origin || '*')
@@ -41,14 +41,14 @@ export const Route = createFileRoute('/oauth/register')({
4141
url.hostname === 'localhost' ||
4242
url.hostname === '127.0.0.1' ||
4343
url.hostname === '[::1]'
44-
const isHttps = url.protocol === 'https:'
4544

46-
if (!isLocalhost && !isHttps) {
45+
// Block only HTTP to non-localhost (allows custom schemes like cursor://)
46+
if (url.protocol === 'http:' && !isLocalhost) {
4747
return new Response(
4848
JSON.stringify({
4949
error: 'invalid_redirect_uri',
5050
error_description:
51-
'Redirect URIs must be localhost or HTTPS',
51+
'HTTP redirect URIs are only allowed for localhost',
5252
}),
5353
{ status: 400 },
5454
)

0 commit comments

Comments
 (0)