Skip to content

Commit

Permalink
fix: Open redirect issues
Browse files Browse the repository at this point in the history
  • Loading branch information
dogukanoksuz committed Feb 13, 2025
1 parent 6bce7ae commit de241ab
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 17 deletions.
61 changes: 44 additions & 17 deletions src/components/ui/user-auth-form.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import * as React from "react"
import Link from "next/link"
import { useRouter } from "next/router"
import { authService, http } from "@/services"
import { zodResolver } from "@hookform/resolvers/zod"
import { AlertCircle, CheckIcon, LogIn } from "lucide-react"
import Link from "next/link"
import { useRouter } from "next/router"
import * as React from "react"
import { useForm } from "react-hook-form"
import * as z from "zod"

import { cn } from "@/lib/utils"
import { useLogin } from "@/hooks/auth/useLogin"
import { Button } from "@/components/ui/button"
import { Icons } from "@/components/ui/icons"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { useLogin } from "@/hooks/auth/useLogin"
import { cn } from "@/lib/utils"

import {
Form,
Expand All @@ -38,7 +38,9 @@ import {
SelectValue,
} from "./select"

interface UserAuthFormProps extends React.HTMLAttributes<HTMLDivElement> { }
interface UserAuthFormProps extends React.HTMLAttributes<HTMLDivElement> {}

const safeToRedirect = ["/auth", "/notifications", "/servers", "/settings"]

export function UserAuthForm({ className, ...props }: UserAuthFormProps) {
const router = useRouter()
Expand All @@ -49,12 +51,10 @@ export function UserAuthForm({ className, ...props }: UserAuthFormProps) {
React.useEffect(() => {
const fetchAuthData = async () => {
try {
const authTypesResponse = await http
.get("/auth/types")
const authTypesResponse = await http.get("/auth/types")
setAuthTypes(authTypesResponse.data)

const authGateResponse = await http
.get("/auth/gate")
const authGateResponse = await http.get("/auth/gate")
loginForm.setValue("type", authGateResponse.data)
} catch (error) {
console.error("An error occurred while fetching auth data:", error)
Expand Down Expand Up @@ -138,6 +138,29 @@ export function UserAuthForm({ className, ...props }: UserAuthFormProps) {

if (toHome) return "/"

const isSafe = (() => {
try {
const resolvedRedirectURL = new URL(redirectUri, location.origin)

if (resolvedRedirectURL.origin === new URL(location.origin).origin) {
for (const safeUrl of safeToRedirect) {
if (resolvedRedirectURL.pathname.startsWith(safeUrl)) {
return true
}
}
return false
} else {
return false
}
} catch (error) {
return false
}
})()

if (!isSafe) {
redirectUri = "/"
}

return redirectUri
}

Expand All @@ -146,13 +169,17 @@ export function UserAuthForm({ className, ...props }: UserAuthFormProps) {

try {
let axiosReply
if (!data.otp) axiosReply = await login(data.name, data.password, "", data.type)
else axiosReply = await login(data.name, data.password, data.otp, data.type)
if (!data.otp)
axiosReply = await login(data.name, data.password, "", data.type)
else
axiosReply = await login(data.name, data.password, data.otp, data.type)

setError("")
setTimeout(() => {
const user = axiosReply.data.user
const isNonRedirectable = !!(user.status === 0 && user.permissions.view.redirect)
const isNonRedirectable = !!(
user.status === 0 && user.permissions.view.redirect
)
router.push(getRedirectUri(isNonRedirectable))
}, 500)
} catch (e: any) {
Expand All @@ -170,8 +197,8 @@ export function UserAuthForm({ className, ...props }: UserAuthFormProps) {
if (e.response.data.new_password) {
setError(
e.response.data.new_password[1] ||
e.response.data.new_password[0] ||
e.response.data.new_password
e.response.data.new_password[0] ||
e.response.data.new_password
)
}

Expand Down Expand Up @@ -232,8 +259,8 @@ export function UserAuthForm({ className, ...props }: UserAuthFormProps) {
if (e.response.data.new_password) {
setError(
e.response.data.new_password[1] ||
e.response.data.new_password[0] ||
e.response.data.new_password
e.response.data.new_password[0] ||
e.response.data.new_password
)
}
} finally {
Expand Down
18 changes: 18 additions & 0 deletions src/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { NextResponse, type NextRequest } from "next/server"
import { IUser } from "./types/user"

const authRoutes = ["/auth/login"]
const safeToRedirect = ["/auth", "/notifications", "/servers", "/settings"]

export function middleware(request: NextRequest) {
let urlBeforeRedirect = request.nextUrl.pathname
Expand Down Expand Up @@ -37,6 +38,23 @@ export function middleware(request: NextRequest) {

if (currentUser && authRoutes.includes(request.nextUrl.pathname)) {
let url = request.nextUrl.searchParams.get("redirect") || "/"

// Check if url is safe
const isSafe =
new URL(url, request.url).origin === new URL(request.url).origin &&
(() => {
for (const safeUrl of safeToRedirect) {
if (url.startsWith(safeUrl)) {
return true
}
}
return false
})()

if (!isSafe) {
url = "/"
}

return NextResponse.redirect(new URL(url, request.url))
}

Expand Down

0 comments on commit de241ab

Please sign in to comment.