diff --git a/docs/base-account/guides/verify-social-accounts.mdx b/docs/base-account/guides/verify-social-accounts.mdx new file mode 100644 index 000000000..87122d800 --- /dev/null +++ b/docs/base-account/guides/verify-social-accounts.mdx @@ -0,0 +1,886 @@ +--- +title: "Verify Social Accounts" +description: "Use Base Verify to let users prove ownership of verified accounts (X, Coinbase, Instagram, TikTok) without sharing credentials, enabling Sybil-resistant airdrops, gated content, and identity-based rewards." +--- + +## What is Base Verify? + +Base Verify allows users to prove ownership of verified accounts (X, Coinbase, Instagram, TikTok) without sharing credentials. Your app receives a deterministic token for Sybil resistance. + +Even if a wallet has few transactions, Base Verify reveals whether the user is high-value through their verified social accounts (X Blue, Instagram, TikTok) or Coinbase One subscription. This lets you identify quality users regardless of onchain activity. + +**Example use cases:** + +- Token-gated airdrops or daily rewards +- Exclusive content access (e.g., creator coins) +- Identity-based rewards and loyalty programs + +--- + +## Core concepts + +### Provider + +An identity platform that Base Verify integrates with. Currently supports **X (Twitter)**, **Coinbase**, **Instagram**, and **TikTok**. + +### Verification + +Cryptographic proof that a wallet owns an account with a specific provider. + +### Trait + +A specific attribute of the provider account that can be verified. + +**Examples:** + +- `verified: true` — X account has blue checkmark +- `coinbase_one_active: true` — active Coinbase One subscription +- `followers: gt:1000` — X account has over 1,000 followers +- `followers_count: gte:5000` — Instagram account with 5,000+ followers +- `video_count: gte:50` — TikTok account with 50+ videos + +### Action + +A developer-defined string that identifies what the user is doing with their verification. Actions let you issue different tokens for different use cases within the same app. + +**Examples:** + +- `claim_daily_reward` — claiming a daily reward +- `join_allowlist` — joining an exclusive allowlist +- `unlock_premium_content` — accessing gated content +- `participate_in_raffle` — entering a raffle + +#### How actions work + +Actions are specified in the SIWE message resources: + +```typescript Title "SIWE resources with action" +resources: [ + 'urn:verify:provider:x', + 'urn:verify:action:claim_daily_reward' +] +``` + +The action is returned in the API response: + +```json Title "API response with action" +{ + "token": "abc123...", + "action": "claim_daily_reward", + "wallet": "0x1234..." +} +``` + +#### Why actions matter + +**Different actions produce different tokens.** This enables multiple independent claims from the same verified account: + +- User verifies X account with action `claim_airdrop` → Token: `abc123` +- Same X account with action `join_allowlist` → Token: `def456` (different) +- Same X account with action `claim_airdrop` again → Token: `abc123` (same as first) + +**Use cases:** + +- **Multiple campaigns** — run separate airdrops without interference +- **Feature gating** — different tokens for different premium features +- **Time-based events** — new action per event (e.g., `raffle_jan_2025`, `raffle_feb_2025`) + +#### Choosing action names + +Use descriptive, lowercase names with underscores: + +| Good | Bad | +| :--- | :--- | +| `claim_genesis_airdrop` | `airdrop` (too generic) | +| `unlock_pro_features` | `action1` (meaningless) | +| `enter_weekly_raffle` | `base_verify_token` (reserved/confusing) | + + +Once you launch with an action name, don't change it. Changing the action generates different tokens for the same users, breaking your Sybil resistance. + + +### Token — Sybil resistance + +A deterministic identifier tied to the provider account, not the wallet. **This is the key anti-Sybil mechanism.** + +#### How it works + +1. Wallet A verifies an X account → Base Verify returns `Token: abc123` → you have never seen it, so grant the airdrop. +2. The same X account tries again with Wallet B → Base Verify returns `Token: abc123` → you have seen it, so block the duplicate claim. + +Without Base Verify, users could claim multiple times with different wallets. With Base Verify, one verified account = one token = one claim. + +#### Token properties + +- **Deterministic** — the same provider account always produces the same token +- **Unique per provider** — a user's X token is different from their Instagram token +- **Unique per app** — your app receives different tokens than other apps (privacy) +- **Action-specific** — tokens vary based on the action in your SIWE message +- **Persistent** — tokens don't expire or rotate (unless the user deletes their verification) +- **Trait-independent** — tokens stay the same even if traits change (e.g., follower count increases) + +#### How to store tokens + +```typescript Title "Token storage schema" +{ + token: "abc123...", + walletAddress: "0x1234...", + provider: "x", + claimedAt: "2024-01-15", +} +``` + +#### Prevent double claims + +```typescript Title "Double claim prevention" expandable lines +async function claimAirdrop(verificationToken: string, walletAddress: string) { + const existingClaim = await db.findClaimByToken(verificationToken); + + if (existingClaim) { + return { error: "This X account already claimed" }; + } + + await db.createClaim({ + token: verificationToken, + wallet: walletAddress, + claimedAt: new Date() + }); + + return { success: true }; +} +``` + +--- + +## Architecture and flow + +``` + ┌─────────────┐ + │ │ 1. User connects wallet + │ Your │ + │ Mini App │ + │ (Frontend) │ + └──────┬──────┘ + │ + │ 2. App generates SIWE message (frontend) + │ • Includes wallet address + │ • Includes provider (x, coinbase, instagram, tiktok) + │ • Includes traits (verified:true, followers:gt:1000) + │ • Includes action (e.g. claim_airdrop) + │ + │ 3. User signs SIWE message with wallet + │ + │ 4. Send signature + message to YOUR backend + │ + ▼ + ┌──────────────┐ + │ Mini App │ • Validates trait requirements + │ Backend │ • Verifies signature with Base Verify API + │ (Your API) │ + └──────┬───────┘ + │ + ▼ + + 200 OK ←───────┌──────────────────┐───────→ 400 + Verified! │ │ User has account + (DONE) │ Base Verify API │ but traits not met + │ verify.base.dev │ (DONE) + └────────┬─────────┘ + │ + │ 404 Not Found + ▼ + + 5. Redirect to Base Verify Mini App + │ + ▼ + ┌──────────────────────┐ + │ Base Verify │ 6. User completes OAuth + │ Mini App │ (X, Coinbase, Instagram, TikTok) + │ verify.base.dev │ 7. Base Verify stores verification + └──────────┬───────────┘ + │ + │ 8. Redirects back to your app + ▼ + ┌─────────────┐ + │ Your │ 9. Check again (step 4) + │ Mini App │ → Now returns 200 or 400 + └─────────────┘ +``` + +### Your app's responsibilities + +- Generate SIWE messages with trait requirements +- Handle user wallet connection +- Redirect to the Base Verify Mini App when verification is not found +- Store the returned verification token to prevent reuse +- Keep your secret key secure on the backend + +### Base Verify's responsibilities + +- Validate SIWE signatures +- Store provider verifications (X, Coinbase, Instagram, TikTok) +- Check if verification meets trait requirements +- Facilitate OAuth flow with providers +- Return deterministic tokens for Sybil resistance + +### Response codes + +| Code | Meaning | Action | +| :--- | :--- | :--- | +| **200 OK** | Wallet has verified the provider account AND meets all trait requirements. Returns a unique token. | Grant access, store the token. | +| **404 Not Found** | Wallet has never verified this provider. | Redirect user to the Base Verify Mini App. | +| **400 Bad Request** (`verification_traits_not_satisfied`) | Wallet has verified the provider, but doesn't meet the trait requirements. | Show user they don't meet requirements. Do **not** redirect. | + +--- + +## Getting started + +### Prerequisites + +1. **API key** — fill out the [interest form](https://forms.gle/6L4hWAHkojYcefz27) to get access +2. **Wallet integration** — users must be able to connect and sign messages +3. **Backend server** — to securely call the Base Verify API + +### Register your app + +Provide the Base Verify team: + +1. Your **Mini App domain** +2. Your **redirect URI** — where users return after verification (e.g., `https://yourapp.com`) + + +Your secret key must **never** be exposed in frontend code. All Base Verify API calls must go through your backend. + + +--- + +## Implementation + + + + +Create a configuration file for your Base Verify integration: + +```typescript lib/config.ts highlight={3} +export const config = { + appUrl: 'https://your-app.com', + baseVerifySecretKey: process.env.BASE_VERIFY_SECRET_KEY, + baseVerifyApiUrl: 'https://verify.base.dev/v1', + baseVerifyMiniAppUrl: 'https://verify.base.dev', +} +``` + +Add your secret key to `.env.local`: + +```shell .env.local +BASE_VERIFY_SECRET_KEY=your_secret_key_here +``` + + + + + +Build a SIWE message that includes the provider, trait requirements, and action: + +```typescript lib/signature-generator.ts expandable lines highlight={7-12,15-24} +import { SiweMessage, generateNonce } from 'siwe' +import { config } from './config' + +export async function generateSignature( + signMessageFunction: (message: string) => Promise, + address: string +) { + const resources = [ + 'urn:verify:provider:x', + 'urn:verify:provider:x:verified:eq:true', + 'urn:verify:provider:x:followers:gte:100', + 'urn:verify:action:claim_airdrop' + ] + + const siweMessage = new SiweMessage({ + domain: new URL(config.appUrl).hostname, + address, + statement: 'Verify your X account', + uri: config.appUrl, + version: '1', + chainId: 8453, + nonce: generateNonce(), + issuedAt: new Date().toISOString(), + expirationTime: new Date(Date.now() + 6 * 60 * 60 * 1000).toISOString(), + resources, + }) + + const message = siweMessage.prepareMessage() + const signature = await signMessageFunction(message) + + return { message, signature, address } +} +``` + + + + + +The frontend generates the signature and sends it to **your** backend, which calls the Base Verify API. + +**Frontend:** + +```typescript Title "Frontend verification check" expandable lines highlight={2-8,11-19} +async function checkVerification(address: string) { + const signature = await generateSignature( + async (msg) => { + return new Promise((resolve, reject) => { + signMessage( + { message: msg }, + { onSuccess: resolve, onError: reject } + ) + }) + }, + address + ) + + const response = await fetch('/api/check-verification', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + signature: signature.signature, + message: signature.message, + address: address + }) + }) + + const data = await response.json(); + return data; +} +``` + +**Backend (your API endpoint):** + +```typescript pages/api/check-verification.ts expandable lines highlight={5-8,15,19-24} +import { validateTraits } from '../../lib/trait-validator'; + +export default async function handler(req, res) { + const { signature, message, address } = req.body; + + const expectedTraits = { + 'verified': 'true', + 'followers': 'gte:100' + }; + + const validation = validateTraits(message, 'x', expectedTraits); + + if (!validation.valid) { + return res.status(400).json({ + error: 'Invalid trait requirements in message', + details: validation.error + }); + } + + const response = await fetch('https://verify.base.dev/v1/base_verify_token', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${process.env.BASE_VERIFY_SECRET_KEY}`, + }, + body: JSON.stringify({ + signature: signature, + message: message, + }) + }); + + if (response.ok) { + const data = await response.json(); + return res.status(200).json({ verified: true, token: data.token }); + } else if (response.status === 404) { + return res.status(404).json({ verified: false, needsVerification: true }); + } else if (response.status === 400) { + const data = await response.json(); + if (data.message === 'verification_traits_not_satisfied') { + return res.status(400).json({ verified: false, traitsNotMet: true }); + } + } + + return res.status(500).json({ error: 'Verification check failed' }); +} +``` + + +Your backend **must** validate that the trait requirements in the SIWE message match what your backend expects. This prevents users from modifying trait requirements on the frontend to bypass your access controls. See [security best practices](#validate-trait-requirements) for details. + + + + + + +If you receive a 404 response, redirect the user to the Base Verify Mini App to complete OAuth: + +```typescript Title "Redirect to Base Verify Mini App" highlight={3-6,9} +function redirectToVerifyMiniApp(provider: string) { + const params = new URLSearchParams({ + redirect_uri: config.appUrl, + providers: provider, + }) + + const miniAppUrl = `${config.baseVerifyMiniAppUrl}?${params.toString()}` + + const deepLink = `cbwallet://miniapp?url=${encodeURIComponent(miniAppUrl)}` + window.open(deepLink, '_blank') +} +``` + +After verification, the user returns to your `redirect_uri` with `?success=true`. Run the check again (step 3) — it now returns 200 with a token. + + + + +### Error handling + +| Response | What to do | +| :--- | :--- | +| **404** | User hasn't verified. Redirect to the Base Verify Mini App. | +| **400** (`verification_traits_not_satisfied`) | User has account but doesn't meet requirements. Show a message — don't redirect and don't retry. | +| **200** | Store the token and grant access. | + + +Do **not** retry 404 responses — the user simply hasn't verified yet. Do **not** retry 400 responses with `verification_traits_not_satisfied` — retrying won't help unless the user's account metrics change (e.g., they gain more followers). + + +--- + +## API reference + +### Authentication + +All API requests require your secret key in the `Authorization` header: + +```typescript Title "Authorization header" +Authorization: Bearer YOUR_SECRET_KEY +``` + +### POST /v1/base_verify_token + +Check if a wallet has a specific verification and retrieve the verification token. + +#### Request + +```typescript Title "Request body" +{ + signature: string, // SIWE signature from wallet + message: string // SIWE message (includes provider/traits in resources) +} +``` + +#### Example request + +```bash Title "cURL example" wrap +curl -X POST https://verify.base.dev/v1/base_verify_token \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer YOUR_SECRET_KEY" \ + -d '{ + "signature": "0x1234...", + "message": "verify.base.dev wants you to sign in..." + }' +``` + +#### Responses + +**200 OK — verified:** + +```json Title "Success response" +{ + "token": "abc123...", + "action": "claim_airdrop", + "wallet": "0x1234..." +} +``` + +| Field | Type | Description | +| :--- | :--- | :--- | +| `token` | string | Deterministic verification token for Sybil resistance. Same provider account + same action = same token. | +| `action` | string | The custom action specified in the SIWE message. Different actions produce different tokens. | +| `wallet` | string | User's wallet address. | + +**404 Not Found — verification not found:** + +```json Title "Not found response" +{ + "error": "verification_not_found" +} +``` + +Redirect the user to the Base Verify Mini App to complete verification. + +**400 Bad Request — traits not satisfied:** + +```json Title "Traits not satisfied response" +{ + "code": 9, + "message": "verification_traits_not_satisfied", + "details": [] +} +``` + +The user has the provider account but doesn't meet trait requirements. Do not redirect. + +**401 Unauthorized — invalid key:** + +```json Title "Unauthorized response" +{ + "error": "unauthorized" +} +``` + +Check that your secret key is correct and included in the Authorization header. + +### Mini App redirect + +To redirect users to Base Verify for verification: + +```typescript Title "Redirect URL format" +https://verify.base.dev?redirect_uri={your_app_url}&providers={provider} +``` + +| Parameter | Required | Description | Example | +| :--- | :--- | :--- | :--- | +| `redirect_uri` | Yes | Where to send the user after verification | `https://yourapp.com` | +| `providers` | Yes | Provider to verify | `x`, `coinbase`, `instagram`, `tiktok` | + +--- + +## Trait catalog + +Traits are specific attributes of a provider account that you can verify. They are specified in SIWE message resources using this format: + +```typescript Title "Trait URN syntax" +urn:verify:provider:{provider}:{trait_name}:{operation}:{value} +``` + +### Operations + +| Operation | Symbol | Applies to | Description | Example | +| :--- | :--- | :--- | :--- | :--- | +| Equals | `eq` | All types | Exact match | `verified:eq:true` | +| Greater than | `gt` | Integers | Strictly greater | `followers:gt:1000` | +| Greater/equal | `gte` | Integers | Greater or equal | `followers:gte:1000` | +| Less than | `lt` | Integers | Strictly less | `followers:lt:5000` | +| Less/equal | `lte` | Integers | Less or equal | `followers:lte:5000` | +| In (list) | `in` | Strings | Value in comma-separated list | `verified_type:in:blue,government` | + +### Type system + +**Boolean traits:** + +- Values: `"true"` or `"false"` (as strings) +- Only supports `eq` operation +- Example: `verified:eq:true` + +**Integer traits:** + +- Values: numbers as strings +- Supports: `eq`, `gt`, `gte`, `lt`, `lte` +- Example: `followers:gte:1000` + +**String traits:** + +- Values: text strings +- Supports: `eq`, `in` +- Example: `verified_type:eq:blue` or `verified_type:in:blue,government` + +### Combining traits + +When you specify multiple traits for the same provider, **all** must be satisfied (AND logic): + +```typescript Title "Multiple traits (AND logic)" +resources: [ + 'urn:verify:provider:x', + 'urn:verify:provider:x:verified:eq:true', + 'urn:verify:provider:x:followers:gte:10000' +] +``` + + +You can only check one provider per request. To check multiple providers, make separate API calls. + + +### Common patterns + +**Tiered access:** + +```typescript Title "Tiered access example" +// Bronze tier: any verified account +traits: { 'verified': 'true' } + +// Silver tier: 1k+ followers +traits: { 'followers': 'gte:1000' } + +// Gold tier: 10k+ followers +traits: { 'followers': 'gte:10000' } +``` + +--- + +### Coinbase + +**Provider:** `coinbase` + +| Trait | Type | Operations | Description | Example values | +| :--- | :--- | :--- | :--- | :--- | +| `coinbase_one_active` | Boolean | `eq` | Active Coinbase One subscription | `"true"`, `"false"` | +| `coinbase_one_billed` | Boolean | `eq` | User has been billed for Coinbase One | `"true"`, `"false"` | + +```typescript Title "Coinbase trait examples" +// Check for Coinbase One subscribers +{ + provider: 'coinbase', + traits: { 'coinbase_one_active': 'true' } +} + +// Check for billed Coinbase One subscribers +{ + provider: 'coinbase', + traits: { 'coinbase_one_billed': 'true' } +} +``` + +### X (Twitter) + +**Provider:** `x` + +| Trait | Type | Operations | Description | Example values | +| :--- | :--- | :--- | :--- | :--- | +| `verified` | Boolean | `eq` | Has any type of verification | `"true"`, `"false"` | +| `verified_type` | String | `eq` | Type of verification | `"blue"`, `"government"`, `"business"`, `"none"` | +| `followers` | Integer | `eq`, `gt`, `gte`, `lt`, `lte` | Number of followers | `"1000"`, `"50000"` | + +```typescript Title "X trait examples" expandable lines +// Check for any verified account +{ + provider: 'x', + traits: { 'verified': 'true' } +} + +// Check for specific verification type +{ + provider: 'x', + traits: { 'verified_type': 'blue' } +} + +// Check for follower count (greater than or equal to) +{ + provider: 'x', + traits: { 'followers': 'gte:1000' } +} + +// Combine multiple traits +{ + provider: 'x', + traits: { + 'verified': 'true', + 'followers': 'gte:10000' + } +} +``` + +### Instagram + +**Provider:** `instagram` + +| Trait | Type | Operations | Description | Example values | +| :--- | :--- | :--- | :--- | :--- | +| `username` | String | `eq` | Instagram username | `"john_doe"` | +| `followers_count` | Integer | `eq`, `gt`, `gte`, `lt`, `lte` | Number of followers | `"1000"`, `"50000"` | +| `instagram_id` | String | `eq` | Unique Instagram user ID | `"1234567890"` | + +```typescript Title "Instagram trait examples" expandable lines +// Check for follower count (greater than) +{ + provider: 'instagram', + traits: { 'followers_count': 'gt:1000' } +} + +// Check for follower count (greater than or equal to) +{ + provider: 'instagram', + traits: { 'followers_count': 'gte:5000' } +} + +// Combine multiple traits +{ + provider: 'instagram', + traits: { + 'username': 'john_doe', + 'followers_count': 'gte:10000' + } +} +``` + +### TikTok + +**Provider:** `tiktok` + +| Trait | Type | Operations | Description | Example values | +| :--- | :--- | :--- | :--- | :--- | +| `open_id` | String | `eq` | TikTok Open ID (unique per app) | `"abc123..."` | +| `union_id` | String | `eq` | TikTok Union ID (unique across apps) | `"def456..."` | +| `display_name` | String | `eq` | TikTok display name | `"John Doe"` | +| `follower_count` | Integer | `eq`, `gt`, `gte`, `lt`, `lte` | Number of followers | `"1000"`, `"50000"` | +| `following_count` | Integer | `eq`, `gt`, `gte`, `lt`, `lte` | Number of accounts following | `"500"`, `"2000"` | +| `likes_count` | Integer | `eq`, `gt`, `gte`, `lt`, `lte` | Total likes received | `"10000"`, `"100000"` | +| `video_count` | Integer | `eq`, `gt`, `gte`, `lt`, `lte` | Number of videos posted | `"50"`, `"200"` | + +```typescript Title "TikTok trait examples" expandable lines +// Check for follower count +{ + provider: 'tiktok', + traits: { 'follower_count': 'gt:1000' } +} + +// Check for likes count +{ + provider: 'tiktok', + traits: { 'likes_count': 'gte:10000' } +} + +// Combine multiple traits (e.g., active creator) +{ + provider: 'tiktok', + traits: { + 'follower_count': 'gte:5000', + 'likes_count': 'gte:100000', + 'video_count': 'gte:100' + } +} +``` + +--- + +## Security and privacy + +### SIWE signature requirement + +Every API call requires a valid SIWE signature from the wallet owner. This prevents: + +- Arbitrary lookup of verification status +- Third parties checking if a wallet is verified +- Enumeration attacks + +The user signs a structured message proving they control the wallet and agree to check specific traits: + +```typescript Title "SIWE payload example" +{ + domain: "your-app.com", + address: "0x1234...", + chainId: 8453, + resources: [ + "urn:verify:provider:x", + "urn:verify:provider:x:verified:eq:true", + "urn:verify:action:claim_airdrop" + ] +} +``` + +### Validate trait requirements + + +When your backend receives a SIWE message from the frontend, you **must** validate that the trait requirements in the message match what your backend expects. This prevents users from modifying trait requirements on the frontend to bypass your access controls. + + +**Example attack without validation:** + +1. Your app requires users to have 100 followers +2. User modifies the frontend to request only 10 followers +3. User signs the modified message +4. Without validation, your backend forwards the request to Base Verify +5. User gains access with fewer than 100 followers + +**Implementation:** + +```typescript Title "Backend trait validation" lines highlight={2-4,7} +import { validateTraits } from './lib/trait-validator'; + +const expectedTraits = { + 'followers': 'gte:100' +}; + +const validation = validateTraits(message, 'x', expectedTraits); + +if (!validation.valid) { + return res.status(400).json({ + error: 'Invalid trait requirements in message', + details: validation.error + }); +} +``` + +### Protect your secret key + +**Never:** + +- Include the secret key in frontend code +- Use `NEXT_PUBLIC_*` or similar environment variables that expose to the browser +- Commit secret keys to version control +- Share secret keys in chat, email, or documentation + +**Always:** + +- Store the secret key in backend environment variables only +- Use `.env` files that are gitignored +- Rotate keys immediately if accidentally exposed +- Call the Base Verify API only from your backend + +### OAuth security model + +Base Verify validates provider accounts through OAuth: + +1. User initiates OAuth in the Base Verify Mini App +2. Provider (X, Instagram, etc.) authenticates the user +3. Provider returns an OAuth token to Base Verify +4. Base Verify fetches account data using the OAuth token +5. Base Verify stores the verification linked to the user's wallet +6. OAuth token is encrypted and stored securely + +Your app never handles OAuth tokens or redirects — this is all handled within the Base Verify Mini App. + +### Data storage + +**What Base Verify stores:** + +- Wallet addresses associated with verified provider accounts +- Provider account metadata (username, follower counts, verification status) +- OAuth tokens (encrypted, never shared with apps) +- Verification timestamps + +**What Base Verify does not store:** + +- Users' private keys +- Provider account passwords +- User activity or browsing history +- Any data beyond what's needed for verification + +**What your app receives:** + +When you call `/v1/base_verify_token`, you receive only `token`, `action`, and `wallet`. No PII is returned. + +### User control + +Users can delete their verifications at any time: + +- Removes all stored provider data +- Invalidates future token generation +- Your app's stored tokens become meaningless (user can't re-verify with the same account) + +### Caching + +Cache verification results to reduce API calls: + +- Cache for the user's session (not permanently) +- Clear cache when the user disconnects their wallet +- Don't check verification on every page load + +--- + +## Support + +**Want to integrate Base Verify?** Fill out the [interest form](https://forms.gle/6L4hWAHkojYcefz27) and the team will reach out with API access. diff --git a/docs/docs.json b/docs/docs.json index 1e9c9816f..814d20524 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -230,6 +230,7 @@ "base-account/improve-ux/spend-permissions", "base-account/improve-ux/magic-spend", "base-account/guides/sign-and-verify-typed-data", + "base-account/guides/verify-social-accounts", "base-account/improve-ux/sponsor-gas/erc20-paymasters" ] }, diff --git a/docs/mini-apps/core-concepts/base-account.mdx b/docs/mini-apps/core-concepts/base-account.mdx index ca02f9d56..bee5bb5d4 100644 --- a/docs/mini-apps/core-concepts/base-account.mdx +++ b/docs/mini-apps/core-concepts/base-account.mdx @@ -149,6 +149,36 @@ The next section lists the methods and capabilities that are not supported in Mi --- +## Verify social accounts with Base Verify + +[Base Verify](/base-account/guides/verify-social-accounts) lets your Mini App users prove ownership of verified accounts (X, Coinbase, Instagram, TikTok) without sharing credentials. You receive a deterministic token for Sybil resistance — one verified account produces the same token regardless of which wallet the user connects with. + +**How it works:** + +1. Check if the wallet has a verification → API returns yes/no +2. If no → redirect to the Base Verify Mini App for OAuth +3. User verifies → returns to your app +4. Check again → now verified, you receive a token + +**Quick example — check if a user has a verified X account:** + +```typescript Title "Base Verify check" highlight={1-4,7-8} +const resources = [ + 'urn:verify:provider:x', + 'urn:verify:provider:x:verified:eq:true', + 'urn:verify:action:claim_airdrop' +] + +// Generate SIWE message with these resources, sign it, +// then send to your backend to call the Base Verify API +``` + + + Complete walkthrough covering all providers, traits, API reference, security, and implementation details. + + +--- + ## Unsupported Methods and Capabilities The following methods and capabilities are not yet supported in Mini Apps but will be added soon: @@ -157,10 +187,7 @@ The following methods and capabilities are not yet supported in Mini Apps but wi |---------|------------------| | Sign in with Base | `wallet_connect` | | Sub accounts | `wallet_getSubAccounts`, `wallet_addSubAccount` | -| Spend permissions | `coinbase_fetchPermissions`, `coinbase_fetchPermission` | | Profiles | `datacallback` | -| Signing typed data | `signTypedData` | -| Signing messages | `wallet_sign` | All other standard Ethereum and Base Account RPC methods work as expected.