Skip to content

PIN Gate Authentication - Bypass Guide

Quick reference for app teams on how PIN gate authentication works and when it applies.

TL;DR

  • Internal API calls via 127.0.0.1:PORT → No PIN required (bypasses NGINX)
  • Localhost domain calls → No PIN required (auth service exempts localhost)
  • Browser requests → PIN required once per session, cookie shared across *.nominate.ai
  • Playwright tests → Need to authenticate with PIN in test setup

How It Works

All public-facing *.nominate.ai sites are protected by PIN gate authentication. When a user visits a protected site:

  1. NGINX checks for a valid session cookie
  2. If no cookie → redirect to /auth/pin
  3. User enters PIN (862509)
  4. Cookie set for .nominate.ai domain
  5. All subsequent requests to any *.nominate.ai subdomain are authenticated

When PIN is NOT Required

Internal Service-to-Service Calls

If your backend needs to call another API, use the internal address:

# ✅ Correct - bypasses NGINX, no PIN needed
response = requests.get("http://127.0.0.1:32321/api/endpoint")

# ❌ Wrong - goes through NGINX, will fail without cookie
response = requests.get("https://ky04api.nominate.ai/api/endpoint")

Port Reference

Service Internal Address
tenant-manager-frontend 127.0.0.1:32200
tenant-manager API 127.0.0.1:32201
auth service 127.0.0.1:32202
testsite frontend 127.0.0.1:32300
testsite API 127.0.0.1:32301
mi20 frontend 127.0.0.1:32310
mi20 API 127.0.0.1:32311
ky04 frontend 127.0.0.1:32320
ky04 API 127.0.0.1:32321

Playwright Testing

Playwright runs in a browser context and will hit the PIN gate. Options:

// In your test setup / beforeAll
test.beforeAll(async ({ page }) => {
  await page.goto('https://yoursite.nominate.ai/auth/pin');
  await page.fill('input[name="pin"]', '862509');
  await page.click('button[type="submit"]');
  // Cookie is now set for all *.nominate.ai
});

Option 2: Reuse Auth State

// Save auth state after first login
await page.context().storageState({ path: 'auth.json' });

// Reuse in other tests
const context = await browser.newContext({ storageState: 'auth.json' });

Questions?

Contact the infrastructure team or check cbauth INFRA-GUIDE.


Last updated: 2026-01-02