Olympus Docs
CookbookDefensive security

Age-of-account gating

Restrict actions for very new accounts

Bots and abuse-attempt accounts are often brand new. A simple defense: gate sensitive actions (transfers, posts, recommendations) by account age.

Why

Real users typically:

  • Take time to fully set up.
  • Don't immediately do high-risk actions.
  • Have some history before being trusted.

Bots / abuse accounts:

  • Sign up and immediately try the abused action.
  • Bulk operations within minutes of signup.

Implementation

Add age check to actions

async function canRecommend(req) {
  const session = await getSession(req);
  const ageHours = (Date.now() - session.identity.created_at) / 3_600_000;
  if (ageHours < 48) {
    return { allowed: false, reason: "account_too_new" };
  }
  return { allowed: true };
}

Show message:

Your account is new. You'll be able to do this after 48 hours.

Different thresholds per action

const minAge = {
  "post": 1,           // 1 hour
  "comment": 0.5,      // 30 min
  "transfer_money": 168, // 1 week
  "delete_account": 0.25, // 15 min (anti-impulsive-deletion)
  "join_premium": 0,    // no gating
};

Combine with reputation

Age alone is a blunt signal. Combine with:

  • Email verified.
  • MFA enrolled.
  • Profile completed.
  • N successful previous actions.
function trustScore(user) {
  let score = 0;
  if (user.age > 24) score += 20;
  if (user.age > 168) score += 30;
  if (user.email_verified) score += 20;
  if (user.has_mfa) score += 20;
  if (user.profile_complete) score += 10;
  if (user.successful_purchases > 0) score += 30;
  return score;
}

if (trustScore(user) < 50) blockAction();

100 = trusted; 0 = brand new and uninvested.

Communication

When blocking, explain:

"You can do this after [hours] hours, or after enabling 2-factor and verifying email."

Trust progression visible: "Your account becomes fully trusted after 1 week or 2FA enrollment."

Many users will speed-run the verification steps to unlock features.

Storing the data

identity.created_at is automatic.

For more granular tracking:

ALTER TABLE identities ADD COLUMN trust_score INTEGER DEFAULT 0;
ALTER TABLE identities ADD COLUMN trust_updated_at TIMESTAMPTZ;

Update via cron or on action:

UPDATE identities SET trust_score = compute_score(...) WHERE id = ...;

Edge cases

Legitimate user blocked

New user wants to do the action right now. Not happy.

UX: show clear progression. "Verify your email to skip the wait."

Make verification reduce / eliminate wait.

Old account, sudden behavior change

Account is 2 years old but just started spamming. Age is fine but pattern is bad.

Combine with anomaly detection.

Migrated accounts

Imported from Auth0 with created_at = original date. Trust pre-existing accounts.

Don't over-rely

Age alone is bypassed by patient adversaries. They register, wait 7 days, then attack.

Pair with:

  • Behavior detection.
  • Action-frequency limits.
  • Manual review for high-stakes.

In Olympus

Olympus's identity.created_at is reliable (immutable). Your app reads from session.identity.created_at, present in /sessions/whoami.

For OAuth2 access tokens, include in claims:

// Hydra session-customizer
session.access_token.account_age_seconds = (Date.now() - identity.created_at) / 1000;

Backends read claim.

On this page