Trust Score
Every Passji identity builds a trust score over time. Use it for progressive access control and risk-based decisions.
What is Trust Scoring?
Trust scores are a number between 0.0 and 1.0 that represent the reputation of an identity. Unlike traditional verification (email, phone, ID), trust scores are earned over time through consistent, legitimate usage.
New accounts start with low trust. As users authenticate regularly, use multiple apps, and maintain good security hygiene, their trust score increases.
Why trust scoring? Traditional identity systems are binary - you're verified or you're not. Trust scores provide a spectrum that apps can use for nuanced access control. A brand-new account can still use your app, just with fewer privileges until they build trust.
Trust Signals
Trust scores are computed from five weighted signals:
Account Age
How long the identity has existed. Ramps linearly over 180 days.
Max contribution: 180 days
Auth Frequency
Regular usage indicates a real, active identity. Logarithmic scale.
Max contribution: 100 auths
App Diversity
Using multiple apps shows real-world utility. Caps at 10 unique apps.
Max contribution: 10 apps
Multi-Device
Passkeys registered on multiple devices indicate good security hygiene.
Max contribution: 5 devices
Activity Recency
Recent activity bonus that decays over 30 days of inactivity.
Max contribution: Active in last 30 days
Score Calculation
The trust score is a weighted sum of normalized signals:
trust_score =
0.30 * min(account_age_days / 180, 1.0)
+ 0.20 * min(log10(auth_count + 1) / 2, 1.0)
+ 0.25 * min(unique_apps / 10, 1.0)
+ 0.10 * min(device_count / 5, 1.0)
+ 0.15 * max(1.0 - days_since_last_auth / 30, 0.0)
// Result is clamped to [0.0, 1.0]Score Examples
| Scenario | Score | Notes |
|---|---|---|
| Brand new account | ~0.15 | Clean history only |
| 30 days, 10 auths, 2 apps | ~0.40 | Casual user |
| 90 days, 50 auths, 5 apps, 2 devices | ~0.65 | Active user |
| 180+ days, 100+ auths, 10+ apps, 3+ devices | ~0.95 | Power user |
Requesting Trust Data
To receive trust information in tokens, include the trust scope in your authorization request:
// Request trust information in your auth flow
const authUrl = new URL("https://passji.com/authorize");
authUrl.searchParams.set("client_id", "your_client_id");
authUrl.searchParams.set("redirect_uri", "https://yourapp.com/callback");
authUrl.searchParams.set("response_type", "code");
authUrl.searchParams.set("scope", "openid emoji trust"); // Include 'trust'
authUrl.searchParams.set("state", generateState());
// The id_token will include trust_score and account_age_daysToken Response
// ID token with trust scope
{
"iss": "https://passji.com",
"sub": "usr_abc123",
"emoji_id": "🦊🌸🎯",
"trust_score": 0.85, // 0.0 to 1.0
"account_age_days": 45, // Days since registration
"auth_method": "webauthn"
}Progressive Access Control
Use trust scores to implement progressive access - grant more privileges as users build trust:
// Progressive access based on trust score
function checkAccess(user, action) {
const trustScore = user.trust_score;
switch (action) {
case "read_public":
return true; // Anyone can read
case "post_content":
if (trustScore < 0.3) {
return { allowed: false, reason: "Account too new" };
}
return { allowed: true };
case "send_messages":
if (trustScore < 0.5) {
return { allowed: false, reason: "Build more trust first" };
}
return { allowed: true };
case "withdraw_funds":
if (trustScore < 0.8) {
return { allowed: false, reason: "High trust required" };
}
return { allowed: true };
default:
return { allowed: false };
}
}Example Thresholds
| Trust Level | Range | Suggested Access |
|---|---|---|
| Minimal | 0.0 - 0.3 | Read-only, limited API calls, no messaging |
| Basic | 0.3 - 0.5 | Create content, basic interactions |
| Standard | 0.5 - 0.7 | Full features, messaging, transactions |
| Elevated | 0.7 - 0.9 | Moderator roles, higher limits |
| Maximum | 0.9 - 1.0 | Admin features, sensitive operations |
UI Patterns
You can show users their trust level to encourage engagement and explain access restrictions:
// Display trust tier to users
function TrustBadge({ trustScore }) {
const tier = getTrustTier(trustScore);
return (
<div className="flex items-center gap-2">
<span className="text-2xl">{tier.emoji}</span>
<div>
<div className="font-medium">{tier.name}</div>
<div className="text-sm text-muted-foreground">
{tier.description}
</div>
</div>
</div>
);
}
function getTrustTier(score) {
if (score >= 0.9) return {
emoji: "🌟",
name: "Stellar",
description: "Maximum trust - long history, diverse usage"
};
if (score >= 0.7) return {
emoji: "✨",
name: "Established",
description: "Well-established identity"
};
if (score >= 0.5) return {
emoji: "🌱",
name: "Growing",
description: "Building trust over time"
};
if (score >= 0.3) return {
emoji: "🌿",
name: "Newcomer",
description: "New but active account"
};
return {
emoji: "🌰",
name: "Fresh",
description: "Just getting started"
};
}Best Practices
- Don't block completely - Even low-trust users should be able to use basic features. This builds trust over time.
- Explain restrictions - Tell users why they can't access something and how to build trust.
- Use appropriate thresholds - High-risk actions need high trust. Read-only operations can be open to anyone.
- Combine with other signals - Trust scores work best alongside other context (rate limiting, content moderation).
- Cache appropriately - Trust scores don't change frequently. Refresh on login, not every request.
Remember: Trust scores are probabilistic, not deterministic. A high trust score doesn't guarantee good behavior, and a low score doesn't mean bad intent. Use them as one signal among many.