Skip to main content

TeeTime Identity Mapping (IDP → Player)

This document describes how TeeTime resolves an authenticated user from the IDP to a TeeTime Player and how to backfill mappings for existing users.

Overview

TeeTime stores player data under the teetime-client schema where Player.id is a UUID. Authentication is handled by the IDP (e.g., Auth0), which exposes a subject (e.g., auth0|abc-123-def). To bridge these systems, the schema includes a UserIdentity table that maps (provider, subject) to a Player.id (UUID).

UserIdentity(provider, subject) ───► Player(id UUID)

The TeeTime backend exposes “me” endpoints (favorites/buddies) that resolve the current player from the JWT and then operate on that player:

  • GET /api/players/me/favorites
  • POST /api/players/me/favorites
  • DELETE /api/players/me/favorites/:clubId
  • GET /api/players/me/buddies — returns buddy relationships with contact details (buddy.firstName, buddy.lastName, buddy.fullName, buddy.email, buddy.phoneNumber)
  • POST /api/players/me/buddies
  • DELETE /api/players/me/buddies/:buddyId

These endpoints use the DB‑backed resolver to find (or create) the player.

Buddy response shape (example):

[
{
"id": "rel-123",
"playerId": "player-abc",
"buddyId": "player-def",
"createdAt": "2025-12-05T10:00:00.000Z",
"buddy": {
"id": "player-def",
"firstName": "Rory",
"lastName": "McIlroy",
"fullName": "Rory McIlroy",
"email": "rory@example.com",
"phoneNumber": "+27820000001"
}
}
]

Backfill script

Script: tools/scripts/teetime/import-seed/backfill-user-identities.mjs

Purpose:

  • Link existing IDP users to TeeTime players by (provider, subject).
  • Resolve by email when possible; optionally create the player on first use.

Usage:

export TEETIME_DATABASE_URL=postgresql://user:pass@host:5432/teetime_db?schema=public

# CSV input (subject,email,givenName,familyName,preferredName)
node tools/scripts/teetime/import-seed/backfill-user-identities.mjs \
--file identities.csv \
--provider auth0 \
--create

# NDJSON input (one JSON per line)
node tools/scripts/teetime/import-seed/backfill-user-identities.mjs \
--file identities.ndjson \
--provider idp \
--format ndjson

Dry run (preview changes):

node tools/scripts/teetime/import-seed/backfill-user-identities.mjs \
--file identities.csv \
--provider auth0 \
--dry-run

# Example output
{
"provider": "auth0",
"create": false,
"dryRun": true,
"linked": 0,
"exists": 5,
"unresolved": 1,
"wouldLink": 3,
"wouldCreate": 2
}

Prerequisite (migration):

Before running the backfill, ensure the UserIdentity table is present:

# Generate/apply migration
pnpm nx run teetime-client:prisma:migrate

# Or directly via Prisma (from repo root):
cd libs/prisma/teetime-client
pnpm exec prisma migrate dev --name add_user_identity_table

Input formats:

  • CSV headers: subject,email,givenName,familyName,preferredName
  • NDJSON fields: { subject, email, givenName, familyName, preferredName }

Behavior:

  • If (provider, subject) exists in UserIdentity, skip (status exists).
  • Else, if email matches an existing Player.email, link identity (status linked).
  • Else, when --create is present, create a Player from the provided names and link (status linked).
  • Otherwise, report unresolved.

Output example:

{
"provider": "auth0",
"create": true,
"linked": 42,
"exists": 10,
"unresolved": 3
}

Sample inputs

  • CSV (identities.csv):
subject,email,givenName,familyName,preferredName
auth0|abc-123-def,jane.doe@example.com,Jane,Doe,
idp|user-789,joe.bloggs@example.com,Joe,Bloggs,Joe B.
  • NDJSON (identities.ndjson):
{"subject":"auth0|abc-123-def","email":"jane.doe@example.com","givenName":"Jane","familyName":"Doe"}
{"subject":"idp|user-789","email":"joe.bloggs@example.com","givenName":"Joe","familyName":"Bloggs","preferredName":"Joe B."}

Operational notes

  • Run in staging first. Review unresolved entries and rerun with --create if appropriate.
  • The resolver logs links as they’re created: Linked identity <provider>:<subject> -> player <uuid>.
  • The backend “me” endpoints will succeed once an identity mapping exists. With findOrCreate enabled in the resolver, the mapping may be created on first use.

Troubleshooting

  • TEETIME_DATABASE_URL missing – ensure the env is exported before running the script.
  • Duplicate email edge cases – the backfill prefers UserIdentity exact match, then email, then create (when --create).