---
title: "Set up Discord login"
description: "Configure your Discord application and wait for a ready authenticated client."
seo:
  noindex: true
---

{/* Copyright (c) 2026 Betide Studio. All Rights Reserved. */}

This package enables Discord on **Win64 and Android**.

1. In your Discord application's **OAuth2** settings, enable **Public Client** for this client-side login flow.
2. Add the redirect URL for each target: `http://127.0.0.1/callback` on Win64, and `discord-APP_ID:/authorize/callback` on Android (replace `APP_ID`). [Discord authentication setup](https://discord.com/developers/docs/social-sdk/authentication.html)
3. Copy the **Application ID** into **Project Settings → Betide → Crossplay → Discord → Client Id**. Set it before packaging Android; the plugin uses it for the app's URL scheme.

## Sign in to Discord

**Blueprint**

<BlueprintViewer title="Sign in to Discord" src="/blueprints/crossplay-discord-login.txt" />

Game Instance variable: `DiscordLoginBusy` (**Boolean**, false).

**C++**

Game module dependencies: `DiscordGame`, `BetideCore`, `Engine`.

```cpp
// DiscordLoginGameInstance.h
#pragma once

#include "Engine/GameInstance.h"
#include "DiscordLoginGameInstance.generated.h"

UCLASS()
class UDiscordLoginGameInstance : public UGameInstance
{
    GENERATED_BODY()
  public:
    UFUNCTION(BlueprintCallable)
    void SignInToDiscord();

  private:
    bool DiscordLoginBusy = false;
};

// DiscordLoginGameInstance.cpp
#include "DiscordLoginGameInstance.h"
#include "DiscordGameSubsystem.h"
#include "Nodes/Discord_GetAuthToken_AsyncFunction.h"

DEFINE_LOG_CATEGORY_STATIC(LogGameDiscordLogin, Log, All);

void UDiscordLoginGameInstance::SignInToDiscord()
{
    if (DiscordLoginBusy)
    {
        UE_LOG(LogGameDiscordLogin, Warning, TEXT("Discord sign-in is already running"));
        return;
    }
    DiscordLoginBusy = true;
    auto *Login = UDiscord_GetAuthToken_AsyncFunction::GetDiscordAuthToken(this);
    if (!Login)
    {
        DiscordLoginBusy = false;
        UE_LOG(LogGameDiscordLogin, Error, TEXT("Could not create Discord login action"));
        return;
    }
    const TWeakObjectPtr<UDiscordLoginGameInstance> WeakThis(this);
    Login->CompletionCallback = [WeakThis](const FString &, const FBetideError &Error) {
        UDiscordLoginGameInstance *Self = WeakThis.Get();
        if (!Self)
            return;
        Self->DiscordLoginBusy = false;
        if (Error.IsError())
        {
            UE_LOG(LogGameDiscordLogin, Warning, TEXT("%s"), *Error.Message);
            return;
        }
        if (const UDiscordGameSubsystem *Discord = Self->GetSubsystem<UDiscordGameSubsystem>())
        {
            UE_LOG(LogGameDiscordLogin, Log, TEXT("Discord ready: %s (%lld)"),
                   *Discord->GetDiscordDisplayName(), Discord->GetDiscordUserId());
        }
    };
    Login->Activate();
}
```

Login authorizes communication scopes, exchanges the code, applies the token, and connects. Success means the client reached **Ready**. The connection stage has a 30-second timeout; that is not a timeout for the authorization UI. The returned access token can be passed explicitly to an external-auth consumer such as EOS Connect; never log it.

## Keep the connection ready

Observe **On Discord Client Status Changed** for disconnects and reconnection. **Connect Discord Client** resumes an initialized connection; **Disconnect Discord Client** stops it. Neither is an account login/logout operation.

In C++, use `UDiscordGameSubsystem` from `DiscordGameSubsystem.h`. `ConnectDiscord()` and `DisconnectDiscord()` control the connection. `RevokeToken(Token)` requests token revocation. Bind **On Discord Command Completed** first and check the `RevokeToken` result. The cached session is cleared before the request completes; an empty cache does not confirm revocation.

<Accordion>
  <AccordionItem title="Refresh and stored tokens">

The subsystem refreshes automatically after token expiry when it has a refresh token. Otherwise, sign in again. **Fetch Discord User For Token** validates a stored access token and reports **On Discord Token User Fetched**; bind the event before calling, then apply a valid token with **Update Access Token**.

Use **Get Discord Runtime Snapshot** when diagnosing SDK availability, application ID, authentication, and the current user. Keep tokens in secure storage if your game persists them. Multiple test users must authorize the same Discord application.

  </AccordionItem>
</Accordion>

Next: [friends and invites](/integrations/discord/social-invites) or [lobbies and voice](/integrations/discord/lobbies-voice).
