Skip to main content

Adding a Legacy Identity Provider

To build a more connected web, Zapf encourages developers to integrate new Legacy Identity Providers (LIDPs) into the network.

Whether you want to add GitHub, Slack, Telegram, or a custom enterprise authentication system, the process follows a standardized pattern.

Understanding the Role

When you add a LIDP, you are essentially teaching an Identity Authority (IA) how to verify ownership of that specific platform and how to construct the standardized Nostr events (Kind 35521 and Kind 35522) that prove it.

The Integration Pattern

If you are developing a new LIDP integration for the official open-source Zapf backend (or your own custom IA), you must implement these four steps:

1. The Verification Flow (OAuth or OTP)

You need to write the logic that securely proves the user owns the identifier.

For OAuth Providers (e.g., GitHub, Google, LinkedIn):

  • Register an OAuth application with the provider to get a Client ID and Secret.
  • Implement the standard OAuth 2.0 authorization code flow.
  • Ensure you request the minimum necessary scopes (usually just read:user or email).
  • Fetch the user's canonical identifier (e.g., their numeric GitHub ID or primary email).

For OTP Providers (e.g., SMS, Custom App Push):

  • Integrate a reliable delivery mechanism (like Twilio, SendGrid, or Firebase).
  • Implement rate limiting (CRITICAL) to prevent abuse.
  • Generate and verify short-lived numeric codes.

2. Identifier Normalization

Before signing any attestations, the IA must rigidly normalize the identifier to prevent spoofing.

  • Emails must be lowercased and stripped of aliases (e.g., user+spam@gmail.com -> user@gmail.com).
  • Phone numbers must be in E.164 format (e.g., +1234567890).
  • Social handles should ignore casing if the platform is case-insensitive.

3. ConnectionKey Generation

Generate the d tag (ConnectionKey) for the Nostr events:

// Example logic
rawString := fmt.Sprintf("%s:%s", lidpName, normalizedIdentifier)
connectionKey := sha256(rawString)

Note: Even if you intend to publish the raw identifier in the content field for searchability (the Public Privacy Model), the d tag must strictly be this SHA-256 hash.

4. Constructing the Evidence Payload (OAuth Only)

If your LIDP uses OAuth, you must construct an evidence object containing the Access Token and provider metadata. This is necessary for Evidence Sharing.

NIP-44 encrypt this payload directed at the user's pubkey.

5. Signing and Publishing

Finally, your IA code must construct the Kind 35522 (IA Attestation):

  • Set d to the generated ConnectionKey.
  • Set p to the user's Nostr pubkey.
  • Set lidp to your provider string (e.g., "github").
  • Specify the expiration based on the OAuth token expiry or a sensible default for OTP.

Sign the event with the IA's private key and publish it to the designated Nostr relays. Return the raw JSON to the user's frontend client so they can construct their Kind 35521 connection.