Dear All,
I have a directus that already integrate to SSO Keycloak with this settings:
"AUTH_PROVIDERS": "keycloak", "AUTH_KEYCLOAK_DRIVER": "openid", "AUTH_KEYCLOAK_CLIENT_ID": "directus", "AUTH_KEYCLOAK_CLIENT_SECRET": "secret", "AUTH_KEYCLOAK_ISSUER_URL": "https://sso.domain.net/realms/realmdomain/.well-known/openid-configuration", "AUTH_KEYCLOAK_IDENTIFIER_KEY": "email", "AUTH_KEYCLOAK_ALLOW_PUBLIC_REGISTRATION": true, "AUTH_KEYCLOAK_REDIRECT_ALLOW_LIST": "http://localhost:3000", "AUTH_KEYCLOAK_LABEL": "SSO-Domain-ID", "ACCESS_TOKEN_TTL": "15m", "REFRESH_TOKEN_TTL": "7d", "REFRESH_TOKEN_COOKIE_SECURE": false, "REFRESH_TOKEN_COOKIE_SAME_SITE": "lax", "REFRESH_TOKEN_COOKIE_NAME": "directus_refresh_token", "SESSION_COOKIE_TTL": "1d", "SESSION_COOKIE_SECURE": false, "SESSION_COOKIE_SAME_SITE": "lax", "SESSION_COOKIE_NAME": "directus_session_token",
With those settings, users can login to directus admin UI with their SSO credentials. It’s done.
Now, I want to integrate my frontend with SSO keycloak and directus as backend. I tried using same client with directus, but after successful login in SSO Keycloak login page, it redirected back to directus admin site, not frontend site.
I also tried using new client, dedicated for frontend site. So redirect_URI set to http://localhost:3000/api/auth/callback/keycloak and when i click “login with SSO”, it redirected to Keycloak login page as it should be.
This is my /api/auth/callback/keycloak/route.ts (I’m using nextjs 13.5.1):
import { NextRequest } from 'next/server';
export const dynamic = 'force-dynamic';
export async function GET(request: NextRequest) {
try {
const { searchParams } = new URL(request.url);
const code = searchParams.get('code');
const error = searchParams.get('error');
if (error) {
return Response.json({ error }, { status: 400 });
}
if (!code) {
return Response.json({ error: 'Missing authorization code' }, { status: 400 });
}
// Exchange code for tokens
const tokenRes = await fetch('https://sso.domain.net/realms/realmdomain/protocol/openid-connect/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
client_id: 'portaldev',
grant_type: 'authorization_code',
code,
redirect_uri: 'http://localhost:3000/api/auth/callback/keycloak',
}),
});
if (!tokenRes.ok) {
const err = await tokenRes.text();
return Response.json({ error: 'Failed to exchange code for tokens', details: err }, { status: 400 });
}
const tokens = await tokenRes.json();
// Fetch user info
const userinfoRes = await fetch('https://sso.domain.net/realms/realmdomain/protocol/openid-connect/userinfo', {
headers: {
Authorization: `Bearer ${tokens.access_token}`,
},
});
const userinfo = userinfoRes.ok ? await userinfoRes.json() : null;
// Set cookie for access_token
const cookie = `kc_access_token=${tokens.access_token}; Path=/; HttpOnly; Secure; Max-Age=${tokens.expires_in}`;
// Redirect to homepage after SSO
return new Response(null, {
status: 302,
headers: {
'Set-Cookie': cookie,
'Location': '/',
},
});
} catch (err) {
console.error('Keycloak SSO callback error:', err);
return Response.json({ error: 'Internal server error' }, { status: 500 });
}
}
But it stuck, redirected to unauth homepage.
How to do it correctly?
After some test and trials, I’m stuck with this conditions:
Directus: 11.9.2
Keycloak: 26.2.4
Nextjs: 13.5.1
- I see that in worked Directus Admin UI, “Login with Keycloak” href to https://directusdomain/auth/login/keycloak?redirect=https://directusdomain/admin/login?=continue
- But I want to redirect to frontend again after successful keycloak login. So I tried: https://directusdomain/auth/login/keycloak?redirect=https://localhost:3000 but it failed with this errror: Invalid Payload, https://localhost:3000 can’t be used to redirect after login. But when I change “redirect” to “redirect_url” (https://directusdomain/auth/login/keycloak?redirect_url)=https://localhost:3000) it work, it redirected to sso keycloak login page.
- But, after success keycloak login, it stay in this kind of link: https://directusdomain/auth/login/keycloak/callback?state=fV7Td1q9redacted&iss=https%3A%2F%2Fsso.domain.net%2Frealms%2Fdomainrealm&code=34a22080-aredacted with this JSON data shown up in browser:
{“data”:{“access_token”:“eyA”,“refresh_token”:“bn-V4A”,“expires”:86400000}}
And I tested the refresh_token via postman to /auth/refresh, it worked!
Any ideas, how to get it done from here?