Unable to Retrieve CSRF Token via /api/config Using express.sid After Keycloak Login
-
Hi,
I have a question regarding the express.sid cookie:After login, the express.sid is generated and stored in the cookies.
I tried using this session ID to fetch the CSRF token by calling /api/config, but it doesn’t seem to work for me.
My goal is:
Successfully retrieve a valid CSRF token.
Use it to create topics or posts via the Write API.
Ensure that once I have this token and session, I can access all the required NodeBB APIs.
Could you please clarify the correct approach to:
Retrieve and use the CSRF token with the express.sid?
Authenticate API requests (like creating topics or posts) when using Keycloak for login instead of NodeBB’s built-in login?
Any guidance or best practices for this integration would be greatly appreciated.
app.post('/api/login', async (req, res) => {
try {
const { username, password } = req.body || {}
if (!username || !password) {
return res.status(400).json({ error: 'Missing username or password' })
}
// 1) Login against Keycloak (Direct Access Grant)
const tokenResponse = await fetchKeycloakTokens({ username, password });// 2) Get user profile to build NodeBB payload const userinfo = await fetchUserInfo({ accessToken: tokenResponse.access_token }); // 3) Build session-sharing JWT for NodeBB // Minimal fields: id (unique), username, email const nodebbPayload = { id: userinfo.sub || userinfo.preferred_username || username, username: userinfo.preferred_username || username, email: userinfo.email || undefined, fullname: userinfo.name || undefined, }; if (!SESSION_SHARING_JWT_SECRET) { return res.status(500).json({ error: 'Server not configured: SESSION_SHARING_JWT_SECRET missing' }); } const signed = await jwt.sign(nodebbPayload, SESSION_SHARING_JWT_SECRET, { expiresIn: '1h' }); const loginRes = await axios.post('http://localhost:4567/api/v3/utilities/login', { username, password }, { headers: { "Content-Type": "application/json", "Authorization": `Bearer ${signed}`, // pass your Bearer token here }, withCredentials: true }); console.log(loginRes, "LOGIN RESPONSE"); // 4) Set cookie for NodeBB domain so it can pick it up const response = await axios.get('http://192.168.60.108:4567/api/config', { withCredentials: true }); const newToken = response?.data?.csrf_token; const cookieOptions = { httpOnly: true, secure: false, sameSite: 'none', path: '/', maxAge: 8 * 60 * 60 * 10000, domain: '192.168.60.108' } res.cookie("token", signed, cookieOptions); res.cookie('csrf_token', newToken, cookieOptions); return res.json({ success: true, message: "login successfully", redirect: NODEBB_BASE_URL, keycloakAccessToken: tokenResponse.access_token });
} catch (err) {
// eslint-disable-next-line no-console
console.error('Login error:', err?.response?.data || err?.message || err);
const status = err?.response?.status || 500
const message = err?.response?.data?.error_description || err?.response?.data?.error || err?.message || 'Unexpected error';
return res.status(status).json({ success: false, error: message });
}});
In the /api/login API, I called the /api/config API, but when I check the /api/config API in Postman using the express.sid generated after login, it never returns the response for that specific user.Thanks in advance!
-
Hi,
I have a question regarding the express.sid cookie:After login, the express.sid is generated and stored in the cookies.
I tried using this session ID to fetch the CSRF token by calling /api/config, but it doesn’t seem to work for me.
My goal is:
Successfully retrieve a valid CSRF token.
Use it to create topics or posts via the Write API.
Ensure that once I have this token and session, I can access all the required NodeBB APIs.
Could you please clarify the correct approach to:
Retrieve and use the CSRF token with the express.sid?
Authenticate API requests (like creating topics or posts) when using Keycloak for login instead of NodeBB’s built-in login?
Any guidance or best practices for this integration would be greatly appreciated.
app.post('/api/login', async (req, res) => {
try {
const { username, password } = req.body || {}
if (!username || !password) {
return res.status(400).json({ error: 'Missing username or password' })
}
// 1) Login against Keycloak (Direct Access Grant)
const tokenResponse = await fetchKeycloakTokens({ username, password });// 2) Get user profile to build NodeBB payload const userinfo = await fetchUserInfo({ accessToken: tokenResponse.access_token }); // 3) Build session-sharing JWT for NodeBB // Minimal fields: id (unique), username, email const nodebbPayload = { id: userinfo.sub || userinfo.preferred_username || username, username: userinfo.preferred_username || username, email: userinfo.email || undefined, fullname: userinfo.name || undefined, }; if (!SESSION_SHARING_JWT_SECRET) { return res.status(500).json({ error: 'Server not configured: SESSION_SHARING_JWT_SECRET missing' }); } const signed = await jwt.sign(nodebbPayload, SESSION_SHARING_JWT_SECRET, { expiresIn: '1h' }); const loginRes = await axios.post('http://localhost:4567/api/v3/utilities/login', { username, password }, { headers: { "Content-Type": "application/json", "Authorization": `Bearer ${signed}`, // pass your Bearer token here }, withCredentials: true }); console.log(loginRes, "LOGIN RESPONSE"); // 4) Set cookie for NodeBB domain so it can pick it up const response = await axios.get('http://192.168.60.108:4567/api/config', { withCredentials: true }); const newToken = response?.data?.csrf_token; const cookieOptions = { httpOnly: true, secure: false, sameSite: 'none', path: '/', maxAge: 8 * 60 * 60 * 10000, domain: '192.168.60.108' } res.cookie("token", signed, cookieOptions); res.cookie('csrf_token', newToken, cookieOptions); return res.json({ success: true, message: "login successfully", redirect: NODEBB_BASE_URL, keycloakAccessToken: tokenResponse.access_token });
} catch (err) {
// eslint-disable-next-line no-console
console.error('Login error:', err?.response?.data || err?.message || err);
const status = err?.response?.status || 500
const message = err?.response?.data?.error_description || err?.response?.data?.error || err?.message || 'Unexpected error';
return res.status(status).json({ success: false, error: message });
}});
In the /api/login API, I called the /api/config API, but when I check the /api/config API in Postman using the express.sid generated after login, it never returns the response for that specific user.Thanks in advance!
You’ll need both the session cookie and its matching CSRF token. Log in so NodeBB sets the cookie, then call /api/config with it to get the token. With Keycloak, it’s best to use the session-sharing plugin and JWT so NodeBB handles sessions and CSRF for you.
-
Hi,
I have a question regarding the express.sid cookie:After login, the express.sid is generated and stored in the cookies.
I tried using this session ID to fetch the CSRF token by calling /api/config, but it doesn’t seem to work for me.
My goal is:
Successfully retrieve a valid CSRF token.
Use it to create topics or posts via the Write API.
Ensure that once I have this token and session, I can access all the required NodeBB APIs.
Could you please clarify the correct approach to:
Retrieve and use the CSRF token with the express.sid?
Authenticate API requests (like creating topics or posts) when using Keycloak for login instead of NodeBB’s built-in login?
Any guidance or best practices for this integration would be greatly appreciated.
app.post('/api/login', async (req, res) => {
try {
const { username, password } = req.body || {}
if (!username || !password) {
return res.status(400).json({ error: 'Missing username or password' })
}
// 1) Login against Keycloak (Direct Access Grant)
const tokenResponse = await fetchKeycloakTokens({ username, password });// 2) Get user profile to build NodeBB payload const userinfo = await fetchUserInfo({ accessToken: tokenResponse.access_token }); // 3) Build session-sharing JWT for NodeBB // Minimal fields: id (unique), username, email const nodebbPayload = { id: userinfo.sub || userinfo.preferred_username || username, username: userinfo.preferred_username || username, email: userinfo.email || undefined, fullname: userinfo.name || undefined, }; if (!SESSION_SHARING_JWT_SECRET) { return res.status(500).json({ error: 'Server not configured: SESSION_SHARING_JWT_SECRET missing' }); } const signed = await jwt.sign(nodebbPayload, SESSION_SHARING_JWT_SECRET, { expiresIn: '1h' }); const loginRes = await axios.post('http://localhost:4567/api/v3/utilities/login', { username, password }, { headers: { "Content-Type": "application/json", "Authorization": `Bearer ${signed}`, // pass your Bearer token here }, withCredentials: true }); console.log(loginRes, "LOGIN RESPONSE"); // 4) Set cookie for NodeBB domain so it can pick it up const response = await axios.get('http://192.168.60.108:4567/api/config', { withCredentials: true }); const newToken = response?.data?.csrf_token; const cookieOptions = { httpOnly: true, secure: false, sameSite: 'none', path: '/', maxAge: 8 * 60 * 60 * 10000, domain: '192.168.60.108' } res.cookie("token", signed, cookieOptions); res.cookie('csrf_token', newToken, cookieOptions); return res.json({ success: true, message: "login successfully", redirect: NODEBB_BASE_URL, keycloakAccessToken: tokenResponse.access_token });
} catch (err) {
// eslint-disable-next-line no-console
console.error('Login error:', err?.response?.data || err?.message || err);
const status = err?.response?.status || 500
const message = err?.response?.data?.error_description || err?.response?.data?.error || err?.message || 'Unexpected error';
return res.status(status).json({ success: false, error: message });
}});
In the /api/login API, I called the /api/config API, but when I check the /api/config API in Postman using the express.sid generated after login, it never returns the response for that specific user.Thanks in advance!
HenryCharles Thank you. I’m using the session-sharing plugin, but I’m facing an issue. After logging in with SSO, I call NodeBB APIs directly (not the UI). The express.sid is generated, and I use that express.sid to get the CSRF token. However, when I call the /api/config API, the response always gives uid: 0 instead of the logged-in user’s UID.
-
HenryCharles Thank you. I’m using the session-sharing plugin, but I’m facing an issue. After logging in with SSO, I call NodeBB APIs directly (not the UI). The express.sid is generated, and I use that express.sid to get the CSRF token. However, when I call the /api/config API, the response always gives uid: 0 instead of the logged-in user’s UID.
balu hey, sorry for the delay, can you post a cURL call to the
/api/config
endpoint with the cookie?I know you posted a code sample (via DM) but the reason I ask for a cURL call is that it is the most straightforward way to test the call and often eliminates errors in header values, etc.