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
- User is logged into your app
- User clicks the feedback button
- Your app calls
/api/feedbackfirst/sso-tokento get a token - The token is passed to the Feedbackfirst widget
- User is automatically logged into Feedbackfirst
- 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
- Go to your Feedbackfirst workspace
- Click Settings → API Keys
- Copy your API key
- 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:
| Field | Type | Required | Description |
|---|---|---|---|
user_id | string | Yes | Unique identifier for the user |
email | string | Yes | User's email address |
name | string | Yes | User's display name |
iat | number | Yes | Issued at (Unix timestamp) |
exp | number | Yes | Expiration 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-urlis 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_idis 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' });
}
});