---
title: "Accounts with Cognito"
description: "Register and sign in users and obtain scoped AWS credentials."
seo:
  noindex: true
---

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

Use a **User Pool** for player accounts. Add an **Identity Pool** when those players need temporary AWS credentials for services such as S3 or DynamoDB.

## Setup

In **Project Settings → Betide → Crossplay → AWS Cognito**, set:

| Setting | Value |
| --- | --- |
| User Pool Id | Your Cognito user pool ID |
| User Pool Client Id | An app client created **without a client secret** |
| Identity Pool Id | Optional pool configured to trust that user pool and app client |

Match the [AWS region and credential source](/integrations/aws/setup) to your resources. Enable the app client's auth flow for your chosen login method: `ALLOW_USER_SRP_AUTH` for SRP or `ALLOW_USER_PASSWORD_AUTH` for password login. [AWS auth flows](https://docs.aws.amazon.com/cognito/latest/developerguide/authentication.html)

Keep **refresh-token rotation disabled** and enable `ALLOW_REFRESH_TOKEN_AUTH`: the plugin refreshes sessions through that flow. [AWS refresh requirements](https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-the-refresh-token.html)

## Register and confirm

Enable self-registration and configure the pool's required attributes and message delivery. Registration does not sign the player in. For a pool requiring confirmation, collect its code before login.

**Blueprint**

<Accordion>
  <AccordionItem title="Register with an email address">
<BlueprintViewer title="Register with an email address" src="/blueprints/crossplay-aws-cognito-register.txt" />
  </AccordionItem>
  <AccordionItem title="Confirm the player account">
<BlueprintViewer title="Confirm the player account" src="/blueprints/crossplay-aws-cognito-confirm.txt" />
  </AccordionItem>
  <AccordionItem title="Resend the confirmation code">
<BlueprintViewer title="Resend the confirmation code" src="/blueprints/crossplay-aws-cognito-resend.txt" />
  </AccordionItem>
</Accordion>

**C++**

Game module dependencies: `AWSIKCognito`, `BetideCore`, `Engine`. The account provider also works without UCIK.

```cpp
#include "Engine/GameInstance.h"
#include "AWSIKCognito.h"
#include "Accounts/IUCIKAccountProvider.h"

FBetideCallbackVoid AccountCompletion(UGameInstance &GameInstance, FString SuccessMessage)
{
    return FBetideCallbackVoid::CreateWeakLambda(
        &GameInstance,
        [SuccessMessage = MoveTemp(SuccessMessage)](const TBetideResult<void> &Result) {
            if (!Result.bOk)
            {
                UE_LOG(LogAWSIKCognito, Warning, TEXT("%s"), *Result.Error.Message);
                return;
            }
            UE_LOG(LogAWSIKCognito, Log, TEXT("%s"), *SuccessMessage);
        });
}

void CreatePlayerAccount(UGameInstance &GameInstance, const FString &Username,
                         const FString &Password, const FString &Email)
{
    const auto Accounts = FAWSIKCognitoModule::Get().GetAccountProvider();
    if (!Accounts)
    {
        UE_LOG(LogAWSIKCognito, Warning, TEXT("The Cognito account provider is unavailable"));
        return;
    }
    FUCIKAccountAttribute EmailAttribute;
    EmailAttribute.Name = TEXT("email");
    EmailAttribute.Value = Email;
    Accounts->Register(Username, Password, {EmailAttribute},
                       AccountCompletion(GameInstance, TEXT("Registration accepted")));
}

void ConfirmPlayerAccount(UGameInstance &GameInstance, const FString &Username, const FString &Code)
{
    const auto Accounts = FAWSIKCognitoModule::Get().GetAccountProvider();
    if (!Accounts)
    {
        UE_LOG(LogAWSIKCognito, Warning, TEXT("The Cognito account provider is unavailable"));
        return;
    }
    Accounts->ConfirmRegistration(
        Username, Code,
        AccountCompletion(GameInstance, TEXT("Account confirmed; sign in to continue")));
}

void ResendPlayerConfirmation(UGameInstance &GameInstance, const FString &Username)
{
    const auto Accounts = FAWSIKCognitoModule::Get().GetAccountProvider();
    if (!Accounts)
    {
        UE_LOG(LogAWSIKCognito, Warning, TEXT("The Cognito account provider is unavailable"));
        return;
    }
    Accounts->ResendConfirmation(
        Username, AccountCompletion(GameInstance, TEXT("Confirmation code requested")));
}
```

Use the same username for registration, confirmation, and resend. Add any other attributes your pool requires. These helpers report success/failure; they do not expose `UserConfirmed` or code-delivery details. Use the generated **Cognito User Pools Sign Up** node if your UI needs those fields. Neither confirmation nor registration establishes a game session.

## Sign in

Sign in with SRP, then answer any MFA or password challenge. Only **On Success** means the player is signed in.

**Blueprint**

<BlueprintViewer title="Cognito login and challenges" src="/blueprints/crossplay-aws-cognito-login.txt" />

**C++**

Game module dependencies: `UCIKCore`, `BetideCore`, `Engine`. Use this Game Instance class, or merge its members into your own.

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

#include "Engine/GameInstance.h"
#include "UCIKIdentitySubsystem.h"
#include "CognitoLoginGameInstance.generated.h"

UCLASS()
class UCognitoLoginGameInstance : public UGameInstance
{
    GENERATED_BODY()

  public:
    UFUNCTION(BlueprintCallable)
    void SignInCognito(const FString &Username, const FString &Password);

    UFUNCTION(BlueprintCallable)
    void AnswerCognitoChallenge(const FString &Answer);

    UFUNCTION(BlueprintCallable)
    void CancelCognitoChallenge();

    UPROPERTY(BlueprintReadOnly)
    FUCIK_LoginUpdate CognitoLoginUpdate;

    UPROPERTY(BlueprintReadOnly)
    bool CognitoChallengePending = false;
};

// CognitoLoginGameInstance.cpp
#include "CognitoLoginGameInstance.h"
#include "UCIKCore.h"

void UCognitoLoginGameInstance::SignInCognito(const FString &Username, const FString &Password)
{
    UUCIKIdentitySubsystem *Identity = GetSubsystem<UUCIKIdentitySubsystem>();
    if (!Identity || Identity->IsLoginInProgress())
    {
        UE_LOG(LogUCIK, Warning, TEXT("Identity unavailable or a login is already in progress"));
        return;
    }

    FUCIK_LoginRequest Request;
    Request.LocalUserIndex = 0;
    Request.MethodId = TEXT("Betide.Cognito.UserPool.SRP");
    Request.Fields.FindOrAdd(TEXT("Username")).StringValue = Username;
    Request.Fields.FindOrAdd(TEXT("Password")).StringValue = Password;
    Identity->Login(
        Request,
        FUCIKLoginUpdateCallback::CreateWeakLambda(this, [this](const FUCIK_LoginUpdate &Update) {
            CognitoLoginUpdate = Update;
            CognitoChallengePending =
                Update.Operation.State == EUCIK_LoginOperationState::NeedsChallengeResponse;
            if (CognitoChallengePending)
            {
                UE_LOG(LogUCIK, Log, TEXT("%s"), *Update.Challenge.Prompt.ToString());
            }
            else if (Update.Operation.IsTerminal())
            {
                if (Update.Operation.State == EUCIK_LoginOperationState::Succeeded)
                {
                    UE_LOG(LogUCIK, Log, TEXT("Signed in to Cognito"));
                }
                else
                {
                    UE_LOG(LogUCIK, Warning, TEXT("%s"), *Update.Error.Message);
                }
            }
        }));
}

void UCognitoLoginGameInstance::AnswerCognitoChallenge(const FString &Answer)
{
    if (!CognitoChallengePending)
    {
        UE_LOG(LogUCIK, Warning, TEXT("No Cognito challenge is waiting for an answer"));
        return;
    }
    UUCIKIdentitySubsystem *Identity = GetSubsystem<UUCIKIdentitySubsystem>();
    if (!Identity)
    {
        return;
    }
    TMap<FName, FUCIK_LoginFieldValue> Responses;
    Responses.FindOrAdd(CognitoLoginUpdate.Challenge.ResponseFields[0].FieldId).StringValue =
        Answer;
    CognitoChallengePending = false;
    Identity->ContinueLogin(CognitoLoginUpdate.Operation, Responses);
}

void UCognitoLoginGameInstance::CancelCognitoChallenge()
{
    if (!CognitoChallengePending)
    {
        UE_LOG(LogUCIK, Warning, TEXT("No Cognito challenge is waiting for an answer"));
        return;
    }
    if (UUCIKIdentitySubsystem *Identity = GetSubsystem<UUCIKIdentitySubsystem>())
    {
        CognitoChallengePending = false;
        Identity->CancelLogin(CognitoLoginUpdate.Operation);
    }
}
```

Required variables: `CognitoLoginUpdate` (**Crossplay Login Update**) and `CognitoChallengePending` (**Boolean**, false). Keep this flow in a Game Instance and route login requests through it.

Show `CognitoLoginUpdate.Challenge.Prompt` in your UI. This SRP flow returns one response field; **Answer Cognito Challenge** uses its ID automatically. **Cancel Cognito Challenge** cancels while that prompt is waiting. Neither event starts a new login. Do not log passwords or challenge answers.

### Temporary AWS access

With an Identity Pool configured, User Pool login also exchanges the verified ID token for AWS credentials **before reporting success**. A failed exchange fails login, even if the password was correct. Check the pool's trusted provider, app client, authenticated role, and role permissions.

Select **Cognito Identity Pool** as the AWS credential source. The plugin supplies and renews the temporary credentials; you do not copy access keys into project settings. Grant access only to the player's intended resources.

## Password recovery

Enable self-service account recovery in the pool. The player needs an eligible verified email address or phone number. [AWS recovery requirements](https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_ForgotPassword.html)

**Blueprint**

<Accordion>
  <AccordionItem title="Request a password reset code">
<BlueprintViewer title="Request a password reset code" src="/blueprints/crossplay-aws-cognito-reset-code.txt" />
  </AccordionItem>
  <AccordionItem title="Reset the player password">
<BlueprintViewer title="Reset the player password" src="/blueprints/crossplay-aws-cognito-reset-password.txt" />
  </AccordionItem>
  <AccordionItem title="Change a signed-in player password">
<BlueprintViewer title="Change a signed-in player password" src="/blueprints/crossplay-aws-cognito-change-password.txt" />
  </AccordionItem>
</Accordion>

**C++**

Uses `AccountCompletion` and the includes from [Register and confirm](#register-and-confirm). Call each function after collecting its input.

```cpp
void RequestPasswordReset(UGameInstance &GameInstance, const FString &Username)
{
    const auto Accounts = FAWSIKCognitoModule::Get().GetAccountProvider();
    if (!Accounts)
    {
        UE_LOG(LogAWSIKCognito, Warning, TEXT("The Cognito account provider is unavailable"));
        return;
    }
    Accounts->BeginPasswordReset(
        Username, AccountCompletion(GameInstance, TEXT("Password reset code requested")));
}

void ResetPlayerPassword(UGameInstance &GameInstance, const FString &Username, const FString &Code,
                         const FString &NewPassword)
{
    const auto Accounts = FAWSIKCognitoModule::Get().GetAccountProvider();
    if (!Accounts)
    {
        UE_LOG(LogAWSIKCognito, Warning, TEXT("The Cognito account provider is unavailable"));
        return;
    }
    Accounts->CompletePasswordReset(
        Username, Code, NewPassword,
        AccountCompletion(GameInstance, TEXT("Password reset; sign in with the new password")));
}

void ChangePlayerPassword(UGameInstance &GameInstance, const FString &PreviousPassword,
                          const FString &NewPassword)
{
    const auto Accounts = FAWSIKCognitoModule::Get().GetAccountProvider();
    if (!Accounts)
    {
        UE_LOG(LogAWSIKCognito, Warning, TEXT("The Cognito account provider is unavailable"));
        return;
    }
    Accounts->ChangePassword(0, PreviousPassword, NewPassword,
                             AccountCompletion(GameInstance, TEXT("Password changed")));
}
```

Use the same username for both reset steps. Resetting a password does not sign the player in. **Change Password** requires a verified Cognito session for local user `0`; after managed login, its access token needs `aws.cognito.signin.user.admin`. [AWS change-password requirements](https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_ChangePassword.html)

The reset helper does not return the code delivery destination. Use **Cognito User Pools Forgot Password** if your UI needs it. Keep password and code inputs out of logs.

<Accordion>
  <AccordionItem title="Managed browser login">

Set **User Pool Domain**, **User Pool Redirect Uri**, and the app client's matching allowed callback URL. Enable the authorization-code grant and requested scopes.

Choose **Cognito · Managed Login** (`Betide.Cognito.UserPool.ManagedLogin`). The plugin opens the browser and uses PKCE. Your game must receive the redirect and pass its `code` and `state` back as `AuthorizationCode` and `State` through **Continue Crossplay Login**. The plugin does not install a callback URL handler for your game. Accept only your configured callback URL and pass its decoded query values unchanged; the plugin checks `state` before exchanging the code. Closing the browser does not cancel login—call **Cancel Cognito Browser Login** when the player cancels or the callback returns an OAuth error.

For account-management calls after browser login, also enable and request `aws.cognito.signin.user.admin` in **Managed Login Scopes**; the default `openid` and `profile` scopes are insufficient. [AWS account scope](https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-define-resource-servers.html)

**Blueprint**

<BlueprintViewer title="Complete managed login" src="/blueprints/crossplay-aws-cognito-managed-login.txt" />

**C++**

Game module dependencies: `UCIKCore`, `BetideCore`, `Engine`. Use this Game Instance class, or merge its members into your own.

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

#include "Engine/GameInstance.h"
#include "UCIKIdentitySubsystem.h"
#include "CognitoBrowserGameInstance.generated.h"

UCLASS()
class UCognitoBrowserGameInstance : public UGameInstance
{
    GENERATED_BODY()

  public:
    UFUNCTION(BlueprintCallable)
    void StartCognitoBrowserLogin();

    UFUNCTION(BlueprintCallable)
    void CompleteCognitoBrowserLogin(const FString &Code, const FString &ReturnedState);

    UFUNCTION(BlueprintCallable)
    void CancelCognitoBrowserLogin();

    UPROPERTY(BlueprintReadOnly)
    FUCIK_LoginUpdate CognitoBrowserUpdate;

    UPROPERTY(BlueprintReadOnly)
    bool CognitoBrowserPending = false;
};

// CognitoBrowserGameInstance.cpp
#include "CognitoBrowserGameInstance.h"
#include "UCIKCore.h"

void UCognitoBrowserGameInstance::StartCognitoBrowserLogin()
{
    UUCIKIdentitySubsystem *Identity = GetSubsystem<UUCIKIdentitySubsystem>();
    if (!Identity || Identity->IsLoginInProgress())
    {
        UE_LOG(LogUCIK, Warning, TEXT("Identity unavailable or a login is already in progress"));
        return;
    }

    FUCIK_LoginRequest Request;
    Request.LocalUserIndex = 0;
    Request.MethodId = TEXT("Betide.Cognito.UserPool.ManagedLogin");
    Identity->Login(
        Request,
        FUCIKLoginUpdateCallback::CreateWeakLambda(this, [this](const FUCIK_LoginUpdate &Update) {
            CognitoBrowserUpdate = Update;
            CognitoBrowserPending =
                Update.Operation.State == EUCIK_LoginOperationState::NeedsUserInterface;
            if (CognitoBrowserPending)
            {
                UE_LOG(LogUCIK, Log, TEXT("%s"), *Update.Challenge.Prompt.ToString());
            }
            else if (Update.Operation.IsTerminal())
            {
                if (Update.Operation.State == EUCIK_LoginOperationState::Succeeded)
                {
                    UE_LOG(LogUCIK, Log, TEXT("Signed in to Cognito"));
                }
                else
                {
                    UE_LOG(LogUCIK, Warning, TEXT("%s"), *Update.Error.Message);
                }
            }
        }));
}

void UCognitoBrowserGameInstance::CompleteCognitoBrowserLogin(const FString &Code,
                                                              const FString &ReturnedState)
{
    if (!CognitoBrowserPending)
    {
        UE_LOG(LogUCIK, Warning, TEXT("No Cognito browser login is waiting for a callback"));
        return;
    }
    UUCIKIdentitySubsystem *Identity = GetSubsystem<UUCIKIdentitySubsystem>();
    if (!Identity)
    {
        return;
    }
    TMap<FName, FUCIK_LoginFieldValue> Responses;
    Responses.FindOrAdd(TEXT("AuthorizationCode")).StringValue = Code;
    Responses.FindOrAdd(TEXT("State")).StringValue = ReturnedState;
    CognitoBrowserPending = false;
    Identity->ContinueLogin(CognitoBrowserUpdate.Operation, Responses);
}

void UCognitoBrowserGameInstance::CancelCognitoBrowserLogin()
{
    if (!CognitoBrowserPending)
    {
        UE_LOG(LogUCIK, Warning, TEXT("No Cognito browser login is waiting for a callback"));
        return;
    }
    if (UUCIKIdentitySubsystem *Identity = GetSubsystem<UUCIKIdentitySubsystem>())
    {
        CognitoBrowserPending = false;
        Identity->CancelLogin(CognitoBrowserUpdate.Operation);
    }
}
```

Required variables: `CognitoBrowserUpdate` (**Crossplay Login Update**) and `CognitoBrowserPending` (**Boolean**, false). Keep the flow in a Game Instance so it survives travel. Do not log the callback URL or authorization code.


  </AccordionItem>
  <AccordionItem title="MFA, one-time passwords, and passkeys">

Sign in to the User Pool first. Enable software-token MFA in the pool; managed-login access tokens need `aws.cognito.signin.user.admin`. Show `MfaContinuation.Secret` in your setup UI while `MfaReady` is true, then submit the player's six-digit authenticator code. Never log or save the secret. [AWS authenticator setup](https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_AssociateSoftwareToken.html)

**Blueprint**

<BlueprintViewer title="Enroll an authenticator" src="/blueprints/crossplay-aws-cognito-enroll-mfa.txt" />

Game Instance variables: `MfaBusy` and `MfaReady` (**Boolean**, false), `MfaContinuation` (**UCIKAccountContinuation**). Dismiss clears local setup data; it does not disable MFA.

**C++**

Game module dependencies: `AWSIKCognito`, `BetideCore`, `Engine`. Bind the setup UI to `MfaReady` and `MfaContinuation.Secret`.

```cpp
// CognitoMfaGameInstance.h
#pragma once
#include "Engine/GameInstance.h"
#include "Accounts/IUCIKAccountProvider.h"
#include "CognitoMfaGameInstance.generated.h"

UCLASS()
class UCognitoMfaGameInstance : public UGameInstance
{
    GENERATED_BODY()
  public:
    UFUNCTION(BlueprintCallable)
    void EnrollAuthenticator();
    UFUNCTION(BlueprintCallable)
    void ConfirmAuthenticator(const FString &Code);
    UFUNCTION(BlueprintCallable)
    void DismissAuthenticatorSetup();

    UPROPERTY(BlueprintReadOnly, Transient)
    bool MfaBusy = false;
    UPROPERTY(BlueprintReadOnly, Transient)
    bool MfaReady = false;
    UPROPERTY(BlueprintReadOnly, Transient)
    FUCIKAccountContinuation MfaContinuation;
};
```

```cpp
// CognitoMfaGameInstance.cpp
#include "CognitoMfaGameInstance.h"
#include "AWSIKCognito.h"

void UCognitoMfaGameInstance::EnrollAuthenticator()
{
    if (MfaBusy)
    {
        UE_LOG(LogAWSIKCognito, Warning, TEXT("An MFA request is already running"));
        return;
    }
    MfaReady = false;
    MfaContinuation = {};
    const auto Accounts = FAWSIKCognitoModule::Get().GetAccountProvider();
    if (!Accounts)
    {
        UE_LOG(LogAWSIKCognito, Warning, TEXT("The Cognito account provider is unavailable"));
        return;
    }
    MfaBusy = true;
    Accounts->EnrolMFA(
        0, FUCIKAccountContinuationCallback::CreateWeakLambda(
               this, [this](const TBetideResult<FUCIKAccountContinuation> &Result) {
                   MfaBusy = false;
                   if (!Result.bOk)
                   {
                       UE_LOG(LogAWSIKCognito, Warning, TEXT("%s"), *Result.Error.Message);
                       return;
                   }
                   MfaContinuation = Result.GetValue();
                   MfaReady = true;
                   UE_LOG(LogAWSIKCognito, Log, TEXT("Authenticator setup key is ready"));
               }));
}

void UCognitoMfaGameInstance::ConfirmAuthenticator(const FString &Code)
{
    if (MfaBusy || !MfaReady)
    {
        UE_LOG(LogAWSIKCognito, Warning, TEXT("Wait for enrollment before confirming the code"));
        return;
    }
    const auto Accounts = FAWSIKCognitoModule::Get().GetAccountProvider();
    if (!Accounts)
    {
        UE_LOG(LogAWSIKCognito, Warning, TEXT("The Cognito account provider is unavailable"));
        return;
    }
    MfaBusy = true;
    Accounts->ConfirmMFA(
        0, Code, MfaContinuation,
        FBetideCallbackVoid::CreateWeakLambda(this, [this](const TBetideResult<void> &Result) {
            MfaBusy = false;
            if (!Result.bOk)
            {
                UE_LOG(LogAWSIKCognito, Warning, TEXT("%s"), *Result.Error.Message);
                return;
            }
            MfaReady = false;
            MfaContinuation = {};
            UE_LOG(LogAWSIKCognito, Log, TEXT("Authenticator MFA enabled"));
        }));
}

void UCognitoMfaGameInstance::DismissAuthenticatorSetup()
{
    if (MfaBusy)
    {
        UE_LOG(LogAWSIKCognito, Warning, TEXT("Wait for the current MFA request to finish"));
        return;
    }
    MfaReady = false;
    MfaContinuation = {};
}
```

A failed confirmation keeps setup available for retry. This flow enrolls an already signed-in player; handle login-time MFA challenges through [Sign in](#sign-in).

**Disable MFA** disables only software-token MFA for signed-in local user `0`. Call it after the player confirms the change. It does not reset the registered authenticator or change SMS/email preferences. Managed-login sessions need `aws.cognito.signin.user.admin`. [AWS MFA preferences](https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_SetUserMFAPreference.html)

**Blueprint**

<BlueprintViewer title="Disable authenticator MFA" src="/blueprints/crossplay-aws-cognito-disable-mfa.txt" />

**C++**

Uses `AccountCompletion` and the includes from [Register and confirm](#register-and-confirm).

```cpp
void DisableAuthenticatorAfterConfirmation(UGameInstance &GameInstance)
{
    const auto Accounts = FAWSIKCognitoModule::Get().GetAccountProvider();
    if (!Accounts)
    {
        UE_LOG(LogAWSIKCognito, Warning, TEXT("The Cognito account provider is unavailable"));
        return;
    }
    Accounts->DisableMFA(0, AccountCompletion(GameInstance, TEXT("Authenticator MFA disabled")));
}
```

Email OTP, SMS OTP, MFA selection, passkey, and custom-challenge methods are also listed by **Get Crossplay Login Methods**. Their availability still depends on your pool, app client, and AWS feature tier. Choice-based methods use `USER_AUTH`; custom challenges use `CUSTOM_AUTH`.

Passkey login expects a WebAuthn `CREDENTIAL` response from your own integration. Selecting that method alone does not open a native passkey picker. Handle the returned challenge fields rather than assuming every challenge is a six-digit code.

  </AccordionItem>
  <AccordionItem title="Profile, devices, and account deletion">

These operations require a verified User Pool session. Attribute updates must be allowed by the app client. Device listing reflects Cognito's remembered devices; it is not a list of every active game client.

**Blueprint**

<Accordion>
  <AccordionItem title="Read the account profile">
<BlueprintViewer title="Read the account profile" src="/blueprints/crossplay-aws-cognito-profile.txt" />
  </AccordionItem>
  <AccordionItem title="Update the player nickname">
<BlueprintViewer title="Update the player nickname" src="/blueprints/crossplay-aws-cognito-update-profile.txt" />
  </AccordionItem>
  <AccordionItem title="List remembered devices">
<BlueprintViewer title="List remembered devices" src="/blueprints/crossplay-aws-cognito-devices.txt" />
  </AccordionItem>
  <AccordionItem title="Forget a selected device">
<BlueprintViewer title="Forget a selected device" src="/blueprints/crossplay-aws-cognito-forget-device.txt" />
  </AccordionItem>
  <AccordionItem title="Delete the confirmed account">
<BlueprintViewer title="Delete the confirmed account" src="/blueprints/crossplay-aws-cognito-delete-account.txt" />
  </AccordionItem>
</Accordion>

**C++**

Game module dependencies: `AWSIKCognito`, `AWSIKCore`, `BetideCore`, `UCIKCore`, `Engine`. Uses `AccountCompletion` from [Register and confirm](#register-and-confirm).

```cpp
#include "Engine/GameInstance.h"
#include "AWSIKCognito.h"
#include "Accounts/IUCIKAccountProvider.h"
#include "Providers/AWSIKProviderIds.h"
#include "UCIKIdentitySubsystem.h"

void ReadAccountProfile(UGameInstance &GameInstance)
{
    const auto Accounts = FAWSIKCognitoModule::Get().GetAccountProvider();
    if (!Accounts)
    {
        UE_LOG(LogAWSIKCognito, Warning, TEXT("The Cognito account provider is unavailable"));
        return;
    }
    Accounts->GetProfile(
        0, FUCIKAccountProfileCallback::CreateWeakLambda(
               &GameInstance, [](const TBetideResult<FUCIKAccountProfile> &Result) {
                   if (!Result.bOk)
                   {
                       UE_LOG(LogAWSIKCognito, Warning, TEXT("%s"), *Result.Error.Message);
                       return;
                   }
                   const FUCIKAccountProfile &Profile = Result.GetValue();
                   UE_LOG(LogAWSIKCognito, Log, TEXT("%s: %d attributes"), *Profile.Username,
                          Profile.Attributes.Num());
               }));
}

void UpdatePlayerNickname(UGameInstance &GameInstance, const FString &Nickname)
{
    const auto Accounts = FAWSIKCognitoModule::Get().GetAccountProvider();
    if (!Accounts)
    {
        UE_LOG(LogAWSIKCognito, Warning, TEXT("The Cognito account provider is unavailable"));
        return;
    }
    FUCIKAccountAttribute Attribute;
    Attribute.Name = TEXT("nickname");
    Attribute.Value = Nickname;
    Accounts->UpdateProfile(0, {Attribute},
                            AccountCompletion(GameInstance, TEXT("Nickname updated")));
}

void ReadRememberedDevices(UGameInstance &GameInstance)
{
    const auto Accounts = FAWSIKCognitoModule::Get().GetAccountProvider();
    if (!Accounts)
    {
        UE_LOG(LogAWSIKCognito, Warning, TEXT("The Cognito account provider is unavailable"));
        return;
    }
    Accounts->ListDevices(
        0, FUCIKAccountDevicesCallback::CreateWeakLambda(
               &GameInstance, [](const TBetideResult<TArray<FUCIKAccountDevice>> &Result) {
                   if (!Result.bOk)
                   {
                       UE_LOG(LogAWSIKCognito, Warning, TEXT("%s"), *Result.Error.Message);
                       return;
                   }
                   for (const FUCIKAccountDevice &Device : Result.GetValue())
                   {
                       UE_LOG(LogAWSIKCognito, Log, TEXT("%s: last authenticated %s"), *Device.Id,
                              *Device.LastAuthenticatedAt.ToIso8601());
                   }
                   UE_LOG(LogAWSIKCognito, Log, TEXT("Device list loaded"));
               }));
}

void ForgetSelectedDevice(UGameInstance &GameInstance, const FString &DeviceId)
{
    const auto Accounts = FAWSIKCognitoModule::Get().GetAccountProvider();
    if (!Accounts)
    {
        UE_LOG(LogAWSIKCognito, Warning, TEXT("The Cognito account provider is unavailable"));
        return;
    }
    Accounts->ForgetDevice(0, DeviceId, AccountCompletion(GameInstance, TEXT("Device forgotten")));
}

void DeletePlayerAccountAfterConfirmation(UGameInstance &GameInstance)
{
    const auto Accounts = FAWSIKCognitoModule::Get().GetAccountProvider();
    if (!Accounts)
    {
        UE_LOG(LogAWSIKCognito, Warning, TEXT("The Cognito account provider is unavailable"));
        return;
    }
    Accounts->DeleteAccount(
        0, FBetideCallbackVoid::CreateWeakLambda(
               &GameInstance, [&GameInstance](const TBetideResult<void> &Result) {
                   if (!Result.bOk)
                   {
                       UE_LOG(LogAWSIKCognito, Warning, TEXT("%s"), *Result.Error.Message);
                       return;
                   }
                   UE_LOG(LogAWSIKCognito, Log, TEXT("Cognito account deleted"));
                   UUCIKIdentitySubsystem *Identity =
                       GameInstance.GetSubsystem<UUCIKIdentitySubsystem>();
                   if (!Identity)
                   {
                       UE_LOG(LogAWSIKCognito, Warning,
                              TEXT("Identity subsystem unavailable for local cleanup"));
                       return;
                   }
                   Identity->Logout(
                       BetideProviderIds::CognitoUserPool, 0, EUCIK_LogoutScope::LocalCache,
                       FUCIKLogoutCallback::CreateWeakLambda(
                           &GameInstance, [](const TBetideResult<FUCIK_LogoutResult> &Logout) {
                               if (!Logout.bOk)
                               {
                                   UE_LOG(LogAWSIKCognito, Warning, TEXT("%s"),
                                          *Logout.Error.Message);
                                   return;
                               }
                               UE_LOG(LogAWSIKCognito, Log, TEXT("Local sign-in state cleared"));
                           }));
               }));
}
```

Allow `nickname` writes in the app client. Email/phone updates can need verification; the update helper does not expose code-delivery details.

Pass a returned device `Id` to **Forget Device**. The helper lists pages automatically, up to 100 pages or 10,000 devices, then fails if more remain. It currently uses the device ID as its display name.

Delete only after the player confirms. The example deletes the Cognito account, then clears local crossplay sign-in state using **Local Cache** scope. It does not delete your game's stored data or the browser's login cookie.


  </AccordionItem>
  <AccordionItem title="Federation and account linking">

Google and Apple Identity Pool routes exchange their ID tokens for AWS access. Configure those providers in the Identity Pool. They do not create a User Pool password account.

**Cognito Identity Pool · Steam** additionally needs a deployed exchange backend, **Exchange Endpoint**, and matching **Steam Ticket Audience**. Its short-lived bearer token comes from the environment variable named by **Exchange Auth Environment Variable**. Keep the Steam verification credentials on that backend.

The User Pool provider rejects client-side account merging with `link_requires_project_backend`. Configure federation or implement linking on a trusted backend; signing in twice does not merge progression.

  </AccordionItem>
  <AccordionItem title="Tokens and sign-out">

The plugin keeps verified sessions in memory and refreshes them before expiry. Do not log passwords, tokens, or challenge answers. Your backend must verify a token before using its claims.

For a signed-in Cognito player, **Logout From Crossplay** with **Authority Session + Local Cache** calls global sign-out and clears local identity. This affects the user's Cognito tokens across sessions. Managed login needs `aws.cognito.signin.user.admin`. [AWS global sign-out](https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_GlobalSignOut.html)

To revoke only the stored refresh-token session, use **Revoke Session**, then **Logout From Crossplay → Local Cache**. The app client must support token revocation, and the stored session must contain a refresh token. [AWS token revocation](https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_RevokeToken.html)

**Blueprint**

<Accordion>
  <AccordionItem title="Sign out of Cognito">
<BlueprintViewer title="Sign out of Cognito" src="/blueprints/crossplay-aws-cognito-signout.txt" />
  </AccordionItem>
  <AccordionItem title="Revoke the current Cognito session">
<BlueprintViewer title="Revoke the current Cognito session" src="/blueprints/crossplay-aws-cognito-revoke-session.txt" />
  </AccordionItem>
</Accordion>

**C++**

Game module dependencies: `AWSIKCognito`, `AWSIKCore`, `BetideCore`, `UCIKCore`, `Engine`.

```cpp
#include "Engine/GameInstance.h"
#include "AWSIKCognito.h"
#include "Accounts/IUCIKAccountProvider.h"
#include "Providers/AWSIKProviderIds.h"
#include "UCIKIdentitySubsystem.h"

void SignOutCognito(UGameInstance &GameInstance)
{
    UUCIKIdentitySubsystem *Identity = GameInstance.GetSubsystem<UUCIKIdentitySubsystem>();
    if (!Identity)
    {
        UE_LOG(LogAWSIKCognito, Warning, TEXT("The identity subsystem is unavailable"));
        return;
    }
    Identity->Logout(BetideProviderIds::CognitoUserPool, 0,
                     EUCIK_LogoutScope::AuthoritySession | EUCIK_LogoutScope::LocalCache,
                     FUCIKLogoutCallback::CreateWeakLambda(
                         &GameInstance, [](const TBetideResult<FUCIK_LogoutResult> &Result) {
                             if (!Result.bOk)
                             {
                                 UE_LOG(LogAWSIKCognito, Warning, TEXT("%s"),
                                        *Result.Error.Message);
                                 return;
                             }
                             UE_LOG(LogAWSIKCognito, Log, TEXT("Signed out of Cognito"));
                         }));
}

void RevokeCurrentCognitoSession(UGameInstance &GameInstance)
{
    const auto Accounts = FAWSIKCognitoModule::Get().GetAccountProvider();
    if (!Accounts)
    {
        UE_LOG(LogAWSIKCognito, Warning, TEXT("The Cognito account provider is unavailable"));
        return;
    }
    Accounts->RevokeSession(
        0, FBetideCallbackVoid::CreateWeakLambda(
               &GameInstance, [&GameInstance](const TBetideResult<void> &Result) {
                   if (!Result.bOk)
                   {
                       UE_LOG(LogAWSIKCognito, Warning, TEXT("%s"), *Result.Error.Message);
                       return;
                   }
                   UE_LOG(LogAWSIKCognito, Log, TEXT("Cognito session revoked"));
                   UUCIKIdentitySubsystem *Identity =
                       GameInstance.GetSubsystem<UUCIKIdentitySubsystem>();
                   if (!Identity)
                   {
                       UE_LOG(LogAWSIKCognito, Warning,
                              TEXT("Identity subsystem unavailable for local cleanup"));
                       return;
                   }
                   Identity->Logout(
                       BetideProviderIds::CognitoUserPool, 0, EUCIK_LogoutScope::LocalCache,
                       FUCIKLogoutCallback::CreateWeakLambda(
                           &GameInstance, [](const TBetideResult<FUCIK_LogoutResult> &Logout) {
                               if (!Logout.bOk)
                               {
                                   UE_LOG(LogAWSIKCognito, Warning, TEXT("%s"),
                                          *Logout.Error.Message);
                                   return;
                               }
                               UE_LOG(LogAWSIKCognito, Log, TEXT("Local sign-in state cleared"));
                           }));
               }));
}
```

Neither flow clears the managed-login browser cookie. To sign out that browser too, open your domain's [Cognito logout endpoint](https://docs.aws.amazon.com/cognito/latest/developerguide/logout-endpoint.html) with `client_id` and an encoded `logout_uri` matching an allowed sign-out URL. Offline JWT validation can still accept a revoked token until expiry; enforce revocation in your backend if immediate denial is required. [Revocation limits](https://docs.aws.amazon.com/cognito/latest/developerguide/token-revocation.html)


  </AccordionItem>
</Accordion>
