Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
Skins
  • Light
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse

Darkscribes Community

  1. Home
  2. Technical Support
  3. Unable to Retrieve CSRF Token via /api/config Using express.sid After Keycloak Login

Unable to Retrieve CSRF Token via /api/config Using express.sid After Keycloak Login

Scheduled Pinned Locked Moved Technical Support
8 Posts 3 Posters 0 Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • B This user is from outside of this forum
    B This user is from outside of this forum
    [email protected]
    wrote last edited by
    #1

    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!

    H B 2 Replies Last reply
    0
    • B [email protected]

      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!

      H This user is from outside of this forum
      H This user is from outside of this forum
      [email protected]
      wrote last edited by
      #2

      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.

      1 Reply Last reply
      0
      • B [email protected]

        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!

        B This user is from outside of this forum
        B This user is from outside of this forum
        [email protected]
        wrote last edited by
        #3

        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.

        julian@community.nodebb.orgJ 1 Reply Last reply
        0
        • B [email protected]

          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.

          julian@community.nodebb.orgJ This user is from outside of this forum
          julian@community.nodebb.orgJ This user is from outside of this forum
          [email protected]
          wrote last edited by
          #4

          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.

          1 Reply Last reply
          0
          • B This user is from outside of this forum
            B This user is from outside of this forum
            [email protected]
            wrote last edited by
            #5

            julian Thank you for your reply, I am DM my question.

            julian@community.nodebb.orgJ 1 Reply Last reply
            0
            • B [email protected]

              julian Thank you for your reply, I am DM my question.

              julian@community.nodebb.orgJ This user is from outside of this forum
              julian@community.nodebb.orgJ This user is from outside of this forum
              [email protected]
              wrote last edited by
              #6

              balu please continue the conversation here so others may contribute

              1 Reply Last reply
              0
              • B This user is from outside of this forum
                B This user is from outside of this forum
                [email protected]
                wrote last edited by
                #7

                Thanks julian for your reply, and sorry for my poor communication earlier. My main issue is that I’m using SSO with Cognito. After a successful login, I call the /api/config API using the generated express.sid, but the response always returns uid: 0. My question is: how is the express.sid generated? I’m using React.js for the frontend, not the default NodeBB UI.

                julian@community.nodebb.orgJ 1 Reply Last reply
                0
                • B [email protected]

                  Thanks julian for your reply, and sorry for my poor communication earlier. My main issue is that I’m using SSO with Cognito. After a successful login, I call the /api/config API using the generated express.sid, but the response always returns uid: 0. My question is: how is the express.sid generated? I’m using React.js for the frontend, not the default NodeBB UI.

                  julian@community.nodebb.orgJ This user is from outside of this forum
                  julian@community.nodebb.orgJ This user is from outside of this forum
                  [email protected]
                  wrote last edited by
                  #8

                  balu the cookie is generated by the express-cookie package.

                  If you're receiving uid 0 it means the cookie isn't being passed in correctly.

                  1 Reply Last reply
                  0
                  Reply
                  • Reply as topic
                  Log in to reply
                  • Oldest to Newest
                  • Newest to Oldest
                  • Most Votes


                  • Login

                  • Don't have an account? Register

                  • Login or register to search.
                  Powered by NodeBB Contributors
                  • First post
                    Last post
                  0
                  • Categories
                  • Recent
                  • Tags
                  • Popular
                  • Users
                  • Groups