KindeK
Kinde5mo ago
5 replies
COACH

Self-serve portal link without a Kinde SDK

Can someone help me understand where I am going wrong with generating a self-serve portal link? I use Netlify and have paths to call my Express app. Authentication all works, but I can't seem to generate a link for the self-serve portal.

Netlify function error

Aug 21, 01:40:03 PM: 918de926 Duration: 4.61 ms    Memory Usage: 146 MB
Aug 21, 01:40:13 PM: e57788d1 INFO   GET /portal - User: anonymous
Aug 21, 01:40:13 PM: e57788d1 INFO   Got the keys
Aug 21, 01:40:13 PM: e57788d1 INFO   Token verification successful
Aug 21, 01:40:14 PM: e57788d1 INFO   Access token type: string
Aug 21, 01:40:14 PM: e57788d1 INFO   Access token (first 50 chars): eyJhbGciOiJSUzI1NiIsImtpZCI6IjlmOjYzOjVmOjg4OjczOm
Aug 21, 01:40:14 PM: e57788d1 ERROR  Portal API error: {"errors":[{"code":"INVALID_CREDENTIALS","message":"Invalid credentials used to access API"},{"code":"MISSING_AUDIENCE","message":"Missing or incorrect Management API audience"}]}
Aug 21, 01:40:14 PM: e57788d1 Duration: 675.77 ms    Memory Usage: 146 MB
Aug 21, 01:40:14 PM: 4a9bb456 INFO   GET /access/ - User: anonymous
Aug 21, 01:40:14 PM: 4a9bb456 Duration: 6.58 ms    Memory Usage: 146 MB


Code

/**
 * API endpoint: Redirect to self-serve portal
 *
 * Generates a one-time portal link for the authenticated user and redirects them.
 * Uses the user's session to get their access token and call Kinde's Account API.
 *
 * @route GET /portal
 * @middleware protectRoute - Ensures user is authenticated
 * @middleware getUser - Adds user object to request
 */
app.get("/portal", protectRoute, getUser, async (req, res) => {
  try {
    // Get access token from the user's session via Kinde client
    const accessToken = await kindeClient.getToken(req);
    console.log("Access token type:", typeof accessToken);
    console.log("Access token (first 50 chars):", accessToken ? accessToken.substring(0, 50) : "null");

    if (!accessToken) {
      return res.redirect(`${baseUrl}/access/?error=no-token`);
    }

    // Generate portal link using Kinde Account API
    const portalResponse = await fetch(
      `${kindeConfig.issuerBaseUrl}/api/v1/account_api/portal_link`,
      {
        method: "POST",
        headers: {
          Authorization: `Bearer ${accessToken}`,
          "Content-Type": "application/json",
        },
        body: JSON.stringify({
          return_url: `${baseUrl}/access/`,
        }),
      },
    );

    if (!portalResponse.ok) {
      console.error("Portal API error:", await portalResponse.text());
      return res.redirect(`${baseUrl}/access/?error=portal-failed`);
    }

    const portalData = await portalResponse.json();

    // Redirect user to the portal
    res.redirect(portalData.url);
  } catch (error) {
    console.error("Portal generation error:", error);
    res.redirect(`${baseUrl}/access/?error=portal-error`);
  }
});
Was this page helpful?