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
| Provider | Command | What Gets Stored |
|---|---|---|
| X (Twitter) | /link x | X username + user ID |
/link google | Google email + user ID | |
| Telegram | /link telegram | Telegram username + user ID |
| Wallet | /link wallet | Ethereum/EVM wallet address |
Linking an Account
Step 1: Run the Link Command
/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.
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
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:
| Column | Type | Description |
|---|---|---|
discord_user_id | TEXT | Discord snowflake ID |
provider | TEXT | x, google, telegram, or wallet |
provider_user_id | TEXT | External platform user ID |
provider_handle | TEXT | Username / handle |
privy_did | TEXT | Privy DID (optional) |
linked_at | TIMESTAMPTZ | When the link was created |
updated_at | TIMESTAMPTZ | Last 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
| Variable | Required | Description |
|---|---|---|
LINK_BASE_URL | Yes | Base URL of the link flow (https://perks.rsnc.network) |
LINK_WEBHOOK_SECRET | Yes | HMAC 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