login to next backend

This commit is contained in:
2026-04-18 23:45:46 +01:00
parent 2131a34e33
commit 174febe986
7 changed files with 139 additions and 80 deletions
+37 -30
View File
@@ -10,7 +10,7 @@ import {
// Importe o componente que criámos (ajuste o caminho se necessário)
import MultiAspectEditor from '../../components/MultiAspectEditor';
import dynamic from "next/dynamic";
import { keycloak } from '@/app/feature/auth/keycloak-config';
// import { keycloak } from '@/app/feature/auth/keycloak-config';
const Editor = dynamic(
() => import("@tinymce/tinymce-react").then((mod) => mod.Editor),
{ ssr: false }
@@ -100,36 +100,43 @@ const CreateNewsPage = () => {
};
// Avoid hydration mismatch by waiting for mount
useEffect(() => {
keycloak.init({
onLoad: "check-sso",
pkceMethod: "S256",
}).then(async (authenticated) => {
if (authenticated) {
const token = keycloak.token!;
localStorage.setItem("token", token);
const res = await fetch("http://localhost:3001/profile/", {
headers: {
Authorization: `Bearer ${token}`,
},
});
const profile = await res.json();
var keycloakData : {
email: string,
email_verified: boolean,
name: string,
picture: string,
roles: string[]
} = profile.keycloak
// useEffect(() => {
// keycloak.init({
// onLoad: "check-sso",
// pkceMethod: "S256",
// }).then(async (authenticated) => {
// if (authenticated) {
// const token = keycloak.token!;
// localStorage.setItem("token", token);
setUser(keycloakData);
console.log("Profile:", keycloakData);
}
});
}, []);
// // 👉 send token to Next.js backend
// fetch("/api/session", {
// method: "POST",
// body: JSON.stringify({ token }),
// });
// const res = await fetch("http://localhost:3001/profile/", {
// headers: {
// //Authorization: `Bearer ${token}`,
// },
// });
// const profile = await res.json();
// var keycloakData : {
// email: string,
// email_verified: boolean,
// name: string,
// picture: string,
// roles: string[]
// } = profile.keycloak
// setUser(keycloakData);
// console.log("Profile:", keycloakData);
// }
// });
// }, []);
return (
<div className="flex h-screen bg-slate-100/50 text-slate-900 font-sans">
+2 -27
View File
@@ -4,7 +4,7 @@ import React, { useState, useEffect } from "react";
import { useGoogleLogin } from "@react-oauth/google";
import { useTheme } from 'next-themes';
import { Sun, Moon } from 'lucide-react'; // Optional: install lucide-react for clean icons
import { keycloak } from '@/app/feature/auth/keycloak-config';
//import { keycloak } from '@/app/feature/auth/keycloak-config';
interface GoogleAuthResponse {
access_token: string;
@@ -33,27 +33,6 @@ export default function AppleStyleAuth() {
const { theme, setTheme } = useTheme();
const [mounted, setMounted] = useState(false);
// Avoid hydration mismatch by waiting for mount
useEffect(() => {
keycloak.init({
onLoad: "check-sso", // or "login-required"
pkceMethod: "S256",
}).then((authenticated) => {
if (authenticated) {
localStorage.setItem("token", keycloak.token!);
console.log("Logged in", keycloak.token);
localStorage.setItem("token", keycloak.token as string);
fetch("http://localhost:3001/profile/", {
headers: {
Authorization: `Bearer ${localStorage.getItem("token")}`,
},
});
}
});
setMounted(true);
}, []);
const handleExchange = async (googleResponse: GoogleAuthResponse): Promise<void> => {
try {
const details: Record<string, string> = {
@@ -190,11 +169,7 @@ export default function AppleStyleAuth() {
{/* 4. BOTÃO GOOGLE */}
<button
onClick={() =>
keycloak.login({
redirectUri: `${window.location.origin}/create-news`,
})
}
onClick={() => (window.location.href = "/api/auth/login")}
className="group flex w-full items-center justify-center gap-3 rounded-2xl border border-neutral-200 bg-white px-6 py-3.5 transition-all hover:bg-neutral-50 active:scale-[0.98] dark:border-neutral-800 dark:bg-transparent dark:hover:bg-neutral-900"
>
<svg className="h-5 w-5" viewBox="0 0 24 24">
+33
View File
@@ -0,0 +1,33 @@
import { NextResponse } from "next/server";
export async function GET(req: Request) {
const url = new URL(req.url);
const code = url.searchParams.get("code");
// exchange code for token (Keycloak token endpoint)
const tokenRes = await fetch("https://keycloak.petermaquiran.xyz/realms/tvone/protocol/openid-connect/token", {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
client_id: "tvone-web",
client_secret: "7jQUciQCCf2WRFRe170UANKzGKVWFIkY",
grant_type: "authorization_code",
code: code!,
redirect_uri: "http://localhost:3000/api/auth/callback",
}),
});
const text = await tokenRes.text();
var data = JSON.parse(text);
const res = NextResponse.redirect("http://localhost:3000/dashboard");
res.cookies.set("access_token", data.access_token, {
httpOnly: true,
secure: true,
sameSite: "lax",
path: "/",
});
return res;
}
+14
View File
@@ -0,0 +1,14 @@
export async function GET() {
const redirect = encodeURIComponent(
"http://localhost:3000/api/auth/callback"
);
const keycloakUrl =
`https://keycloak.petermaquiran.xyz/realms/tvone/protocol/openid-connect/auth` +
`?client_id=tvone-web` +
`&response_type=code` +
`&scope=openid` +
`&redirect_uri=${redirect}`;
return Response.redirect(keycloakUrl);
}
+14
View File
@@ -0,0 +1,14 @@
export async function GET() {
const redirect = encodeURIComponent(
"http://localhost:3000/api/auth/callback"
);
const keycloakUrl =
`https://keycloak.petermaquiran.xyz/auth/realms/tvone/protocol/openid-connect/auth` +
`?client_id=tvone-web` +
`&response_type=code` +
`&scope=openid` +
`&redirect_uri=${redirect}`;
return Response.redirect(keycloakUrl);
}
+16
View File
@@ -0,0 +1,16 @@
import { NextResponse } from "next/server";
export async function POST(req: Request) {
const { token } = await req.json();
const res = NextResponse.json({ ok: true });
res.cookies.set("auth_token", token, {
httpOnly: true,
secure: true,
sameSite: "lax",
path: "/",
});
return res;
}
+23 -23
View File
@@ -1,29 +1,29 @@
import Keycloak from "keycloak-js";
// import Keycloak from "keycloak-js";
/**
* KEYCLOAK CONFIGURATION
* Logic: Environment variable validation and OIDC configuration.
*/
// /**
// * KEYCLOAK CONFIGURATION
// * Logic: Environment variable validation and OIDC configuration.
// */
export const keycloakConfig = {
clientId: process.env.KEYCLOAK_CLIENT_ID!,
clientSecret: process.env.KEYCLOAK_CLIENT_SECRET!,
issuer: `${process.env.KEYCLOAK_ISSUER_URL}/realms/${process.env.KEYCLOAK_REALM}`,
// export const keycloakConfig = {
// clientId: process.env.KEYCLOAK_CLIENT_ID!,
// clientSecret: process.env.KEYCLOAK_CLIENT_SECRET!,
// issuer: `${process.env.KEYCLOAK_ISSUER_URL}/realms/${process.env.KEYCLOAK_REALM}`,
// Scopes needed for OIDC and profile access
scope: 'openid profile email',
// // Scopes needed for OIDC and profile access
// scope: 'openid profile email',
// Endpoint for global logout
endSessionEndpoint: `${process.env.KEYCLOAK_ISSUER_URL}/realms/${process.env.KEYCLOAK_REALM}/protocol/openid-connect/logout`,
};
// // Endpoint for global logout
// endSessionEndpoint: `${process.env.KEYCLOAK_ISSUER_URL}/realms/${process.env.KEYCLOAK_REALM}/protocol/openid-connect/logout`,
// };
// Simple check to ensure environment variables are present
if (!process.env.KEYCLOAK_CLIENT_ID || !process.env.KEYCLOAK_ISSUER_URL) {
console.warn("Auth Warning: Keycloak environment variables are missing.");
}
// // Simple check to ensure environment variables are present
// if (!process.env.KEYCLOAK_CLIENT_ID || !process.env.KEYCLOAK_ISSUER_URL) {
// console.warn("Auth Warning: Keycloak environment variables are missing.");
// }
export const keycloak = new Keycloak({
url: "https://keycloak.petermaquiran.xyz",
realm: "tvone", // ✅ IMPORTANT
clientId: "tvone-web", // must match Keycloak client
});
// export const keycloak = new Keycloak({
// url: "https://keycloak.petermaquiran.xyz",
// realm: "tvone", // ✅ IMPORTANT
// clientId: "tvone-web", // must match Keycloak client
// });