Skip to main content

Set up OIDC as an identity provider

Last updated on October 28, 2024

Overview

The purpose of this guide is to help you integrate OpenID Connect (OIDC) with AccelByte Gaming Services (AGS). We support three OIDC authentication types: Authorization Code, ID Token, Access Token.

You may need to set up additional identity provider features which are not listed here, depending on your game. For more information about setting up OIDC services, we recommend reviewing your identity provider's documentation.

important

This guide is intended for public use and contains limited information due to confidentiality. We recommend you refer to the full confidential guide first. To request a copy of the confidential guide, contact your AccelByte Technical Producer.

Goals

  • Enable the OIDC authentication method for your game with AccelByte's Game SDK.
  • Enable the OIDC authentication method for your publisher website with AGS's Player Portal.

Prerequisites

Web login integration

  • An identity provider that supports OIDC.
  • An AccelByte Admin Portal account to set up authentication and manage permissions.
  • A publisher Namespace for your Player Portal and Launcher
  • A Player Portal.

In-game login integration

  • An identity provider that supports OIDC.
  • An AGS Admin Portal account to set up authentication and manage permissions.
  • A game Namespace.
  • A Unity or Unreal game project into which you imported AccelByte's Game SDK.
  • The latest version of the AccelByte Unity SDK or Unreal SDK.
  • Familiarity with AGS Identity and Access Management (IAM) Clients.

Authorization Code type

Authorization Code type supports authentication by both using the standard Authorization Code Flow and validating ID token via the JSON Web Key Set (JWKS). Authorization Code is webflow-based authentication. The system redirects you to your identity provider login page and back to the AccelByte portal with access.

Set up your IdP

Follow these steps to set up identity provider (IdP) OIDC logins in your game. This will allow your players to sign in to the Admin Portal, Player Portal, and Launcher using their accounts.

We use Okta OIDC as an example to set up the OIDC Player Portal and Admin Portal login. For Authorization Code, you need these items to integrate your identity provider OIDC with AGS:

  • JWKS URL
  • Issuer
  • Client ID
  • Client Secret
  • Authorization Request URL
  • Token Endpoint URL

As an example, we will use Okta as our identity provider. You can use another IdP that supports OIDC.

Create an Okta application

Follow Okta's Create OIDC app integrations guide to create an Okta Application under your Okta account.

note

Keep a copy of the client ID and client secret that you generate in this step. You will need it when you set up the web login.

Get your Okta OIDC configuration

Use this URL to open your app openid-configuration: https://<YOUR_OKTA_URL>.okta.com/oauth2/default/.well-known/openid-configuration.

You will need to copy the following items to configure OIDC Authorization Code type login:

  • JWKS URL
  • Issuer
  • Authorization Request URL
  • Token Endpoint URL

Set up web login

To set up OIDC logins in the Player Portal and Admin Portal and to enable your players to sign in to the Player Portal using their IdP accounts, follow these steps:

  1. In the AGS Admin portal, go to your publisher namespace.

  2. On the sidebar menu, go to Game Setup > 3rd Party Configuration > Auth & Account Linking.

  3. On the Login Methods page, click on the + Add New button.

    Image shows the Login Methods page

  4. At the bottom of the login method options, click on + Create OIDC.

    Image shows the Create OIDC button

  5. The Create OpenID Connect (OIDC) form displays. Fill in the required information. In this example, we use the Authorization Code type.

    • Platform Name: Type in a representative name, e.g, oidcokta.
    • Platform ID: Fill in the field with the platform ID that you use as Valid RedirectURI on the Okta App, e.g., oidcokta.
    • Authentication type: Select Authorization Code.
    • JWKS URL: Use the jwks_url on your Okta openid configuration URL.
    • Issuer: Use the issuer on your Okta openid configuration URL.
    • Client ID: Use the Okta Client ID that you obtained from the Create an Okta application step.
    • Client Secret: Use your Okta Client Secret that you obtained from the Create an OKTA application step.
    • Authorization Request URL: Use the authorization_endpoint on your Okta openid configuration URL.
    • Token Endpoint URL: Use the token_endpoint on your Okta openid configuration URL.
    • Clients: Depending on your usage, you can use OIDC login for the Player Portal, Launcher, Admin Portal, and In-Game login by setting those clients on this OIDC. For example, if you want to integrate OIDC Login with the Player Portal and Admin Portal, you need to add the Player Portal Website IAM Client and Admin Portal Website IAM Client here.
    • Scopes (Optional): By default, this is set to openid email profile.

    Image shows the upper part of the Create OIDC page

    Image shows the power part of the Create OIDC page

  6. Click Next.

  7. On the Token Claims Mapping form, map the user identity in the ID Token to enable the system to parse it into the AccelByte account.

    Image hows the Token Claims Mapping form

  8. Click Next.

  9. (Optional) In the Domains section, you can register your domain and attach a role to the user.

    • To skip this step, click Create.

      Image shows the Domains page

    • To register your domain, click + Register Domain. Then, fill in the following fields on the Register Domain form. After you fill in the fields, click Save, then click Create.

      Image shows the Register Domain form

      • Domain Name: Type in the name of the domain. In this example, we use accelbyte.net.

      • Clients: The system grants the user default roles when they first login to the client. In this example we want to grant the Admin role when a user logs in using OIDC on the Admin Portal, so we set it to Admin Portal Website IAM Client.

      • Default Role: The role you want to grant the user.

      • Default Namespace: if the default role is not a global role, specify its targeted Namespace.

        Register Domain is useful if you want to automatically assign roles to the user under a specified domain. For example, the OIDC login is used for Admin Portal Login. You want to automatically grant a role to the user under <your-domain> after they log in using the OIDC Login.

  10. The system redirects you to the details page of the OIDC you created. Click Activate to activate the OIDC.

    Image shows the OIDC Details page

User login from the Player Portal using OIDC

Ensure you set up an OIDC Login Method on Publisher Namespace and set Player Portal Website Client as the OIDC Client.

  1. Go to the Player Portal and click Login.

    Image shows the Player Portal

  2. Click More Login Options.

    Image shows the More Login Options button

  3. Click the login option with the OIDC you set up. In this example, we use Okta.

  4. The system redirects you to the Okta Login Page. Type your credentials and log in.

    Image shows the Okta Login page

    note

    The system asks players to link their OIDC account with an existing or new AccelByte account. AccelByte is working to make this configurable, enabling players to log in to the Player Portal directly with a headless account.

User login from Admin Portal using OIDC

Ensure you set up the OIDC Login Method on Publisher Namespace and set the Admin Portal Website Client as an OIDC Client.

note

To access the Admin Portal you need to attach an Admin Role to the user. If you already registered your domain as an OIDC Login Method and attached admin roles to it, the system grants every OIDC user under your registered domain that role.

  1. Go to the Admin Portal. The system redirects you to the login page. Click on the OIDC logo. If there's more than one, hover over the button to ensure you use the correct OIDC Login.

    Image shows the Admin Portal page

  2. The system redirects you to the Okta Login page. Use your credentials to log in.

    Image shows the Okta Login page

  3. The system redirects you to the Admin Portal.

    Image shows the Admin Portal

    Your access to the portal is based on your role. For example, if the role you assigned is Admin Portal Read Only for the AccelByte namespace, you can only access the features that role allows.

    Image shows sample domain settings

ID token type

ID Token type support authentication by validating ID Token via the JWKS. ID Token is for in-game-flow based authentication, You need to generate the ID Token from your Identity Provider. You can exchange it for an AccelByte access token.

Set up your IdP

Use the following steps to set up your identity provider (IdP) OIDC logins in your game. This will allow your players to sign in to your game using their accounts.

You need to handle the authorization from your IdP to get an ID Token. You need the following items to integrate your IdP OIDC with AGS:

  • JWKS URL
  • Issuer
  • Client ID

As an example, we use Facebook as our IdP. You can use another IdP that supports OIDC.

Create a Facebook application

Follow Facebook's Create an App guide to create a Facebook application under your Facebook Developer Account.

Add a Facebook login product to your app

Follow the Facebook Login for the Web with the JavaScript SDK guide to add a Facebook Login Product to your app.

Set up in-game login

Follow these steps to set up OIDC logins in-game. We use Facebook OIDC as an example. This enables your players to sign in to your game using their identity provider accounts.

  1. In the AGS Admin portal, go to your game namespace.

  2. On the sidebar menu, go to Game Setup > 3rd Party Configuration > Auth & Account Linking.

  3. On the Login Methods page, click on the + Add New button.

    Image shows the Login Methods page

  4. At the bottom of the login method options, click on + Create OIDC.

    Image shows the Create OIDC button

  5. On the Create OpenID Connect (OIDC) form, fill in the required fields. In this example, we use ID Token as the authentication type.

    • Platform Name: Type in the name for the login method, e.g., oidcfacebook.
    • Platform ID: Type in the ID for the login method, e.g., oidcfacebook.
    • Authentication type: Select ID Token.
    • JWKS URL: As this example uses Facebook OIDC, use https://www.facebook.com/.well-known/oauth/openid/jwks/.
    • Issuer: As this example uses Facebook OIDC, use [https://www.facebook.com](https://www.facebook.com).

    Image shows the Create Open ID (OIDC) form

  6. Click Next.

  7. Map the user identity in the ID token to enable the system to parse it into the AccelByte account. You can learn more about token claims in Claims.

    Image shows the Claims Mapping page

  8. The system redirects you to the details page of the OIDC you created. Click Activate to activate the OIDC.

    Image shows the Detail page of the new login method

Access Token type

Access Token type supports authentication by validating an access token via the UserInfo API Endpoint. Access Token type is for custom needs when the IdP can't give you with an ID Token but has a User Info endpoint that you can use with its access token.

Set up your IdP

Follow these steps to set up your IdP OIDC logins in your game. This will allow your players to sign in to your game using their accounts.

To use the Access Token type, you need these items to integrate your IdP OIDC with AGS:

  • User Info Endpoint URL
  • User Info Endpoint HTTP method

As an example, we use Okta as our IdP. You can use another IdP that supports OIDC.

Create an Okta application

Follow Okta's Create OIDC app integrations guide to create an Okta application.

Set the sign-in redirect URIs for your OKTA application using these URLs:

  • <BaseURL>/iam/v3/platforms/<PlatformId>/authenticate
  • <BaseURL>/iam/v3/public/namespaces/<namespace>/users/me/platforms/<platformId>/web/link/establish
note
  • BaseURL is your domain address. For example, https://development.accelbyte.io.
  • Namespace is your publisher or studio namespace. For example, accelbyte.
  • PlatformId is your OIDC PlatformId that you will set on the AGS Admin Portal.

Set up in-game login

To set up in-game OIDC logins and to enable your players to sign in using their IdP accounts, follow these steps.

  1. In the AGS Admin portal, go to your game namespace.

  2. On the sidebar menu, go to Game Setup > 3rd Party Configuration > Auth & Account Linking.

  3. On the Login Methods page, click on the + Add New button.

    Image shows the Login Methods page

  4. At the bottom of the login method options, click on + Create OIDC.

    Image shows the Create OIDC button

  5. On the Create OpenID Connect (OIDC) for, fill in the required information. In this example (Okta), we use Access Token as the authentication type.

    • Platform Name: Type in a name for the OIDC, e.g., oidc okta access token.
    • Platform ID: Type in the ID for the OIDC, oktaaccesstoken.
    • Authentication type: Select Access Token.
    • UserInfo Endpoint: Use your Okta application user info endpoint, e.g., https://YOUR_OKTA_URL.okta.com/oauth2/v1/userinfo.
    • HTTP Method: Enter the corresponding HTTP method used by the IdP, e.g., GET.

    Image shows the Create OIDC page

  6. Click Next.

  7. Map the user identity in the User Info endpoint response to enable the system to parse it into the AccelByte account.

    Image shows the Token Claims Mapping form

  8. The system redirects you to the details page of the OIDC you created. Click Activate to activate the OIDC.

    Image shows the OIDC details page

Create an IAM client for OIDC

An IAM client is a representation of the game client that you want to release on your target platform.

If you already have an IAM Client for your game on a specific SDK Platform (e.g., Xbox, Steam, or Playstation), you don't have to create a new IAM Client. Since an IdP is not a platform on which to build games, you can use an existing IAM Client. To learn more about IAM Clients, see the Manage Access control for applications article.

In-game login

The setup for each game engine is different. Follow the steps applicable to your game engine.

Unreal Engine in-game login integration

To seamlessly integrate your game with the AccelByte SDK for in-game sign-in and enable your players to access games using OIDC credentials, an approach is taken due to IdP support for Unreal Engine 4. The implementation needs web login.

Unreal Engine preparation and configuration

Adding dependency

Start by incorporating the AccelbyteUe4Sdk public dependency modules into your build.cs file. This linkage facilitates the integration of the AccelByte SDK Plugin into your Unreal Engine project.

public ABThirdPartyLogin(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;

PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" , "AccelByteUe4Sdk", "HTTP"});
}

Since Unreal Engine lacks support for URI redirection from external browsers, you must utilize an in-game browser within Unreal itself. This requires adding private dependency modules to your build.cs file as follows:

PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore", "UMG", "WebBrowser", "WebBrowserWidget" });

Add AccelbyteUe4Sdk in <YourProject>.Target.cs.

public ABThirdPartyLoginTarget( TargetInfo Target) : base(Target)
{
Type = TargetType.Game;
DefaultBuildSettings = BuildSettingsVersion.V2;
ExtraModuleNames.AddRange( new string[] { "ABThirdPartyLogin", "AccelByteUe4Sdk" } );
}

And <YourProjectEditor>.Target.cs

public ABThirdPartyLoginEditorTarget( TargetInfo Target) : base(Target)
{
Type = TargetType.Editor;
DefaultBuildSettings = BuildSettingsVersion.V2;
ExtraModuleNames.AddRange( new string[] { "ABThirdPartyLogin", "AccelByteUe4Sdk" } );
}

UE project settings for OIDC login

Add your AccelByte credentials in your DefaultEngine.ini file.

[/Script/AccelByteUe4Sdk.AccelByteSettings]
ClientId=<Your Client_Id>
ClientSecret=<Your Client_Secret>
Namespace=<Your Namespace>
PublisherNamespace=<Your Publisher Namespace>
RedirectURI="http://127.0.0.1"
BaseUrl="https://prod.gamingservices.accelbyte.io"
IamServerUrl="https://prod.gamingservices.accelbyte.io/iam"
PlatformServerUrl="https://prod.gamingservices.accelbyte.io/platform"

Sample code implementation

The subsequent step is to implement the OIDC authentication method for your game using the provided sample code. Begin by creating a C++ class; for instance, the following code will utilize an actor class.LoginActor.h.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "SWebBrowser.h"
#include "HttpModule.h"
#include "Core/AccelByteRegistry.h"
#include "Api/AccelByteUserApi.h"
#include "Core/AccelByteApiClient.h"
#include "Core/AccelByteMultiRegistry.h"
#include "Core/AccelByteMultiRegistry.h"
#include "Core/AccelByteError.h"

#include "LoginActor.generated.h"

DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FActionInfo, FString, Message);

UCLASS()
class ABTHIRDPARTYTEST_API ALoginActor : public AActor
{
GENERATED_BODY()

public:
// Sets default values for this actor's properties
ALoginActor();
UPROPERTY(BlueprintAssignable)
FActionInfo OnActionInfoUpdated;

private:
TSharedPtr<SWebBrowser> SlateBrowserWidget;
FHttpModule* HttpModule;

protected:
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "OIDC Settings")
FString ClientID;

UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "OIDC Settings")
FString ClientSecret;

UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "OIDC Settings")
FString RedirectURI;

UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "OIDC Settings")
FString AuthorizationUrl;

UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "OIDC Settings")
FString TokenUrl;

UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "OIDC Settings")
FString ServerURL;

UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "OIDC Settings")
FString ResponseType;

UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "OIDC Settings")
FString OIDCScope;

UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "OIDC Status")
FString LoginStatus;

UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "OIDC Status")
bool bIsLoggedIn;

UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "OIDC Status")
FString AccessToken;

UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "OIDC Status")
FString IdToken;

public:
UFUNCTION(BlueprintCallable, Category = "OIDC")
void ExecBroadcast();

void HandleUrlChanged(const FText& Text);

void OnAccessTokenReceived(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful);
void ParseAndHandleTokens(const FString& ResponseBody);
void LoginWithOtherPlatformId(const FString& PlatformId, const FString& Token, const FVoidHandler& OnSuccessDelegate);

protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;

public:
// Called every frame
virtual void Tick(float DeltaTime) override;
};

Obtain an ID Token or Access Token

Obtaining an ID Token or Access Token in your application is split into two scenarios:obtaining tokens using the Authorization Code and handling ID Tokens directly.

Obtain token using the Authorization Code

If your application requires the Authorization Code as the initial step to acquire the ID Token or Access Token and doesn't support the Implicit (Hybrid) Grant Type, follow these steps.

  1. Initialization and configuration: Ensure your application is properly configured with the required credentials, URLs, and scopes.
  2. Web-based authorization: Initiate the authorization process by constructing the Authorization URL with necessary parameters, including response_type, client_id, redirect_uri, scope, and more. Launch a web browser widget to present the authorization page to the user.
  3. URL change handling: Implement a URL change handler to capture the URL changes in the web browser widget. Detect if the URL contains an Authorization Code parameter.
  4. Token exchange: If an Authorization Code is detected, construct a request to exchange the code for an access token and possibly an ID token. Send a POST request to the Token URL with the required form data (e.g., code, client_id, client_secret, redirect_uri, and grant_type).
  5. Token parsing and handling: Parse the JSON response from the token request. If the response contains an access_token field, extract the access token and ID token (if applicable). Use these tokens as needed for further operations.
  6. Log in to AGS: If tokens are obtained, attempt to log in to AGS using the LoginWithOtherPlatformId method. Handle the success and error scenarios accordingly.
  7. Cleanup: Hide or close the web browser widget as needed.

Handle ID tokens directly

In situations where the response URL directly contains a Platform ID Token fragment, follow these steps:

  1. Initialization and configuration: Ensure your application is properly configured with the required credentials, URLs, and scopes.
  2. Web-based authorization: Initiate the authorization process by constructing the Authorization URL with parameters. Launch a web browser widget.
  3. URL change handling: Detect if the URL contains a ID Token fragment.
  4. Token handling: If a ID Token fragment is detected, handle the token directly as a string. Parse any necessary information from the token.
  5. Log in to AGS: Attempt to log in to AGS using the LoginWithOtherPlatformId method. Handle the success and error scenarios.
  6. Cleanup: Hide or close the web browser widget.

After following these steps and utilizing the provided code snippets, you can efficiently obtain ID Tokens or Access Tokens in your application based on your specific requirements.

Code example

LoginActor.cpp

ALoginActor::ALoginActor()
{
// Set this actor to call Tick() every frame.
PrimaryActorTick.bCanEverTick = false;
HttpModule = &FHttpModule::Get();

// Initialize variables with your desired values.
ClientID = TEXT("Your_IdentityProvider_ClientId");
ClientSecret = TEXT("Your_IdentityProvider_ClientSecret");// Used to exchange the authorization_code into idtoken or access token using /oauth2/default/v1/token endpoint.
RedirectURI = TEXT("http://localhost:8080/login/callback");
AuthorizationUrl = TEXT("https://{Your_Identity Provider_BaseUrl}/oauth2/default/v1/authorize");
TokenUrl = TEXT("https://{Your_Identity Provider_BaseUrl}/oauth2/default/v1/token");
OIDCScope = TEXT("openid profile email phone address");
}

// Called when the game starts or when spawned.
void ALoginActor::BeginPlay()
{
Super::BeginPlay();
}

void ALoginActor::ExecBroadcast()
{
// Please be advised that this can be different. Sync it with your own requested form data.
FString AuthorizationURL = AuthorizationUrl + "?response_type=" + ResponseType + "&client_id=" + ClientID +
"&redirect_uri=" + RedirectURI + "&scope=" + OIDCScope + "&state=foo" + "&nonce=nonce";
OnActionInfoUpdated.Broadcast(FString::Printf(TEXT("Trying To Login First")));
TSharedRef<SVerticalBox> MainContent = SNew(SVerticalBox)
+ SVerticalBox::Slot()
[
// WebBrowserWidget will launch in-game browser.
SAssignNew(SlateBrowserWidget, SWebBrowser)
.InitialURL(AuthorizationURL)
.ShowControls(false)
.ShowAddressBar(false)
.ShowErrorMessage(true)
.SupportsTransparency(false)
.SupportsThumbMouseButtonNavigation(false)
.ShowInitialThrobber(true)
.PopupMenuMethod(TOptional<EPopupMethod>())
.ViewportSize(FVector2D::ZeroVector)
.OnUrlChanged_Lambda([this](const FText& Text)
{
FString UrlString = Text.ToString();
UE_LOG(LogTemp, Warning, TEXT("WebBrowser URL changed: %s"), *UrlString);
FText FullUrlText = FText::FromString(UrlString); // Assuming UrlString is the full URL
HandleUrlChanged(FullUrlText);
})];

if (GEngine && GEngine->GameViewport)
{
GEngine->GameViewport->AddViewportWidgetContent(SlateBrowserWidget.ToSharedRef());
}
}

void ALoginActor::HandleUrlChanged(const FText& Text)
{
UE_LOG(LogTemp, Warning, TEXT("The link has changed: %s"), *Text.ToString());
FString UrlString = Text.ToString();

// Check if the URL contains a query parameter or a fragment.
FString AuthorizationCode;
FString PlatformIdToken;

if (UrlString.Contains(TEXT("?code=")))
{
AuthorizationCode = FPlatformHttp::UrlDecode(UrlString.RightChop(UrlString.Find(TEXT("?code=")) + 6));
}
else if (UrlString.Contains(TEXT("#id_token=")))
{
PlatformIdToken = FPlatformHttp::UrlDecode(UrlString.RightChop(UrlString.Find(TEXT("#id_token=")) + 10));
}

// Process the authorization code or ID token.
if (!AuthorizationCode.IsEmpty())
{
// Build the request form data.
FString FormData = TEXT("code=") + AuthorizationCode
+ TEXT("&client_id=") + ClientID
+ TEXT("&client_secret=") + ClientSecret
+ TEXT("&redirect_uri=") + RedirectURI
+ TEXT("&grant_type=authorization_code");

// Make a POST request to exchange the authorization code for an access token or ID Token.
TSharedRef<IHttpRequest> HttpRequest = HttpModule->CreateRequest();
HttpRequest->SetURL(TokenUrl);
HttpRequest->SetVerb(TEXT("POST"));
HttpRequest->SetHeader(TEXT("Content-Type"), TEXT("application/x-www-form-urlencoded"));
HttpRequest->SetContentAsString(FormData);
HttpRequest->OnProcessRequestComplete().BindUObject(this, &ALoginActor::OnAccessTokenReceived);
HttpRequest->ProcessRequest();

// Hide or close the SWebBrowser widget.
if (SlateBrowserWidget.IsValid())
{
SlateBrowserWidget->SetVisibility(EVisibility::Collapsed);
}
}
else if (!PlatformIdToken.IsEmpty())
{
OnActionInfoUpdated.Broadcast(FString::Printf(TEXT("Platform ID Token : %s"), *PlatformIdToken));

// Call the common handling function for tokens.
ParseAndHandleTokens(PlatformIdToken);

// Hide or close the SWebBrowser widget.
if (SlateBrowserWidget.IsValid())
{
SlateBrowserWidget->SetVisibility(EVisibility::Collapsed);
}
}
}

void ALoginActor::OnAccessTokenReceived(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful)
{
if (bWasSuccessful && Response.IsValid())
{
FString ResponseBody = Response->GetContentAsString();
// Parse the JSON response and handle tokens.
ParseAndHandleTokens(ResponseBody);
}
}

void ALoginActor::ParseAndHandleTokens(const FString& ResponseBody)
{
// Remove any line breaks and extra spaces from the response body.
FString CleanedResponseBody = ResponseBody.Replace(TEXT("\n"), TEXT("")).Replace(TEXT(" "), TEXT(""));
UE_LOG(LogTemp, Warning, TEXT("Response Body: %s"), *ResponseBody);
const FVoidHandler OnLoginSuccessDelegate = FVoidHandler::CreateLambda([=]()
{
FAccountUserData AccountUserData = AccelByte::FRegistry::Credentials.GetAccountUserData();
FString AccountUserDataString;
FJsonObjectConverter::UStructToJsonObjectString(AccountUserData, AccountUserDataString);
FString Message = FString::Printf(TEXT("Login to AB Service Success : %s"), *AccountUserDataString);
OnActionInfoUpdated.Broadcast(Message);
UE_LOG(LogTemp, Warning, TEXT("Login to AB Service Success: %s"), *AccountUserDataString);
});
if (CleanedResponseBody.Contains(TEXT("access_token")))
{
TSharedPtr<FJsonObject> JsonObject;
TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(CleanedResponseBody);
if (FJsonSerializer::Deserialize(Reader, JsonObject))
{
AccessToken = JsonObject->GetStringField(TEXT("access_token"));
IdToken = JsonObject->GetStringField(TEXT("id_token"));

// Use the access token and ID token as needed.
UE_LOG(LogTemp, Warning, TEXT("Access Token: %s"), *AccessToken);
UE_LOG(LogTemp, Warning, TEXT("ID Token: %s"), *IdToken);
OnActionInfoUpdated.Broadcast(FString::Printf(TEXT("Platform Access Token : %s"), *AccessToken));
OnActionInfoUpdated.Broadcast(FString::Printf(TEXT("Platform ID Token : %s"), *IdToken));
// Try to login into AGS Endpoint
if (!AccessToken.IsEmpty() || !IdToken.IsEmpty())
{
LoginWithOtherPlatformId("oidctest", IdToken, OnLoginSuccessDelegate);
}
}
else
{
FString ErrorMsg = Reader->GetErrorMessage();
UE_LOG(LogTemp, Error, TEXT("Failed to parse JSON response. Error: %s"), *ErrorMsg);
}
}
else if (!CleanedResponseBody.IsEmpty()) // Check if the response body is not empty.
{
// Handle ID token directly as a string.
IdToken = CleanedResponseBody;

// Use the ID token as needed.
UE_LOG(LogTemp, Warning, TEXT("ID Token: %s"), *IdToken);
OnActionInfoUpdated.Broadcast(FString::Printf(TEXT("Platform ID Token : %s"), *IdToken));

// Try to log in to AGS Endpoint.
LoginWithOtherPlatformId("oidctest", IdToken, OnLoginSuccessDelegate);
}
}

void ALoginActor::LoginWithOtherPlatformId(const FString& PlatformId, const FString& Token, const FVoidHandler& OnSuccessDelegate)
{
OnActionInfoUpdated.Broadcast(FString::Printf(TEXT("Login to AGS")));

FAccountUserData AccountUserData = AccelByte::FRegistry::Credentials.GetAccountUserData();
AccelByte::FRegistry::User.LoginWithOtherPlatformId(PlatformId, Token, OnSuccessDelegate, LoginTestFErrorOAuthHandler);

UE_LOG(LogTemp, Warning, TEXT("Request LoginWithOtherPlatform"));
OnActionInfoUpdated.Broadcast(TEXT("Request LoginWithOtherPlatform"));
}

// Called every frame
void ALoginActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
important

The provided code serves as an illustrative example and guide only. While it offers insights into the process of obtaining tokens, it's imperative to recognize that real-world implementations might involve additional considerations and limitations. Ensure rigorous error handling, follow best security practices, and conduct comprehensive testing to establish a resilient authentication and token retrieval system tailored to your application's specific requirements.

Sample code login log

PIE: Server logged in
PIE: Play in editor total start time 0.317 seconds.
LogViewport: Scene viewport resized to 1282x730, mode Windowed.
LogAccelByte: Warning: [Compatibility] Version mismatch: Expected "group" version "2.11.0"-"2.11.0", found "2.18.1"
LogTemp: Warning: The link has changed: https://dev-23268164.okta.com/oauth2/default/v1/authorize?response_type=code&client_id=0oaaomiaanDOioaIe5d7&redirect_uri=http://localhost:8080/login/callback&scope=openid%20profile%20email%20phone%20address&state=foo
LogTemp: Warning: The link has changed: https://dev-23268164.okta.com/oauth2/default/v1/authorize?response_type=code&client_id=0oaaomiaanDOioaIe5d7&redirect_uri=http://localhost:8080/login/callback&scope=openid%20profile%20email%20phone%20address&state=foo
LogTemp: Warning: The link has changed: https://dev-23268164.okta.com/signin/refresh-auth-state/00JnYaV0WU7hlAu9AXJSC-GtLmPAUwtfEpBP_SHCxO
LogTemp: Warning: The link has changed: https://dev-23268164.okta.com/
LogTemp: Warning: The link has changed: http://localhost:8080/login/callback?code=cnV2E9gIfouXrtaQ9rG347W2o9q7yZ3COO5H3-EfTYI&state=foo
LogTemp: Warning: Access token response: {"token_type":"Bearer","expires_in":3600,"access_token":"xxxxxx","scope":"openid profile address phone email","id_token":"xxxxxx"}
LogTemp: Warning: Access Token: xxxxxx
LogTemp: Warning: ID Token: xxxxxx
LogTemp: Warning: Request LoginWithOtherPlatform
LogTemp: Warning: Login to AB Service Success: {
"authType": "EMAILPASSWD",
"bans": [],
"country": "ID",
"createdAt": "2023-08-30T01:58:08.038119Z",
"dateOfBirth": "",
"displayName": "Ken",
"emailVerified": false,
"enabled": true,
"lastEnabledChangedTime": "0001-01-01T00:00:00Z",
"loginId": "",
"namespace": "sdktest",
"permissions": [],
"phoneVerified": false,
"platformId": "",
"platformUserId": "",
"roles": [
"2251438839e948d783ec0e5281daf05b"
],
"userId": "415271c4ef874a99aa81aa6d0a999e00",
"username": "",
"emailAddress": "",
"avatarUrl": "",
"deletionDate": "",
"deletionStatus": false,
"lastDateOfBirthChangedTime": "0001.01.01-00.00.00",
"namespaceRoles": [
{
"namespace": "*",
"roleId": "2251438839e948d783ec0e5281daf05b"
}
],
"newEmailAddress": "",
"oldEmailAddress": "",
"phoneNumber": "",
"platformAvatarUrl": "",
"platformDisplayName": ""
}

Sample code testing

The provided code has been successfully tested using Okta in the Unreal Engine editor. After clicking the sign-in button on Okta login, you will be automatically logged in.

Image shows sample code for Unreal code testing

Extend SDK Login

You can integrate your OIDC and sign in using OIDC credentials through Extend SDK.

func main() {
mux := http.NewServeMux()

// proceed
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, "./templates/index.html")
})

// config
mux.HandleFunc("/config", func(w http.ResponseWriter, r *http.Request) {
if r.Method == "GET" {
oidcConfig := struct {
Scope string `json:"scope"`
PlatformId string `json:"platform_id"`
DiscoveryUrl string `json:"discovery_url"`
ClientSecret string `json:"client_secret"`
ClientId string `json:"client_id"`
}{"openid profile email", platformId, discoveryUrl, os.Getenv("CLIENT_SECRET"), os.Getenv("CLIENT_ID")}

data, err := json.Marshal(oidcConfig)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)

return
}

w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)
_, err = w.Write(data)
if err != nil {
return
}
}
})

// callback
mux.HandleFunc("/callback", func(w http.ResponseWriter, r *http.Request) {
form := url.Values{}
form.Add("grant_type", "authorization_code")
form.Add("client_id", os.Getenv("CLIENT_ID"))
form.Add("client_secret", os.Getenv("CLIENT_SECRET"))
form.Add("redirect_uri", "http://localhost")
form.Add("code", r.URL.Query().Get("code"))

post, errPost := http.PostForm("url", form)
if errPost != nil {
return
}
bodyBytes, err := io.ReadAll(post.Body)
if err != nil {
log.Fatal(err)
}
response := token{}
errUnmarshal := json.Unmarshal(bodyBytes, &response)
if errUnmarshal != nil {
return
}

// Login Platform from AGS
loginPlatform(*response.IDToken)

// Try to call AGS after login success
getCountryLocation()

w.WriteHeader(http.StatusOK)
w.Header().Set("Content-Type", "application/text")
_, err = w.Write([]byte("Success Login"))
if err != nil {
return
}
})

log.Printf("listening on http://%s/", "127.0.0.1:8080")
log.Fatal(http.ListenAndServe("127.0.0.1:8080", mux))
}

// Login platform using Go Extend SDK wrapper
func loginPlatform(platformToken string) {
input := &o_auth2_0.PlatformTokenGrantV3Params{
ClientID: &clientIDPhantAuth,
CreateHeadless: &createHeadless,
PlatformID: platformId,
PlatformToken: &platformToken,
}
err := oauth.LoginPlatform(input)
if err != nil {
log.Fatal("failed login platform")

return
} else {
log.Printf("successful login")
}
}

// Get country location using Go Extend SDK wrapper
func getCountryLocation() {
// Try to call AGS service
}

type token struct {
AccessToken *string `json:"access_token"`
IDToken *string `json:"id_token"`
RefreshToken *string `json:"refresh_token"`
TokenType *string `json:"token_type"`
}