security(auth): move guest credentials to secure server-side API route and update docker config
All checks were successful
Deploy Flowstate to VPS / deploy (push) Successful in 48s
All checks were successful
Deploy Flowstate to VPS / deploy (push) Successful in 48s
This commit is contained in:
parent
f99d1f15c4
commit
9ae5ddaf90
4 changed files with 82 additions and 13 deletions
|
|
@ -10,13 +10,6 @@ WORKDIR /app
|
||||||
COPY --from=deps /app/node_modules ./node_modules
|
COPY --from=deps /app/node_modules ./node_modules
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
# Inject NEXT_PUBLIC_ vars at build time (required for client-side embedding)
|
|
||||||
ARG NEXT_PUBLIC_GUEST_EMAIL
|
|
||||||
ARG NEXT_PUBLIC_GUEST_PASSWORD
|
|
||||||
|
|
||||||
ENV NEXT_PUBLIC_GUEST_EMAIL=$NEXT_PUBLIC_GUEST_EMAIL
|
|
||||||
ENV NEXT_PUBLIC_GUEST_PASSWORD=$NEXT_PUBLIC_GUEST_PASSWORD
|
|
||||||
|
|
||||||
RUN npm run build
|
RUN npm run build
|
||||||
|
|
||||||
# 3. Run production image (Node.js server)
|
# 3. Run production image (Node.js server)
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { getGuestCredentials, loginUser } from "@/services/auth.service";
|
import { loginAsGuest, loginUser } from "@/services/auth.service";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders the login page containing the authentication form, error handling,
|
* Renders the login page containing the authentication form, error handling,
|
||||||
|
|
@ -65,7 +65,7 @@ export default function LoginPage() {
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches guest user credentials and triggers guest authentication.
|
* Triggers secure guest authentication via the backend service.
|
||||||
*
|
*
|
||||||
* @async
|
* @async
|
||||||
*/
|
*/
|
||||||
|
|
@ -73,15 +73,15 @@ export default function LoginPage() {
|
||||||
setError(null);
|
setError(null);
|
||||||
setLoadingType("guest");
|
setLoadingType("guest");
|
||||||
|
|
||||||
const result = await getGuestCredentials();
|
const result = await loginAsGuest();
|
||||||
|
|
||||||
if (result.error || !result.email || !result.password) {
|
if (result.error) {
|
||||||
setError(result.error || "Guest login is not configured properly.");
|
setError(result.error);
|
||||||
setLoadingType(null);
|
setLoadingType(null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await handleSignIn(result.email, result.password, "guest");
|
router.push("/summary");
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
51
app/api/auth/guest/route.ts
Normal file
51
app/api/auth/guest/route.ts
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
/**
|
||||||
|
* @file api/auth/guest/route.ts
|
||||||
|
* @description API route handler for guest authentication utilizing server-side environment variables and auto sign-in.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { NextResponse } from "next/server";
|
||||||
|
import { signIn } from "@/auth";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles POST requests to authenticate as a guest user using environment credentials.
|
||||||
|
*
|
||||||
|
* @async
|
||||||
|
* @returns {Promise<NextResponse>} A JSON response confirming successful guest sign-in or an error message.
|
||||||
|
*/
|
||||||
|
export async function POST() {
|
||||||
|
const email = process.env.GUEST_EMAIL;
|
||||||
|
const password = process.env.GUEST_PASSWORD;
|
||||||
|
|
||||||
|
if (!email || !password) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: "Guest login is not configured on the server." },
|
||||||
|
{ status: 500 },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await signIn("credentials", {
|
||||||
|
email,
|
||||||
|
password,
|
||||||
|
redirect: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
return NextResponse.json({ success: true }, { status: 200 });
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error("Guest login error:", error);
|
||||||
|
if (
|
||||||
|
error?.message?.includes("CredentialsSignin") ||
|
||||||
|
error?.type === "CredentialsSignin"
|
||||||
|
) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: "Incorrect guest credentials." },
|
||||||
|
{ status: 401 },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: "Internal Server Error" },
|
||||||
|
{ status: 500 },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -23,6 +23,31 @@ export async function getGuestCredentials() {
|
||||||
return { email, password };
|
return { email, password };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Triggers guest authentication securely via the backend API route.
|
||||||
|
*
|
||||||
|
* @async
|
||||||
|
* @returns {Promise<{ success?: boolean; error?: string }>} An object indicating success or describing the error.
|
||||||
|
*/
|
||||||
|
export async function loginAsGuest() {
|
||||||
|
try {
|
||||||
|
const response = await fetch("/api/auth/guest", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
return { error: data.error || "Guest login failed." };
|
||||||
|
}
|
||||||
|
|
||||||
|
return { success: true };
|
||||||
|
} catch (err: any) {
|
||||||
|
return { error: "Server error: API endpoint not available" };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Authenticates a user using email and password credentials, performing client-side validation first.
|
* Authenticates a user using email and password credentials, performing client-side validation first.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue