Keycloak and NextJS: Difference between revisions
Jump to navigation
Jump to search
(4 intermediate revisions by the same user not shown) | |||
Line 5: | Line 5: | ||
Awful way to document but here goes<br> | Awful way to document but here goes<br> | ||
[[File:Keycloak setup1.png |600px]] | [[File:Keycloak setup1.png |600px]] | ||
== | ==Add Capabilities== | ||
Set the flow<br> | |||
[[File:Keycloak setup2.png |600px]] | [[File:Keycloak setup2.png |600px]] | ||
==Add Redirect== | ==Add Redirect== | ||
Line 14: | Line 14: | ||
Next this <br> | Next this <br> | ||
[[File:Keycloak setup4.png |600px]] | [[File:Keycloak setup4.png |600px]] | ||
==Create User== | |||
Create the user<br> | |||
[[File:Keycloak setup5.png |600px]] | |||
==Set The Password== | |||
Set The Password<br> | |||
[[File:Keycloak setup6.png |600px]] | |||
=NextJS Bit= | |||
==Setup Auth== | |||
Make the nextauth bits | |||
<syntaxhighlight lang="bash"> | |||
mkdir -p "src/app/api/auth/[...nextauth]" && touch "src/app/api/auth/[...nextauth]/route.ts" | |||
</syntaxhighlight> | |||
Implement the root.ts | |||
<syntaxhighlight lang="ts"> | |||
// src/app/api/auth/[...nextauth]/route.ts | |||
import { AuthOptions } from "next-auth"; | |||
import KeycloakProvider from "next-auth/providers/keycloak" | |||
export const authOptions: AuthOptions = { | |||
providers: [ | |||
KeycloakProvider({ | |||
clientId: process.env.KEYCLOAK_CLIENT_ID, | |||
clientSecret: process.env.KEYCLOAK_CLIENT_SECRET, | |||
issuer: process.env.KEYCLOAK_ISSUER | |||
}) | |||
] | |||
} | |||
const handler = NextAuth(authOptions); | |||
export { handler as GET, handler as POST | |||
</syntaxhighlight> | |||
Fix up ProcessEnv | |||
<syntaxhighlight lang="bash"> | |||
mkdir types && touch types/node-env.d.ts | |||
</syntaxhighlight> | |||
And | |||
<syntaxhighlight lang="ts"> | |||
// types/node-env.d.ts | |||
declare namespace NodeJS { | |||
export interface ProcessEnv { | |||
KEYCLOAK_CLIENT_ID: string | |||
KEYCLOAK_CLIENT_SECRET: string | |||
KEYCLOAK_ISSUER: string | |||
} | |||
} | |||
</syntaxhighlight> | |||
And | |||
<syntaxhighlight lang="bash"> | |||
touch .env.local | |||
</syntaxhighlight> | |||
And add | |||
<syntaxhighlight lang="bash"> | |||
KEYCLOAK_CLIENT_ID="nextjs" | |||
KEYCLOAK_CLIENT_SECRET="<client_secret>" | |||
KEYCLOAK_ISSUER="http://localhost:8080/realms/myrealm" | |||
</syntaxhighlight> | |||
==Login Page== | |||
Home Page | |||
<syntaxhighlight lang="ts"> | |||
// src/app/page.tsx | |||
import { getServerSession } from 'next-auth' | |||
import { authOptions } from './api/auth/[...nextauth]/route' | |||
import Login from '../components/Login' | |||
import Logout from '../components/Logout' | |||
export default async function Home() { | |||
const session = await getServerSession(authOptions) | |||
if (session) { | |||
return <div> | |||
<div>Your name is {session.user?.name}</div> | |||
<div><Logout /> </div> | |||
</div> | |||
} | |||
return ( | |||
<div> | |||
<Login /> | |||
</div> | |||
) | |||
} | |||
</syntaxhighlight> | |||
Login Component | |||
<syntaxhighlight lang="ts"> | |||
// src/components/Login.tsx | |||
"use client" | |||
import { signIn } from "next-auth/react"; | |||
export default function Login() { | |||
return <button onClick={() => signIn("keycloak")}> | |||
Signin with keycloak | |||
</button> | |||
} | |||
</syntaxhighlight> | |||
Logout Component | |||
<syntaxhighlight lang="ts"> | |||
// src/components/Logout.tsx | |||
"use client" | |||
import { signOut } from "next-auth/react"; | |||
export default function Logout() { | |||
return <button onClick={() => signOut()}> | |||
Signout of keycloak | |||
</button> | |||
} | |||
</syntaxhighlight> |
Latest revision as of 04:55, 28 April 2025
Introduction
Only do it once a project so wanted to capture how to this. Thanks to Harsh Bhandari
Keycloak
Create a Client
Awful way to document but here goes
Add Capabilities
Add Redirect
Get The Secret
Create User
Set The Password
NextJS Bit
Setup Auth
Make the nextauth bits
mkdir -p "src/app/api/auth/[...nextauth]" && touch "src/app/api/auth/[...nextauth]/route.ts"
Implement the root.ts
// src/app/api/auth/[...nextauth]/route.ts
import { AuthOptions } from "next-auth";
import KeycloakProvider from "next-auth/providers/keycloak"
export const authOptions: AuthOptions = {
providers: [
KeycloakProvider({
clientId: process.env.KEYCLOAK_CLIENT_ID,
clientSecret: process.env.KEYCLOAK_CLIENT_SECRET,
issuer: process.env.KEYCLOAK_ISSUER
})
]
}
const handler = NextAuth(authOptions);
export { handler as GET, handler as POST
Fix up ProcessEnv
mkdir types && touch types/node-env.d.ts
And
// types/node-env.d.ts
declare namespace NodeJS {
export interface ProcessEnv {
KEYCLOAK_CLIENT_ID: string
KEYCLOAK_CLIENT_SECRET: string
KEYCLOAK_ISSUER: string
}
}
And
touch .env.local
And add
KEYCLOAK_CLIENT_ID="nextjs"
KEYCLOAK_CLIENT_SECRET="<client_secret>"
KEYCLOAK_ISSUER="http://localhost:8080/realms/myrealm"
Login Page
Home Page
// src/app/page.tsx
import { getServerSession } from 'next-auth'
import { authOptions } from './api/auth/[...nextauth]/route'
import Login from '../components/Login'
import Logout from '../components/Logout'
export default async function Home() {
const session = await getServerSession(authOptions)
if (session) {
return <div>
<div>Your name is {session.user?.name}</div>
<div><Logout /> </div>
</div>
}
return (
<div>
<Login />
</div>
)
}
Login Component
// src/components/Login.tsx
"use client"
import { signIn } from "next-auth/react";
export default function Login() {
return <button onClick={() => signIn("keycloak")}>
Signin with keycloak
</button>
}
Logout Component
// src/components/Logout.tsx
"use client"
import { signOut } from "next-auth/react";
export default function Logout() {
return <button onClick={() => signOut()}>
Signout of keycloak
</button>
}