> ## Documentation Index
> Fetch the complete documentation index at: https://sleekplan.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# JWT Single Sign-On

> Authenticate your existing users into Sleekplan with signed JSON Web Tokens — no separate login required, supported in the widget, embed, and standalone page

Sleekplan's Single Sign-On (SSO) lets users who already have accounts in your application log in to the feedback widget without a separate sign-up step. SSO is built on JSON Web Tokens (JWT), a standard for securely passing authentication data between systems. When a user performs an action that requires authentication — such as voting or submitting feedback — Sleekplan calls `$sleek.sso`, your server generates a signed token, and Sleekplan accepts it to authenticate the user instantly.

## How it works

1. A user performs an authenticated action (vote, submit feedback) in the widget.
2. The JavaScript SDK calls `$sleek.sso`.
3. Your server generates a JWT signed with your private SSO key.
4. You pass the token back via the `callback()` parameter.
5. Sleekplan verifies the token and logs the user in seamlessly.

***

<Steps>
  <Step title="Get your SSO secret key">
    Find your private SSO key in **Settings → Developer** inside the Sleekplan dashboard at [https://app.sleekplan.com/settings/developer](https://app.sleekplan.com/settings/developer).

    <Warning>
      Your SSO secret key must only ever be used server-side. Never expose it in client-side JavaScript, public repositories, or environment variables that are shipped to the browser. Anyone who obtains the key can generate tokens for any user.
    </Warning>

    <Note>
      The SSO secret key is available on the **Starter** and **Business** plans.
    </Note>
  </Step>

  <Step title="Generate a JWT on your server">
    Use your language's JWT library to create a token signed with HMAC SHA-256 (`HS256`). Replace `PRIVATE_SSO_KEY` with the key from the previous step. The `localUser` parameter in each example represents your application's user record.

    <Tabs>
      <Tab title="Node.js">
        **Install the library**

        ```bash theme={"system"}
        npm install --save jsonwebtoken
        ```

        **Generate a token**

        ```javascript theme={"system"}
        const jwt = require('jsonwebtoken');

        const key = 'PRIVATE_SSO_KEY';

        function createSSOToken(localUser) {
            const userData = {
                // Required: email address used to uniquely identify the user
                mail: localUser.mail,
                // Recommended: your internal user ID
                id: localUser.id,
                // Optional: lowercase username, letters and numbers only
                name: localUser.name,
                // Optional: URL to the user's avatar image
                img: localUser.imgStr,
                // Optional: user weighting from 1-10 (e.g. based on MRR)
                weight: 4,
                // Optional: additional key-value pairs for the user profile
                meta: {
                    companyName: localUser.cName,
                },
            };
            return jwt.sign(userData, key, { algorithm: 'HS256' });
        }
        ```
      </Tab>

      <Tab title="PHP">
        **Install the library**

        ```bash theme={"system"}
        composer require firebase/php-jwt
        ```

        **Generate a token**

        ```php theme={"system"}
        use \Firebase\JWT\JWT;

        $key = 'PRIVATE_SSO_KEY';

        function createSSOToken($localUser) {
            $userData = [
                // Required: email address used to uniquely identify the user
                'mail'   => $localUser['mail'],
                // Recommended: your internal user ID
                'id'     => $localUser['id'],
                // Optional: lowercase username, letters and numbers only
                'name'   => $localUser['name'],
                // Optional: URL to the user's avatar image
                'img'    => $localUser['imgStr'],
                // Optional: user weighting from 1-10 (e.g. based on MRR)
                'weight' => 4,
                // Optional: additional key-value pairs for the user profile
                'meta'   => [
                    'companyName' => $localUser['cName'],
                ],
            ];
            return JWT::encode($userData, $key, 'HS256');
        }
        ```
      </Tab>

      <Tab title="Python">
        **Install the library**

        ```bash theme={"system"}
        pip install PyJWT
        ```

        **Generate a token**

        ```python theme={"system"}
        import jwt

        PrivateKey = 'PRIVATE_SSO_KEY'

        def create_sleekplan_token(localUser):
            user_data = {
                # Required: email address used to uniquely identify the user
                'mail': localUser.mail,
                # Recommended: your internal user ID
                'id': localUser.id,
                # Optional: lowercase username, letters and numbers only
                'name': localUser.name,
                # Optional: URL to the user's avatar image
                'img': localUser.img,
                # Optional: user weighting from 1-10 (e.g. based on MRR)
                'weight': 4,
                # Optional: additional key-value pairs for the user profile
                'meta': {
                    'companyName': localUser.cName,
                },
            }
            return jwt.encode(user_data, PrivateKey, algorithm='HS256')
        ```
      </Tab>

      <Tab title="Ruby">
        **Install the library**

        ```ruby theme={"system"}
        # Gemfile
        gem 'jwt'
        ```

        **Generate a token**

        ```ruby theme={"system"}
        require 'jwt'

        PrivateKey = 'PRIVATE_SSO_KEY'

        def createSleekplanToken(localUser)
            user_data = {
                # Required: email address used to uniquely identify the user
                mail: localUser.mail,
                # Recommended: your internal user ID
                id: localUser.id,
                # Optional: lowercase username, letters and numbers only
                name: localUser.name,
                # Optional: URL to the user's avatar image
                img: localUser.img,
                # Optional: user weighting from 1-10 (e.g. based on MRR)
                weight: 4,
                # Optional: additional key-value pairs for the user profile
                meta: {
                    companyName: localUser.cName,
                },
            }
            JWT.encode(user_data, PrivateKey, 'HS256')
        end
        ```
      </Tab>

      <Tab title="Java">
        **Install the library**

        Add the [jjwt dependency](https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt/0.7.0) to your `pom.xml` or `build.gradle`.

        **Generate a token**

        ```java theme={"system"}
        import java.util.HashMap;
        import io.jsonwebtoken.Jwts;
        import io.jsonwebtoken.SignatureAlgorithm;

        public class CreateSSOToken {
            private static final String PrivateKey = "PRIVATE_SSO_KEY";

            public static String createToken(User user) throws Exception {
                HashMap<String, Object> userData = new HashMap<>();

                // Required: email address used to uniquely identify the user
                userData.put("mail", user.email);
                // Recommended: your internal user ID
                userData.put("id", user.id);
                // Optional: lowercase username, letters and numbers only
                userData.put("name", user.name);
                // Optional: URL to the user's avatar image
                userData.put("img", user.avatarURL);
                // Optional: user weighting from 1-10 (e.g. based on MRR)
                userData.put("weight", 4);

                return Jwts.builder()
                           .setClaims(userData)
                           .signWith(SignatureAlgorithm.HS256, PrivateKey.getBytes("UTF-8"))
                           .compact();
            }
        }
        ```
      </Tab>

      <Tab title="C#/.NET">
        **Install the library**

        ```bash theme={"system"}
        dotnet add package System.IdentityModel.Tokens.Jwt
        ```

        **Generate a token**

        ```csharp theme={"system"}
        using System.IdentityModel.Tokens.Jwt;
        using System.Security.Claims;
        using System.Text;
        using Microsoft.IdentityModel.Tokens;

        public class LocalUser {
            public string Mail { get; set; }
            public string Id { get; set; }
            public string Name { get; set; }
        }

        public static class SsoHelper {
            private const string PrivateKey = "PRIVATE_SSO_KEY";

            public static string CreateSsoToken(LocalUser localUser) {
                var claims = new List<Claim> {
                    new Claim("mail", localUser.Mail),
                    new Claim("id", localUser.Id ?? ""),
                    new Claim("name", localUser.Name ?? ""),
                    new Claim("weight", "4"),
                };

                var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(PrivateKey));
                var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

                var token = new JwtSecurityToken(
                    claims: claims,
                    signingCredentials: creds
                );

                return new JwtSecurityTokenHandler().WriteToken(token);
            }
        }
        ```
      </Tab>
    </Tabs>

    ### JWT payload attributes

    Sleekplan requires an email address to uniquely identify each user. All other attributes are optional but recommended.

    | Attribute   | Required | Description                                                                                                      |
    | ----------- | -------- | ---------------------------------------------------------------------------------------------------------------- |
    | `mail`      | Yes      | Email address of the user. Used as the primary identifier for the Sleekplan user record.                         |
    | `id`        | No       | Your internal user ID. Highly recommended — allows Sleekplan to match users even if their email address changes. |
    | `name`      | No       | Username. Use lowercase letters and numbers only — no spaces or special characters.                              |
    | `full_name` | No       | Full display name shown in the Sleekplan UI.                                                                     |
    | `img`       | No       | URL to the user's avatar image.                                                                                  |
    | `weight`    | No       | User weighting from 1 to 10 (e.g., based on MRR). Used for impact scoring on feedback items.                     |
    | `meta`      | No       | Object of additional key-value pairs stored on the user profile.                                                 |
  </Step>

  <Step title="Authenticate the user with the token">
    Once you have a token, pass it to Sleekplan using the method that fits your integration type.

    <Tabs>
      <Tab title="Widget — on page load">
        Set `window.SLEEK_USER` before the widget snippet loads. This is the simplest approach for server-rendered pages where the user is already authenticated when the page is served.

        ```html theme={"system"}
        <!-- User token — place this BEFORE the widget snippet -->
        <script type="text/javascript">
        window.SLEEK_USER = {
            token: 'YOURTOKEN',
        };
        </script>

        <!-- Sleekplan widget snippet -->
        <script type="text/javascript">
        window.$sleek = [];
        window.SLEEK_PRODUCT_ID = YOUR_PRODUCT_ID;
        (function() {
            d = document;
            s = d.createElement("script");
            s.src = "https://client.sleekplan.com/sdk/e.js";
            s.async = 1;
            d.getElementsByTagName("head")[0].appendChild(s);
        })();
        </script>
        ```

        <Warning>
          Place the `window.SLEEK_USER` block **before** the widget snippet. If the snippet loads first, it will not pick up the token.
        </Warning>
      </Tab>

      <Tab title="Widget — single-page apps">
        Call `$sleek.setUser()` after the SDK has initialized. Use the `sleek:init` event listener to guarantee the SDK is ready before calling the method.

        ```javascript theme={"system"}
        window.document.addEventListener('sleek:init', () => {
            $sleek.setUser({ token: 'YOURTOKEN' });
        }, false);
        ```

        <Tip>
          Always wrap `$sleek.setUser()` in the `sleek:init` listener in SPAs. Calling the method before the SDK finishes loading is the most common cause of authentication failures.
        </Tip>
      </Tab>

      <Tab title="Widget — async / on-demand">
        Assign a function to `$sleek.sso` that fetches a fresh token from your server and passes it to the `callback`. Sleekplan calls this function only when an unauthenticated user performs an action that requires authentication.

        ```javascript theme={"system"}
        $sleek.sso = function(callback) {
            fetch('https://yourserver.com/your_sso_endpoint/')
                .then(response => response.json())
                .then((data) => {
                    callback({ token: data.ssoToken });
                });
        };
        ```
      </Tab>

      <Tab title="Standalone page">
        Enable **Force single sign-on** in your product settings, then set a custom login URL. When an unauthenticated user visits your feedback board, Sleekplan redirects them to your login page. After authenticating, redirect the user back to the board with the token appended.

        You can pass the token in any of these ways:

        | Method         | How to pass the token                     |
        | -------------- | ----------------------------------------- |
        | GET parameter  | `feedback.yourcompany.com/?sso=YOURTOKEN` |
        | POST parameter | Form field named `sso`                    |
        | Request header | `Authorization: Bearer YOURTOKEN`         |

        <Tip>
          Check the `Referer` header to determine the correct redirect URL. Ensure your login page is served over HTTPS so the `Referer` header is sent with the request.
        </Tip>
      </Tab>

      <Tab title="Iframe">
        Append the token as a GET parameter to your iframe embed URL:

        ```html theme={"system"}
        <iframe
          src="https://embed-PRODUCTID.sleekplan.app/?sso=YOURTOKEN"
          width="100%"
          height="600"
          frameborder="0">
        </iframe>
        ```

        You can also pass the token via POST parameter or `Authorization: Bearer` header using the same methods described for standalone pages.
      </Tab>
    </Tabs>
  </Step>
</Steps>
