Skip to main content

SSO Integration

SSO (Single Sign-On) automatically recognizes your logged-in users on Feedbackfirst. They don't have to sign up again — they just click the feedback button and start reviewing.

How SSO Works

  1. User is logged into your app
  2. User clicks the feedback button
  3. Your app calls /api/feedbackfirst/sso-token to get a token
  4. The token is passed to the Feedbackfirst widget
  5. User is automatically logged into Feedbackfirst
  6. User leaves feedback without extra sign-up

Implementation

Step 1: Create an SSO Endpoint

Create an endpoint on your server that returns a JWT token signed with your Feedbackfirst API key.

Endpoint: POST /api/feedbackfirst/sso-token

Request body:

{
"user_id": "user-123",
"email": "user@example.com",
"name": "John Doe"
}

Response:

{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

Step 2: Generate the JWT Token

The token must be signed with your Feedbackfirst API key. Here's how to generate it:

Node.js example:

const jwt = require('jsonwebtoken');

app.post('/api/feedbackfirst/sso-token', (req, res) => {
const { user_id, email, name } = req.body;

const token = jwt.sign(
{
user_id,
email,
name,
iat: Math.floor(Date.now() / 1000),
exp: Math.floor(Date.now() / 1000) + 3600 // 1 hour expiry
},
process.env.FEEDBACKFIRST_API_KEY,
{ algorithm: 'HS256' }
);

res.json({ token });
});

Python example:

import jwt
import time
from datetime import datetime, timedelta

@app.route('/api/feedbackfirst/sso-token', methods=['POST'])
def sso_token():
data = request.json
user_id = data['user_id']
email = data['email']
name = data['name']

payload = {
'user_id': user_id,
'email': email,
'name': name,
'iat': int(time.time()),
'exp': int(time.time()) + 3600 # 1 hour expiry
}

token = jwt.encode(
payload,
os.environ['FEEDBACKFIRST_API_KEY'],
algorithm='HS256'
)

return {'token': token}

PHP example:

require 'vendor/autoload.php';
use Firebase\JWT\JWT;

$data = json_decode(file_get_contents('php://input'), true);
$user_id = $data['user_id'];
$email = $data['email'];
$name = $data['name'];

$payload = [
'user_id' => $user_id,
'email' => $email,
'name' => $name,
'iat' => time(),
'exp' => time() + 3600
];

$token = JWT::encode(
$payload,
$_ENV['FEEDBACKFIRST_API_KEY'],
'HS256'
);

echo json_encode(['token' => $token]);

Step 3: Get Your API Key

  1. Go to your Feedbackfirst workspace
  2. Click Settings → API Keys
  3. Copy your API key
  4. Store it as an environment variable (e.g., FEEDBACKFIRST_API_KEY)

Step 4: Use the Token in Your Widget

Pass the token to the feedback button:

<ff-feedback-button
maker-id="feedbackfirst"
sso-token-url="/api/feedbackfirst/sso-token"
label="Leave feedback"
></ff-feedback-button>

The widget will automatically call your endpoint and use the returned token to log in the user.

Token Requirements

Your JWT token must include:

FieldTypeRequiredDescription
user_idstringYesUnique identifier for the user
emailstringYesUser's email address
namestringYesUser's display name
iatnumberYesIssued at (Unix timestamp)
expnumberYesExpiration time (Unix timestamp)

Token expiry: Tokens should expire within 1 hour. The widget will request a new token if the current one expires.

Security Considerations

Protect Your API Key

  • Store your API key as an environment variable
  • Never commit it to version control
  • Never expose it in client-side code
  • Rotate your key regularly

Validate User Identity

Before generating a token, verify that the user is actually logged in:

app.post('/api/feedbackfirst/sso-token', (req, res) => {
// Check if user is authenticated
if (!req.user) {
return res.status(401).json({ error: 'Not authenticated' });
}

// Generate token for authenticated user
const token = jwt.sign(
{
user_id: req.user.id,
email: req.user.email,
name: req.user.name,
iat: Math.floor(Date.now() / 1000),
exp: Math.floor(Date.now() / 1000) + 3600
},
process.env.FEEDBACKFIRST_API_KEY,
{ algorithm: 'HS256' }
);

res.json({ token });
});

Use HTTPS

Always use HTTPS for your SSO endpoint. Never send tokens over unencrypted connections.

Troubleshooting

Widget shows login screen instead of auto-logging in

  • Check that your sso-token-url is correct
  • Verify the endpoint is returning a valid token
  • Check browser console for errors
  • Make sure the token is properly signed with your API key

"Invalid token" error

  • Verify you're using the correct API key
  • Check that the token is properly formatted (JWT with 3 parts separated by dots)
  • Verify the token hasn't expired
  • Check that all required fields are included

User not recognized after login

  • Verify the user_id is unique and consistent
  • Check that the email is valid
  • Make sure the token is being passed to the widget correctly

CORS errors

  • Your SSO endpoint must be accessible from the browser
  • Make sure CORS headers are set correctly
  • The endpoint should accept POST requests

Testing

Test your SSO implementation:

# Get a token
curl -X POST http://localhost:3000/api/feedbackfirst/sso-token \
-H "Content-Type: application/json" \
-d '{
"user_id": "test-user-123",
"email": "test@example.com",
"name": "Test User"
}'

# Response should be:
# {"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."}

Decode the token to verify it's correct:

# Use jwt.io or a CLI tool
jwt decode "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Best Practices

Rate Limiting

Rate limit your SSO endpoint to prevent abuse:

const rateLimit = require('express-rate-limit');

const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100 // limit each IP to 100 requests per windowMs
});

app.post('/api/feedbackfirst/sso-token', limiter, (req, res) => {
// ... token generation
});

Logging

Log SSO token requests for debugging:

app.post('/api/feedbackfirst/sso-token', (req, res) => {
console.log(`SSO token requested for user: ${req.user.id}`);
// ... token generation
});

Error Handling

Handle errors gracefully:

app.post('/api/feedbackfirst/sso-token', (req, res) => {
try {
if (!req.user) {
return res.status(401).json({ error: 'Not authenticated' });
}

const token = jwt.sign(
{ /* payload */ },
process.env.FEEDBACKFIRST_API_KEY,
{ algorithm: 'HS256' }
);

res.json({ token });
} catch (error) {
console.error('SSO token generation failed:', error);
res.status(500).json({ error: 'Token generation failed' });
}
});