Skip to main content

Account Linking

Connect your Discord identity to external accounts (X/Twitter, Google, Telegram, or a Web3 wallet) using the /link command. Linked accounts unlock cross-platform rewards and on-chain perks.

Supported Providers

ProviderCommandWhat Gets Stored
X (Twitter)/link xX username + user ID
Google/link googleGoogle email + user ID
Telegram/link telegramTelegram username + user ID
Wallet/link walletEthereum/EVM wallet address

Linking an Account

/link x
/link google
/link telegram
/link wallet

The bot sends you an ephemeral message (visible only to you) with a "Connect" button.

Step 2: Click Connect

The button opens perks.rsnc.network in your browser, pre-loaded with:

  • Your Discord user ID
  • The provider you selected
  • A one-time session nonce (expires in 5 minutes)

Step 3: Authenticate

Complete the OAuth flow on the provider's site (e.g., authorize the X app, log in to Telegram). The Resonance app confirms the connection.

Step 4: Done

The bot sends you a DM confirming the link. Your account is now connected.

Session Nonce Security

Each link session uses a single-use 64-character random nonce stored in KV with a 5-minute TTL. The nonce is deleted immediately after use — replaying it returns an error.


Viewing Linked Accounts

/link view

Shows all currently linked accounts for your Discord user:

🔗 Your Linked Accounts

X (Twitter): @your_handle
Telegram: @your_telegram
Wallet: 0xAbCd...1234

Use /link unlink <provider> to remove a connection.

Unlinking an Account

/link unlink x
/link unlink telegram
/link unlink wallet

Removes the connection. You can re-link at any time.


How It Works (Technical)

1. /link x
→ Bot generates nonce → stored in KV as link_session:{nonce} (5 min TTL)
→ Bot returns ephemeral message with URL:
https://perks.rsnc.network/link?session={nonce}&provider=x&discord_id={userId}

2. User completes OAuth at perks.rsnc.network

3. perks.rsnc.network POSTs to the bot:
POST /webhook/link-confirm
X-Resonance-Signature: hmac-sha256(LINK_WEBHOOK_SECRET, body)
Body: { session_nonce, discord_user_id, provider, provider_user_id, provider_handle, privy_did? }

4. Bot verifies HMAC → validates nonce → deletes nonce (one-time use)
→ Upserts row in discord_linked_accounts (Supabase)
→ Sends DM to user confirming the link

Webhook Security

The /webhook/link-confirm endpoint requires an X-Resonance-Signature header — an HMAC-SHA256 of the raw request body signed with the LINK_WEBHOOK_SECRET environment secret. Requests without a valid signature return 401 Unauthorized.

Set the secret:

wrangler secret put LINK_WEBHOOK_SECRET
Legacy Alias

POST /webhook/privy-link is supported as a legacy alias and routes to the same handler.


Supabase Schema

Linked accounts are stored in the discord_linked_accounts table:

ColumnTypeDescription
discord_user_idTEXTDiscord snowflake ID
providerTEXTx, google, telegram, or wallet
provider_user_idTEXTExternal platform user ID
provider_handleTEXTUsername / handle
privy_didTEXTPrivy DID (optional)
linked_atTIMESTAMPTZWhen the link was created
updated_atTIMESTAMPTZLast update time

The table has a composite unique constraint on (discord_user_id, provider) — one row per provider per user. Re-linking upserts (updates) the existing row.

Run the Migration

If the table doesn't exist yet, run in Supabase SQL Editor:

-- discord-bot/sql/discord_linked_accounts.sql

Environment Variables

VariableRequiredDescription
LINK_BASE_URLYesBase URL of the link flow (https://perks.rsnc.network)
LINK_WEBHOOK_SECRETYesHMAC secret for webhook signature verification

Set LINK_BASE_URL in wrangler.toml:

[vars]
LINK_BASE_URL = "https://perks.rsnc.network"

Set LINK_WEBHOOK_SECRET as a Worker secret:

wrangler secret put LINK_WEBHOOK_SECRET