Skip to main content
Documentation - Discord Docs

Search documentation

Type to search this documentation.

On this pageOverview

Account Linking on Mobile

This guide explains how to implement OAuth2 authentication for mobile users (iOS and Android), allowing them to link their Discord accounts with your game on mobile devices.

Mobile authentication uses deep linking to provide a seamless experience. When the Discord mobile app is installed, users are automatically redirected to Discord to authorize your game, then returned to your game via a custom URL scheme.

Mobile platforms have unique requirements that differ from desktop:

  • Custom URL schemes (deep links) are required instead of HTTP redirects
  • PKCE (Proof Key for Code Exchange) is mandatory for all mobile apps using deep links, regardless of whether you're using a public or confidential client
  • Platform-specific configuration is required (Info.plist for iOS, AndroidManifest.xml for Android)

Before you begin, make sure you have:

  • Read the Core Concepts guide to understand:
    • OAuth2 authentication flow
    • Discord application setup
    • SDK initialization
  • Set up your development environment with:

If you haven't completed these prerequisites, we recommend first following the Getting Started guide.


For OAuth2 to work correctly on mobile, you must first register the correct redirect URI for your app in the Discord Developer Portal.

Platform Redirect URI
Mobile (iOS & Android) discord-YOUR_APP_ID:/authorize/callback (replace YOUR_APP_ID with your Discord application ID)

Please follow the Unity Getting Started guide for general setup instructions. There are additional mobile-specific configurations you'll need to complete:

1. Configure URL Scheme in Unity Project Settings

Section titled “1. Configure URL Scheme in Unity Project Settings”

To enable Client::Authorize support, configure a callback URL scheme:

  1. Open Project Settings: Edit -> Project Settings...
  2. Navigate to the Player section
  3. Select the iOS tab
  4. Under Other Settings, locate Supported URL Schemes
  5. Add discord-YOUR_APP_ID to the list (e.g., if your application ID is 123456, add discord-123456)

In the same Player settings page:

  1. Set Microphone Usage Description to a valid description
  2. This string will be displayed by iOS when microphone permissions are requested
  3. Required for voice support

For native authentication to work, you must update your Info.plist to include the discord scheme:

<key>LSApplicationQueriesSchemes</key>
<array>
  <string>discord</string>
</array>

This allows your app to detect if the Discord mobile app is installed and deep-link into it for authentication.

4. Enable Background Voice Support (Optional)

Section titled “4. Enable Background Voice Support (Optional)”

To enable voice support while your game is backgrounded:

  1. Edit your Info.plist to enable the appropriate background modes
  2. A build postprocessor is supplied in the Unity sample project that you may copy into your own project, located at Assets/Scripts/Editor/VoicePostBuildProcessor.cs

Client::Authorize requires an activity with a custom URL scheme to be added to your application manifest:

  1. An example build processor is provided in the Unity sample project at Assets/Scripts/Editor/AndroidPostBuildProcessor.cs
  2. Alternatively, manually add an intent filter to your AndroidManifest.xml:
<activity android:name="com.discord.socialsdk.AuthenticationActivity"
android:exported="true">
  <intent-filter>
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    <data android:scheme="discord-YOUR_APP_ID" />
  </intent-filter>
</activity>

Replace YOUR_APP_ID with your actual Discord application ID (e.g., discord-1234567890123456789).

Authorization requires androidx.browser as a Gradle dependency:

  • If you use Google External Dependency Manager in your project, a suitable dependencies XML file is provided as part of the Unity plugin
  • Otherwise, you'll need to add this dependency manually to your build.gradle:
dependencies {
    implementation 'androidx.browser:browser:1.8.0'
    // Your other dependencies...
}

The Android SDK uses the following permissions:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.BLUETOOTH" /> <!-- SDK <= 30 -->
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" /> <!-- SDK >= 31 -->

Please follow the Unreal Engine Getting Started guide for general setup instructions. For mobile-specific configuration, you only need to configure your Discord Application ID in the project settings.

To enable mobile authentication in Unreal Engine:

  1. Open Project Settings (Edit -> Project Settings...)
  2. Search for discord in the search bar
  3. Under the Discord Social SDK section, enter your Discord Application ID

Unreal Engine Discord Application ID Setting

This setting configures the custom URL scheme (discord-YOUR_APP_ID) for deep linking on both iOS and Android platforms automatically.


The Discord Social SDK may be used as a C++ library in a standard iOS or Android project. Follow the steps below for your platform:

  1. Create an Objective-C iOS project in Xcode
  2. Add discord_partner_sdk.xcframework to your project
  1. Add the xcframework to Build Phases -> Link Binary with Libraries if needed
  2. In the General tab, under Frameworks, Libraries and Embedded Content:
    • Set discord_partner_sdk.xcframework to Embed & Sign

To maintain voice connectivity while backgrounded:

  1. Configure background audio modes in your Info.plist using the Signing & Capabilities tab
  2. Select Audio, AirPlay, and Picture in Picture, not Voice over IP
  3. See Configuring your app for Media Playback for detailed instructions

To enable Client::Authorize support, register the appropriate URL scheme in your Info.plist:

<key>CFBundleURLTypes</key>
<array>
    <dict>
        <key>CFBundleURLSchemes</key>
        <array>
            <string>discord-YOUR_APP_ID</string>
        </array>
    </dict>
</array>
<key>LSApplicationQueriesSchemes</key>
<array>
    <string>discord</string>
</array>

Replace YOUR_APP_ID with your application ID from the Discord Developer Portal (e.g., if your app ID is 123456, register discord-123456).

In a C++ or Objective-C++ (.mm) source file:

C++
#define DISCORDPP_IMPLEMENTATION // Define this in exactly ONE file
#include <discord_partner_sdk/discordpp.h>

In your main game loop, make sure to call:

C++
discordpp::RunCallbacks();

Create an Android game project based on the Game Activity (C++) template.

Add discord_partner_sdk.aar as a dependency in your Gradle project:

  1. Add the AAR to a directory (e.g., app/libs)
  2. In your app/build.gradle, add to dependencies:
dependencies {
    implementation files("libs/discord_partner_sdk.aar")
}

Ensure Prefab is enabled in your Gradle build. See Native Dependencies in AARs for details.

In your CMakeLists.txt:

find_package(discord_partner_sdk REQUIRED CONFIG)
target_link_libraries(your_target discord_partner_sdk::discord_partner_sdk)

In your C++ source:

C++
#define DISCORDPP_IMPLEMENTATION // Define this in exactly ONE file
#include "discordpp.h"

In the onCreate method for your main activity (Java/Kotlin):

com.discord.socialsdk.DiscordSocialSdkInit.setEngineActivity(this);

In your main C++ loop:

C++
discordpp::RunCallbacks();

To support Client::Authorize:

  1. Add androidx.browser dependency (version 1.8 or later) to your build.gradle:
dependencies {
    implementation 'androidx.browser:browser:1.8.0'
    // Your other dependencies...
}
  1. Add the appropriate AndroidManifest.xml activity registration:
<activity android:name="com.discord.socialsdk.AuthenticationActivity"
android:exported="true">
  <intent-filter>
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    <data android:scheme="discord-YOUR_APP_ID" />
  </intent-filter>
</activity>

Replace YOUR_APP_ID with your actual Discord application ID (e.g., discord-1234567890123456789).


PKCE (Proof Key for Code Exchange, pronounced "pixie") is a security extension to OAuth2 that prevents authorization code interception attacks. It adds an extra layer of security by requiring both the client and server to prove they're part of the same authentication flow.

This is because custom URL schemes don't have the same security guarantees as HTTPS redirects, making them vulnerable to interception attacks without PKCE.

  1. Client generates a code verifier: A cryptographically random string
  2. Client creates a code challenge: A hashed version of the verifier
  3. Client sends code challenge with the authorization request
  4. Server stores the code challenge with the authorization code
  5. Client sends code verifier when exchanging the authorization code for a token
  6. Server verifies that the verifier matches the stored challenge

The Discord Social SDK handles the generation of the code verifier and challenge for you via Client::CreateAuthorizationCodeVerifier.


If your app does not have a backend server and you've enabled Public Client in the Discord Developer Portal, you can use the SDK to handle the entire authentication flow, including PKCE.

Use Client::CreateAuthorizationCodeVerifier to generate the PKCE values:

C++
// Generate code verifier and challenge
auto codeVerifier = client->CreateAuthorizationCodeVerifier();

Use Client::Authorize with the code challenge:

C++
discordpp::AuthorizationArgs args{};
args.SetClientId(YOUR_DISCORD_APPLICATION_ID);
args.SetScopes(discordpp::Client::GetDefaultPresenceScopes());
args.SetCodeChallenge(codeVerifier.Challenge());

client->Authorize(args, [client, codeVerifier](
    discordpp::ClientResult result,
    std::string code,
    std::string redirectUri) {
  if (!result.Successful()) {
    std::cerr << "❌ Authorization Error: " << result.Error() << std::endl;
  } else {
    std::cout << "✅ Authorization successful! Exchanging code for token...\n";
    // Proceed to Step 3
  }
});

After calling Client::Authorize, the SDK will:

  • Deep-link into the Discord mobile app if installed
  • Or open a browser if Discord is not installed
  • Present the authorization screen to the user

Step 4: Exchange Authorization Code for Token

Section titled “Step 4: Exchange Authorization Code for Token”

Once the user approves and your app receives the authorization code, exchange it for an access token using Client::GetToken with the code verifier:

C++
client->GetToken(
    YOUR_DISCORD_APPLICATION_ID,
    code,
    codeVerifier.Verifier(),  // Critical: Pass the verifier
    redirectUri,
    [client](discordpp::ClientResult result,
        std::string accessToken,
        std::string refreshToken,
        discordpp::AuthorizationTokenType tokenType,
        int32_t expiresIn,
        std::string scope) {
      if (!result.Successful()) {
        std::cerr << "❌ Error getting token: " << result.Error() << std::endl;
        return;
      }

      std::cout << "🔓 Access token received! Establishing connection...\n";

      // Update token and connect
      client->UpdateToken(tokenType, accessToken, [client](discordpp::ClientResult result) {
        client->Connect();
      });
    });

Authentication Flow for Confidential Clients

Section titled “Authentication Flow for Confidential Clients”

If your application has a backend server and uses a confidential client (with client secret), you must still implement PKCE on mobile, but the token exchange happens on your server.

Generate the PKCE values on the client:

C++
// Generate code verifier and challenge
auto codeVerifier = client->CreateAuthorizationCodeVerifier();

Pass the code challenge to the authorization request:

C++
discordpp::AuthorizationArgs args{};
args.SetClientId(YOUR_DISCORD_APPLICATION_ID);
args.SetScopes(discordpp::Client::GetDefaultPresenceScopes());
args.SetCodeChallenge(codeVerifier.Challenge());

client->Authorize(args, [client, codeVerifier](
    discordpp::ClientResult result,
    std::string code,
    std::string redirectUri) {
  if (!result.Successful()) {
    std::cerr << "❌ Authorization Error: " << result.Error() << std::endl;
  } else {
    std::cout << "✅ Authorization successful!\n";

    // Send BOTH the authorization code AND the code verifier to your server
    SendToServer(code, redirectUri, codeVerifier.Verifier());
  }
});

Your server must include the code_verifier parameter when exchanging the authorization code for an access token:

Python
import requests

API_ENDPOINT = 'https://discord.com/api/v10'
CLIENT_ID = 'YOUR_CLIENT_ID'
CLIENT_SECRET = 'YOUR_CLIENT_SECRET'

def exchange_code(code, redirect_uri, code_verifier):
    """
    Exchange authorization code for access token with PKCE verification.

    IMPORTANT: The code_verifier parameter is REQUIRED for mobile apps,
    even when using confidential clients with client secrets.
    """
    data = {
        'grant_type': 'authorization_code',
        'code': code,
        'redirect_uri': redirect_uri,
        'code_verifier': code_verifier,  # Required for mobile deep links
    }
    headers = {'Content-Type': 'application/x-www-form-urlencoded'}

    r = requests.post(
        f'{API_ENDPOINT}/oauth2/token',
        data=data,
        headers=headers,
        auth=(CLIENT_ID, CLIENT_SECRET)
    )
    r.raise_for_status()
    return r.json()
JSON
{
  "access_token": "<access token>",
  "token_type": "Bearer",
  "expires_in": 604800,
  "refresh_token": "<refresh token>",
  "scope": "sdk.social_layer"
}

Once your server returns the access token, update it in the SDK:

C++
// Receive access token from your server
void OnTokenReceived(const std::string& accessToken) {
  client->UpdateToken(
      discordpp::AuthorizationTokenType::Bearer,
      accessToken,
      [client](discordpp::ClientResult result) {
    if (result.Successful()) {
      client->Connect();
    }
  });
}

You'll want to store the access and refresh tokens for the player to use in future sessions.

Since access_tokens generally expire after 7 days. You'll need to use the refresh_token to refresh the player's access token, which is covered under Refreshing Access Tokens.


iOS: Authorization Opens Browser Instead of Discord App

Section titled “iOS: Authorization Opens Browser Instead of Discord App”

Problem: The authorization flow opens a browser instead of deep-linking to the Discord mobile app.

Solutions:

  1. Verify that LSApplicationQueriesSchemes includes discord in your Info.plist
  2. Ensure the Discord mobile app is installed on the device
  3. Check that your redirect URI is correctly configured as discord-YOUR_APP_ID:/authorize/callback

Problem: After authorization, the app doesn't receive the callback.

Solutions:

  1. Verify your AndroidManifest.xml has the correct intent filter
  2. Ensure the android:scheme matches your redirect URI: discord-YOUR_APP_ID
  3. Check that your activity is set to launch mode singleTask or singleTop if you need to handle multiple intents

Problem: Server-side token exchange returns an invalid_grant error.

Solutions:

  1. Most common: You forgot to include the code_verifier parameter in the token exchange request
  2. Verify you're sending the verifier (from codeVerifier.Verifier()), not the challenge
  3. Ensure the code hasn't expired (codes are short-lived)
  4. Check that the redirect URI matches exactly what was used in the authorization request

Now that you've successfully implemented account linking on mobile, you can integrate more social features into your game.

Need help? Join the Discord Developers Server and share questions in the #social-sdk-dev-help channel for support from the community.

If you encounter a bug while working with the Social SDK, please report it here: https://dis.gd/social-sdk-bug-report


Date Changes
September 8, 2026 Clarified that native auth reuses an existing signed-in Discord app session
January 26, 2026 Initial release
Suggest an edit

Propose a replacement for this page. The site team reviews it before applying any changes.

Export
Documentation menu