Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.dc.heliumnetworks.xyz/llms.txt

Use this file to discover all available pages before exploring further.

Overview

New to account linking? Read the Account Linking overview to understand what it is, how it works, and which flow to use.
This guide explains how to authenticate users with their existing Discord accounts via OAuth2, enabling seamless login and access to Discord features.

Flexible Account Options

If a player does not have a Discord account, you can use the SDK to create a provisional account instead so that they can still access your game’s features. For the account linking integration flow we recommend — provisional account first, and Discord-linked account second — see Recommended Integration Path below.

Prerequisites

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:
    • Discord application created in the Developer Portal
    • Discord Social SDK downloaded and configured
    • Basic SDK integration working (initialization and connection)
If you haven’t completed these prerequisites, we recommend first following the Getting Started guide.
This feature requires the Default Presence Scopes (openid and sdk.social_layer_presence). Use Client::GetDefaultPresenceScopes when configuring your OAuth2 flow. See the OAuth2 Scopes guide for details on all available scopes.

Our Authentication Flow

OAuth2 is the standard authentication flow that allows users to sign in using their Discord account. The process follows these steps:
  1. Request authorization: Your game sends an authentication request to Discord.
  2. User Approval: The user approves the request, granting access to your application.
  3. Receive Authorization Code: After approval, Discord redirects the user to your app with an authorization code.
  4. Exchange for Tokens: The authorization code is exchanged for:
    • Access Token, which is valid for ~7 days
    • Refresh Token, used to obtain a new access token
The OAuth2 flow requires a user’s account to be verified

OAuth2 using the Discord Social SDK

  • If the Discord client has overlay support (Windows only), the OAuth2 login modal appears in your game instead of opening a browser.
  • The SDK automatically handles redirects, simplifying the authentication flow.
  • Some security measures, such as CSRF protection, are built-in, but you should always follow best practices to secure your app.

Requesting Access Tokens

Step 0: Configure OAuth2 Redirects

For OAuth2 to work correctly, you must register the correct redirect URIs for your app in the Discord Developer Portal.
PlatformRedirect URI
Desktophttp://127.0.0.1/callback
Mobilediscord-APP_ID:/authorize/callback (replace APP_ID with your Discord application ID)

Step 1: Request Authorization

The SDK provides helper methods to simplify OAuth2 login. Use the Client::Authorize method to initiate authorization and allow the user to approve access.

Authorization Scopes

One of the required arguments to Client::Authorize is scopes — the set of permissions your game is requesting from the user. The Discord Social SDK provides two helper methods that cover the most common use cases:
Helper MethodScopes RequestedFeatures Enabled
Client::GetDefaultPresenceScopesopenid sdk.social_layer_presenceAccount linking, friends list, rich presence
Client::GetDefaultCommunicationScopesopenid sdk.social_layerAll of the above, plus lobbies, voice chat, direct messaging, and linked channels
Start with Client::GetDefaultPresenceScopes unless you know you need the communication features. You can always add more scopes later as your integration expands. See the OAuth2 Scopes guide for full details.

Authorization Code Verifier

If you are using Client::GetToken in Step 4, you will need to specify a “code challenge” and “code verifier” in your requests. We’ll spare you the boring details of how that works (woo… crypto), as we’ve made a simple function to create these for you, Client::CreateAuthorizationCodeVerifier, which you can use to generate the code challenge and verifier.
// Create a code verifier and challenge if using GetToken
auto codeVerifier = client->CreateAuthorizationCodeVerifier();
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! Next step: exchange code for an access token \n";
  }
});

Step 2: User Approval

After calling Client::Authorize, the SDK will open a browser window, Discord client, or an in-game overlay to prompt the user to approve the request.

Step 3: Receiving the Authorization Code

Once the user approves the request from Step 2, Discord will redirect the user back to your app with an authorization code that you can use to exchange for an access token.

Step 4: Exchanging the Authorization Code for an Access Token

Server-to-Server Get Token Exchange

If your application uses a backend server and does not have Public Client enabled, you can manually exchange the authorization code for an access token using the Discord API.
import requests

API_ENDPOINT = 'https://dc.heliumnetworks.xyz/api/v10'
CLIENT_ID = 'YOUR_CLIENT_ID'
CLIENT_SECRET = 'YOUR_CLIENT_SECRET'

def exchange_code(code, redirect_uri, code_verifier):
    data = {
        'grant_type': 'authorization_code',
        'code': code,
        'redirect_uri': redirect_uri,
        'code_verifier': code_verifier
    }
    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()

Example Response

{
  "access_token": "<access token>",
  "token_type": "Bearer",
  "expires_in": 604800,
  "refresh_token": "<refresh token>",
  "scope": "sdk.social_layer"
}

Token Exchange for Public Clients

This method requires enabling Public Client for your app. Most games will not want to ship with this enabled. Learn more
If your app does not have a backend server, enable Public Client in the Discord Developer Portal and use Client::GetToken to automatically exchange the authorization code for a token. We will also need the code verifier used to generate the code challenge in Step 1.
client->GetToken(YOUR_DISCORD_APPLICATION_ID, code, codeVerifier.Verifier(), redirectUri,
  [client](discordpp::ClientResult result,
    std::string accessToken,
    std::string refreshToken,
    discordpp::AuthorizationTokenType tokenType,
    int32_t expiresIn,
    std::string scope) {
    std::cout << "🔓 Access token received! Establishing connection...\n";
    // Next step: Update the token in the client and connect to Discord
  });

Working with Tokens

Once you’ve received your access token, you’ll want to set the token in the SDK. You can use Client::UpdateToken to do that. At this point, you’re authorized and ready to go! You’ll want to store the player’s access token and refresh tokens somewhere. Please note that the access_token values do expire. You’ll need to use the refresh_token to refresh the player’s access token.
client->UpdateToken(discordpp::AuthorizationTokenType::Bearer, ACCESS_TOKEN_VALUE, [client](discordpp::ClientResult result) {
  client->Connect();
);

Refreshing Access Tokens

Access tokens expire after 7 days, requiring refresh tokens to get a new one.

Server-to-Server Token Refresh

If you’re handling authentication on your server, send an API request to refresh the token.
import requests

API_ENDPOINT = 'https://dc.heliumnetworks.xyz/api/v10'
CLIENT_ID = 'YOUR_CLIENT_ID'
CLIENT_SECRET = 'YOUR_CLIENT_SECRET'

def refresh_token(refresh_token):
    data = {
        'grant_type': 'refresh_token',
        'refresh_token': refresh_token
    }
    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()

Refreshing Access Tokens for Public Clients

This method requires enabling Public Client for your app. Most games will not want to ship with this enabled. Learn more
The easiest way to refresh tokens is using the SDK’s Client::RefreshToken method.
client->RefreshToken(
      YOUR_DISCORD_APPLICATION_ID, GetRefreshToken(),
      [client](discordpp::ClientResult result, std::string accessToken,
               std::string refreshToken,
               discordpp::AuthorizationTokenType tokenType, int32_t expiresIn,
               std::string scope) {
        if (!result.Successful()) {
          std::cout << "❌ Error refreshing token: " << result.Error()
                    << std::endl;
          return;
        }

        // Update token and connect
        UpdateToken(client, refreshToken, accessToken);
      });

When Refresh Fails

A stored refresh token can become invalid between sessions for several reasons — the user revoked your application from their User Settings → Authorized Apps page, you called the unmerge or token revocation endpoint, or the user’s Discord account was banned. In every one of these cases the response on /oauth2/token with grant_type=refresh_token is the same:
  • HTTP status: 400
  • Body: { "error": "invalid_grant", "error_description": "Invalid \"refresh_token\" in request" }
There is no ban-specific or unlink-specific error code on this path — by the time the refresh runs, the underlying tokens have already been deleted server-side, so the lookup fails. When you do see invalid_grant on refresh, drop the stored Discord tokens for the user and fall back to the provisional account flow to keep the player in-game even if their Discord link is gone.
To be notified when a user’s authorization for your app is revoked — whether by an unlink, a token revocation, or a Discord account ban — integrate the APPLICATION_DEAUTHORIZED webhook event. That gives you a proactive signal you can act on immediately, rather than discovering the change the next time a refresh fails.

Revoking Access Tokens

A user’s authorization for your app can be revoked in several ways — by your own backend, by the user from Discord’s UI, or by Discord itself when an account is banned. Regardless of how the revocation is triggered:
  • The user’s access and refresh tokens are immediately invalidated. Any subsequent refresh on /oauth2/token returns HTTP 400 with error: "invalid_grant" (see When Refresh Fails).
  • An APPLICATION_DEAUTHORIZED webhook is fired to your app.
For Discord Social SDK-integrated apps, every revocation path in this section is mechanically an unmerge: the merged Discord account reverts to a new provisional account carrying the original external_auth_token. This holds whether revocation comes from /oauth2/token/revoke, Client::RevokeToken, the user removing your app from Discord’s UI, or a ban. Only ban-driven unmerges create a restricted provisional account — the others create an unrestricted one that can be re-merged freely.
You’ll naturally know about revocations your own code initiates. For revocations triggered outside your code — see Out-of-Band Revocation below — the webhook is the only proactive way to find out.
When any valid access or refresh token is revoked, all of your application’s access and refresh tokens for that user are immediately invalidated.

Server-to-Server Token Revocation

If your application uses a backend server, you can revoke tokens by making an API request to Discord’s token revocation endpoint.
import requests

API_ENDPOINT = 'https://dc.heliumnetworks.xyz/api/v10'
CLIENT_ID = 'YOUR_CLIENT_ID'
CLIENT_SECRET = 'YOUR_CLIENT_SECRET'

def revoke_token(access_or_refresh_token):
    data = {'token': access_or_refresh_token}
    headers = {'Content-Type': 'application/x-www-form-urlencoded'}
    r = requests.post(f'{API_ENDPOINT}/oauth2/token/revoke', data=data, headers=headers, auth=(CLIENT_ID, CLIENT_SECRET))
    r.raise_for_status()

Revoking Access Tokens for Public Clients

This method requires enabling Public Client for your app. Most games will not want to ship with this enabled. Learn more
The easiest way to revoke tokens is using the SDK’s Client::RevokeToken method. This will invalidate all access and refresh tokens for the user and they cannot be used again.
client->RevokeToken(YOUR_DISCORD_APPLICATION_ID,
                    accessToken, // Can also use refresh token
                    [](const discordpp::ClientResult &result) {
                      if (!result.Successful()) {
                        std::cout
                            << "? Error revoking token: " << result.Error()
                            << std::endl;
                        return;
                      }

                      std::cout
                          << "? Token successfully revoked! User logged out."
                          << std::endl;
                      // Handle successful logout (clear stored tokens,
                      // redirect to login, etc.)
                    });

Out-of-Band Revocation

Two paths sever the user’s Discord link without your code calling any unmerge or revoke endpoint:
  • The user removes your app from their Discord User Settings → Authorized Apps page (a user-initiated unmerge).
  • The user’s Discord account is banned (a ban-driven unmerge, with an additional cross-platform-restricted state on the new provisional account).
Subscribe to APPLICATION_DEAUTHORIZED to be notified when either happens. If you miss the webhook, the next token use will fail and your game should fall back to the provisional account flow.
The recommended path for integrating the Discord Social SDK is that your game has a primary authentication other than Discord that initially sets up a provisional account, and have the player link their Discord account to this primary authentication. This approach protects your users’ game access and data if they encounter issues with their Discord account, such as a permanent or temporary ban. To implement this recommended path:
  1. Create an account through a non-Discord authentication provider, and create a provisional account attached to it.
  2. When users later authenticate through Discord to link their account, have your game back end execute the merge their provisional account with their Discord Account.
  3. The account merging process will internally store the externalAuthToken from the provisional account against their Discord account. If a ban of the Discord account happens, that externalAuthToken will be attached to the new provisional account that is created in its stead, with the original Discord account’s in-game friends, and will be available through the authentication provider the account was initially setup with.
  4. As a last step, your game back end should maintain the record of the externalAuthToken against the user account, even after the account merging process, since it will be needed to authenticate via a provisional account should Discord authentication fails for a ban, or any other reason.
If you use Discord as the primary or sole authentication mechanism for your game, you risk players permanently losing access to their in-game data if their Discord account is banned, as there is no way to migrate them to a provisional account that is connected to an external authentication provider.

Next Steps

Now that you’ve successfully implemented account linking with Discord, you can integrate more social features into your game.

Design: Signing In

Design guidelines for account linking and user authentication

Creating a Unified Friends List

Combine Discord and game friends into a single list for easy management.

Setting Rich Presence

Display game status and information to Discord friends.
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

Change Log

DateChanges
May 22, 2026Add out-of-band revocation and recommended integration path
March 17, 2025initial release