OIDC をアイデンティティプロバイダーとして設定する
注釈:本資料はAI技術を用いて翻訳されています。
Overview
このガイドの目的は、OpenID Connect (OIDC) を AccelByte Gaming Services (AGS) と統合できるようにすることです。3 つの OIDC 認証タイプ(Authorization Code、ID Token、Access Token)をサポートしています。
ゲームによっては、ここに記載されていない追加のアイデンティティプロバイダー機能を設定する必要がある場合があります。OIDC サービスの設定に関する詳細は、お使いのアイデンティティプロバイダーのドキュメントを確認することをお勧めします。
このガイドは一般公開用であり、機密保持の観点から掲載できる情報が限られています。まずは機密版の完全なガイドを参照することをお勧めします。機密版ガイドのコピーが必要な場合は、担当の AccelByte テクニカルプロデューサーにお問い合わせください。
Goals
- AccelByte の Game SDK を使用して、ゲームに OIDC 認証方式を有効にします。
- AGS の Player Portal を使用して、パブリッシャーの Web サイトに OIDC 認証方式を有効にします。
Prerequisites
Web ログイン統合
- OIDC をサポートするアイデンティティプロバイダー。
- 認証を設定し権限を管理するための AccelByte Admin Portal アカウント。
- Player Portal と Launcher 用のパブリッシャー Namespace
- Player Portal。
Web ログイン統合は現在 AGS Public Cloud では利用できず、AGS Private Cloud のみでサポートされています。
インゲームログイン統合
- OIDC をサポートするアイデンティティプロバイダー。
- 認証を設定し権限を管理するための AGS Admin Portal アカウント。
- ゲーム Namespace。
- AccelByte の Game SDK をインポートした Unity または Unreal のゲームプロジェクト。
- 最新バージョンの AccelByte Unity SDK または Unreal SDK。
- AGS Identity and Access Management (IAM) クライアントに関する知識。
Authorization Code type
Authorization Code タイプは、標準の Authorization Code フローを使用した認証と、JSON Web Key Set (JWKS) による ID トークンの検証の両方をサポートします。Authorization Code は Web フローベースの認証 です。システムはアイデンティティプロバイダーのログインページにリダイレクトし、認証後にアクセス権を付与した状態で AccelByte のポータルに戻します。
IdP を設定する
以下の手順に従って、ゲームにアイデンティティプロバイダー (IdP) の OIDC ログインを設定します。これにより、プレイヤーは自分のアカウントを使用して Admin Portal、Player Portal、Launcher にサインインできるようになります。
ここでは、OIDC の Player Portal と Admin Portal のログインを設定する例として Okta OIDC を使用します。Authorization Code の場合、アイデンティティプロバイダーの OIDC を AGS と統合するために以下の項目が必要です。
- JWKS URL
- Issuer
- Client ID
- Client Secret
- Authorization Request URL
- Token Endpoint URL
例として、アイデンティティプロバイダーに Okta を使用します。OIDC をサポートする他の IdP を使用することもできます。
Okta アプリケーションを作成する
Okta の Create OIDC app integrations ガイドに従って、Okta アカウント内に Okta アプリケーションを作成します。
この手順で生成したクライアント ID とクライアントシークレットのコピーを保管しておいてください。Web ログインを設定する際に必要になります。
Okta OIDC 設定を取得する
次の URL を使用して、アプリの openid-configuration を開きます: https://<YOUR_OKTA_URL>.okta.com/oauth2/default/.well-known/openid-configuration。
OIDC Authorization Code タイプのログインを設定するために、以下の項目をコピーする必要があります。
- JWKS URL
- Issuer
- Authorization Request URL
- Token Endpoint URL
Web ログインを設定する
Player Portal と Admin Portal で OIDC ログインを設定し、プレイヤーが自分の IdP アカウントを使用して Player Portal にサインインできるようにするには、以下の手順に従います。
-
AGS Admin Portal で、パブリッシャー namespace に移動します。
-
サイドバーメニューで、Foundations > 3rd Party Integrations > Auth & Account Linking に移動します。
-
Auth & Account Linking ページで、+ Add New ボタンをクリックします。

-
ログイン方式オプションの一番下にある + Create OIDC をクリックします。

-
Create OpenID Connect (OIDC) フォームが表示されます。必要な情報を入力します。この例では Authorization Code タイプを使用します。
- Platform Name: わかりやすい名前を入力します。例:
oidcokta。 - Platform ID: Okta アプリの
Valid RedirectURIとして使用するプラットフォーム ID を入力します。例:oidcokta。 - Authentication type: Authorization Code を選択します。
- JWKS URL: Okta の
openid設定 URL にある jwks_url を使用します。 - Issuer: Okta の
openid設定 URL にある issuer を使用します。 - Client ID: Okta アプリケーションを作成する手順で取得した Okta Client ID を使用します。
- Client Secret: Okta アプリケーションを作成する手順で取得した Okta Client Secret を使用します。
- Authorization Request URL: Okta の
openid設定 URL にある authorization_endpoint を使用します。 - Token Endpoint URL: Okta の
openid設定 URL にある token_endpoint を使用します。 - Clients: 使用状況に応じて、この OIDC にクライアントを設定することで、Player Portal、Launcher、Admin Portal、In-Game ログインで OIDC ログインを使用できます。例えば、OIDC ログインを Player Portal と Admin Portal に統合したい場合は、ここに Player Portal Website IAM Client と Admin Portal Website IAM Client を追加する必要があります。
- Scopes (Optional): デフォルトでは openid email profile に設定されています。


- Platform Name: わかりやすい名前を入力します。例:
-
Next をクリックします。
-
Token Claims Mapping フォームで、システムが ID トークン内のユーザーアイデンティティを解析して AccelByte アカウントに変換できるようにマッピングします。

-
Next をクリックします。
-
(オプション) Domains セクションでは、ドメインを登録し、ユーザーにロールを割り当てることができます。
注記Register Domain は、指定したドメイン配下のユーザーに自動的にロールを割り当てたい場合に便利です。例えば、OIDC ログインが Admin Portal ログインに使用されている場合、
<your-domain>配下のユーザーが OIDC ログインを使用してログインした後に、そのユーザーに自動的に Admin ロールを付与したい場合に使用します。-
この手順をスキップするには、Create をクリックします。

-
ドメインを登録するには、+ Register Domain をクリックします。次に、Register Domain フォームの以下のフィールドに入力します。フィールドの入力後、Save をクリックし、続けて Create をクリックします。

- Domain Name: ドメイン名を入力します。この例では
accelbyte.netを使用します。 - Clients: ユーザーがクライアントに初めてログインしたとき、システムはデフォルトのロールをそのユーザーに付与します。この例では、ユーザーが Admin Portal で OIDC を使用してログインした際に Admin ロールを付与したいので、Admin Portal Website IAM Client に設定します。
- Default Role: ユーザーに付与したいロールです。
- Default Namespace: デフォルトロールがグローバルロールでない場合、対象の Namespace を指定します。
- Domain Name: ドメイン名を入力します。この例では
-
-
システムは作成した OIDC の詳細ページにリダイレクトします。Activate をクリックして OIDC を有効化します。

OIDC を使用した Player Portal からのユーザーログイン
Publisher Namespace で OIDC ログイン方式を設定し、Player Portal Website Client を OIDC クライアントとして設定していることを確認してください。
-
Player Portal に移動し、Login をクリックします。

-
More Login Options をクリックします。

-
設定した OIDC のログインオプションをクリックします。この例では Okta を使用します。
-
システムは Okta のログインページにリダイレクトします。認証情報を入力してログインします。
注記ログインに成功すると、システムはプレイヤーに OIDC アカウントを既存の AccelByte アカウントとリンクするか、新しいアカウントを作成するように要求します。さらに、ヘッドレスアカウントを使用してプレイヤーが直接 Player Portal にログインできるように(つまり登録フォームを必要とせずに)プラットフォームを設定することもできます。
OIDC を使用した Admin Portal からのユーザーログイン
Publisher Namespace で OIDC ログイン方式を設定し、Admin Portal Website Client を OIDC クライアントとして設定していることを確認してください。
Admin Portal にアクセスするには、ユーザーに Admin Role を付与する必要があります。ただし、既にドメインを OIDC ログイン方式として登録し、Admin ロールを付与している場合、システムはそのドメインに関連付けられた認証済み OIDC ユーザーに自動的にその Admin ロールを付与します。
-
Admin Portal に移動します。システムがログインページにリダイレクトします。OIDC のロゴをクリックします。ロゴが複数ある場合は、ボタンにマウスを合わせて、正しい OIDC ログインを選択していることを確認してください。

-
システムは Okta Login ページにリダイレクトします。認証情報を使用してログインします。

-
システムは Admin Portal にリダイレクトします。

ポータルへのアクセスは、割り当てられているロールに基づきます。例えば、割り当てられたロールが AccelByte namespace に対する Admin Portal Read Only の場合、そのロールで許可された機能のみにアクセスできます。

ID token type
ID Token タイプは、JWKS を介して ID トークンを検証することで認証をサポートします。ID Token はインゲームフローベースの認証向けです。アイデンティティプロバイダーから ID トークンを生成する必要があります。これを AccelByte のアクセストークンと交換できます。
IdP を設定する
以下の手順に従って、ゲームにアイデンティティプロバイダー (IdP) の OIDC ログインを設定します。これにより、プレイヤーは自分のアカウントを使用してゲームにサインインできるようになります。
ID トークンを取得するために、IdP からの認可を処理する必要があります。IdP の OIDC を AGS と統合するには、以下の項目が必要です。
- JWKS URL
- Issuer
- Client ID
例として、Facebook を IdP として使用します。OIDC をサポートする他の IdP を使用することもできます。
Facebook アプリケーションを作成する
Facebook の Create an App ガイドに従って、Facebook Developer アカウント内に Facebook アプリケーションを作成します。
アプリに Facebook Login プロダクトを追加する
Facebook Login for the Web with the JavaScript SDK ガイドに従って、アプリに Facebook Login プロダクトを追加します。
インゲームログインを設定する
以下の手順に従って、ゲーム内で OIDC ログインを設定します。例として Facebook OIDC を使用します。これにより、プレイヤーはアイデンティティプロバイダーのアカウントを使用してゲームにサインインできるようになります。
-
AGS Admin Portal で、ゲーム namespace に移動します。
-
サイドバーメニューで、Foundations > 3rd Party Integrations > Auth & Account Linking に移動します。
-
Auth & Account Linking ページで、+ Add New ボタンをクリックします。

-
ログイン方式オプションの一番下にある + Create OIDC をクリックします。

-
Create OpenID Connect (OIDC) フォームで、必要なフィールドに入力します。この例では認証タイプとして ID Token を使用します。
- Platform Name: ログイン方式の名前を入力します。例:
oidcfacebook。 - Platform ID: ログイン方式の ID を入力します。例:
oidcfacebook。 - Authentication type: ID Token を選択します。
- JWKS URL: この例では Facebook OIDC を使用しているため、https://www.facebook.com/.well-known/oauth/openid/jwks/ を使用します。
- Issuer: この例では Facebook OIDC を使用しているため、
[https://www.facebook.com](https://www.facebook.com)を使用します。

- Platform Name: ログイン方式の名前を入力します。例:
-
Next をクリックします。
-
システムが ID トークン内のユーザーアイデンティティを解析して AccelByte アカウントに変換できるようにマッピングします。トークンクレームの詳細については、Claims を参照してください。

-
システムは作成した OIDC の詳細ページにリダイレクトします。Activate をクリックして OIDC を有効化します。

Access Token type
Access Token タイプは、UserInfo API エンドポイントを介してアクセストークンを検証することで認証をサポートします。Access Token タイプは、IdP が ID トークンを提供できないものの、アクセストークンとともに使用できる User Info エンドポイントを持っている場合のカスタム要件向けです。
IdP を設定する
以下の手順に従って、ゲームに IdP の OIDC ログインを設定します。これにより、プレイヤーは自分のアカウントを使用してゲームにサインインできるようになります。
Access Token タイプを使用するには、IdP の OIDC を AGS と統合するために以下の項目が必要です。
- User Info エンドポイント URL
- User Info エンドポイントの HTTP メソッド
例として、Okta を IdP として使用します。OIDC をサポートする他の IdP を使用することもできます。
Okta アプリケーションを作成する
Okta の Create OIDC app integrations ガイドに従って、Okta アプリケーションを作成します。
以下の URL を使用して、Okta アプリケーションのサインインリダイレクト URI を設定します。
<BaseURL>/iam/v3/platforms/<PlatformId>/authenticate<BaseURL>/iam/v3/public/namespaces/<namespace>/users/me/platforms/<platformId>/web/link/establish
BaseURLはドメインアドレスです。例:https://development.accelbyte.io。Namespaceはパブリッシャーまたはスタジオの namespace です。例:accelbyte。PlatformIdは、AGS Admin Portal で設定する OIDC のPlatformIdです。
インゲームログインを設定する
インゲームの OIDC ログインを設定し、プレイヤーが自分の IdP アカウントを使用してサインインできるようにするには、以下の手順に従います。
-
AGS Admin Portal で、ゲーム namespace に移動します。
-
サイドバーメニューで、Foundations > 3rd Party Integrations > Auth & Account Linking に移動します。
-
Auth & Account Linking ページで、+ Add New ボタンをクリックします。

-
ログイン方式オプションの一番下にある + Create OIDC をクリックします。

-
Create OpenID Connect (OIDC) フォームで、必要な情報を入力します。この例 (Okta) では、認証タイプとして Access Token を使用します。
- Platform Name: OIDC の名前を入力します。例:
oidc okta access token。 - Platform ID: OIDC の ID を入力します。
oktaaccesstoken。 - Authentication type: Access Token を選択します。
- UserInfo Endpoint: Okta アプリケーションのユーザー情報エンドポイントを使用します。例:
https://YOUR_OKTA_URL.okta.com/oauth2/v1/userinfo。 - HTTP Method: IdP が使用する対応する HTTP メソッドを入力します。例:
GET。

- Platform Name: OIDC の名前を入力します。例:
-
Next をクリックします。
-
システムが User Info エンドポイントのレスポンスを解析して AccelByte アカウントに変換できるように、ユーザーアイデンティティをマッピングします。

-
システムは作成した OIDC の詳細ページにリダイレクトします。Activate をクリックして OIDC を有効化します。

Create an IAM client for OIDC
IAM クライアントは、対象プラットフォームでリリースしたいゲームクライアントを表すものです。
特定の SDK プラットフォーム(例: Xbox、Steam、または PlayStation)向けにゲームの IAM クライアントを既に持っている場合、新しい IAM クライアントを作成する必要はありません。IdP はゲームを構築するためのプラットフォームではないため、既存の IAM クライアントを使用できます。IAM クライアントの詳細については、Manage Access control for applications の記事を参照してください。
In-game login
各ゲームエンジンごとに設定方法が異なります。お使いのゲームエンジンに適用される手順に従ってください。
- Unreal Engine
- Unity Engine
Unreal Engine in-game login integration
インゲームサインインのために AccelByte SDK とゲームをシームレスに統合し、プレイヤーが OIDC 認証情報を使用してゲームにアクセスできるようにします。Unreal Engine 4 の IdP サポートの制約上、ここでは特定のアプローチを採用しており、この実装には Web ログインが必要です。
Unreal Engine preparation and configuration
依存関係の追加
まず、AccelbyteUe4Sdk パブリック依存モジュールを build.cs ファイルに組み込みます。このリンクにより、AccelByte SDK プラグインを Unreal Engine プロジェクトに統合できます。
public ABThirdPartyLogin(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" , "AccelByteUe4Sdk", "HTTP"});
}
Unreal Engine は外部ブラウザからの URI リダイレクトをサポートしていないため、Unreal 自体の中でインゲームブラウザを利用する必要があります。これには、以下のように build.cs ファイルにプライベート依存モジュールを追加する必要があります。
PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore", "UMG", "WebBrowser", "WebBrowserWidget" });
<YourProject>.Target.cs に AccelbyteUe4Sdk を追加します。
public ABThirdPartyLoginTarget( TargetInfo Target) : base(Target)
{
Type = TargetType.Game;
DefaultBuildSettings = BuildSettingsVersion.V2;
ExtraModuleNames.AddRange( new string[] { "ABThirdPartyLogin", "AccelByteUe4Sdk" } );
}
そして <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
DefaultEngine.ini ファイルに AccelByte の認証情報を追加します。
[/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
次のステップは、提供されているサンプルコードを使用してゲームに OIDC 認証方式を実装することです。まず C++ クラスを作成します。例えば、以下のコードはアクター 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
アプリケーションで ID トークンまたはアクセストークンを取得する方法は、Authorization Code を使用してトークンを取得する方法と、ID トークンを直接処理する方法の 2 つのシナリオに分かれます。
Authorization Code を使用してトークンを取得する
アプリケーションが ID トークンまたはアクセストークンを取得する最初のステップとして Authorization Code を必要とし、Implicit (Hybrid) Grant Type をサポートしていない場合は、以下の手順に従います。
- 初期化と設定: アプリケーションが必要な認証情報、URL、スコープで適切に設定されていることを確認します。
- Web ベースの認可:
response_type、client_id、redirect_uri、scopeなどの必要なパラメーターを使用して Authorization URL を構築し、認可プロセスを開始します。Web ブラウザウィジェットを起動して、ユーザーに認可ページを表示します。 - URL 変更の処理: Web ブラウザウィジェット内での URL の変化を捕捉する URL 変更ハンドラーを実装します。URL に Authorization Code パラメーターが含まれているかを検出します。
- トークン交換: Authorization Code が検出された場合、そのコードをアクセストークンおよび(該当する場合)ID トークンと交換するリクエストを構築します。必要なフォームデータ(例:
code、client_id、client_secret、redirect_uri、grant_type)を含む POST リクエストを Token URL に送信します。 - トークンの解析と処理: トークンリクエストからの JSON レスポンスを解析します。レスポンスに
access_tokenフィールドが含まれている場合、アクセストークンと(該当する場合)ID トークンを抽出します。これらのトークンを後続の操作に必要に応じて使用します。 - AGS へのログイン: トークンが取得された場合、
LoginWithOtherPlatformIdメソッドを使用して AGS にログインを試みます。成功およびエラーのシナリオをそれぞれ適切に処理します。 - クリーンアップ: 必要に応じて Web ブラウザウィジェットを隠すか閉じます。
ID トークンを直接処理する
レスポンス URL に Platform ID Token フラグメントが直接含まれている場合は、以下の手順に従います。
- 初期化と設定: アプリケーションが必要な認証情報、URL、スコープで適切に設定されていることを確認します。
- Web ベースの認可: パラメーターを含む Authorization URL を構築して認可プロセスを開始します。Web ブラウザウィジェットを起動します。
- URL 変更の処理: URL に
ID Tokenフラグメントが含まれているかを検出します。 - トークンの処理:
ID Tokenフラグメントが検出された場合、トークンを文字列として直接処理します。トークンから必要な情報を解析します。 - AGS へのログイン:
LoginWithOtherPlatformIdメソッドを使用して AGS にログインを試みます。成功およびエラーのシナリオを処理します。 - クリーンアップ: Web ブラウザウィジェットを隠すか閉じます。
これらの手順に従い、提供されたコードスニペットを利用することで、特定の要件に基づいてアプリケーション内で ID トークンやアクセストークンを効率的に取得できます。
Code example
スニペット内で使用される AccelByteOnlineSubsystemPtr を取得するには、こちらのガイド に従ってください。
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);
}
}
//Manage these field as member of the class to make this accessible by each function in ALoginActor class
auto ApiClient = AccelByteOnlineSubsystemPtr->GetApiClient(TEXT("YOUR_KEY"));
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 = ApiClient->CredentialsRef->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 = ApiClient->CredentialsRef->GetAccountUserData();
auto UserApi = ApiClient->GetUserApi().Pin();
UserApi->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);
}
提供されているコードは、あくまで説明とガイドのための一例です。トークンを取得するプロセスについての知見を提供するものですが、実際の実装ではさらに多くの検討事項や制約が発生する可能性があることを認識しておく必要があります。厳格なエラーハンドリングを行い、セキュリティのベストプラクティスに従い、アプリケーション固有の要件に合わせた堅牢な認証およびトークン取得システムを構築するために、包括的なテストを実施してください。
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
提供されているコードは、Unreal Engine エディター内で Okta を使用して正常にテストされています。Okta のログインでサインインボタンをクリックすると、自動的にログインします。

Unity in-game login integration
OIDC を AccelByte SDK と統合してサインインすることで、プレイヤーが OIDC 認証情報を使用してゲームにログインできるようにすることができます。
Preparation and configuration
Unity project settings for AGS
AccelByte SDK がプロジェクト内で正しく動作するようにするには、事前に AccelByte Admin Portal で作成した値を入力し、以下の手順に従う必要があります。
-
JSON ファイルを作成し、ファイル名として
AccelByteSDKConfig.jsonを使用します。 -
AccelByteSDKConfig.jsonファイルをコピーし、Unity プロジェクトのAssets/Resourceディレクトリに追加します。 -
ゲームの情報を使用して
AccelByteSDKConfig.jsonファイルに入力します。以下は JSON ファイルの例です。{
"Default": {
"Namespace": "<Your Game Namespace>",
"UsePlayerPrefs": true,//It will use Player Preferences
"EnableDebugLog": true,//Enable Debug Logging
"DebugLogFilter": "Log",//Type of Debug Log
"BaseUrl": "https://prod.gamingservices.accelbyte.io",
"RedirectUri": "http://127.0.0.1",
"AppId": "<Your AppId>",
"PublisherNamespace": "<Your Publisher Namespace>"
}
} -
JSON ファイルを作成し、ファイル名として
AccelByteServerSDKConfig.jsonを使用します。次に、Unity プロジェクトのAssets/Resourceディレクトリに追加します。 -
AccelByteServerSDKConfig.jsonファイルに、ゲームサーバーを設定するための以下のコードを追加します。以下は JSON ファイルの例です。{
"Default": {
"Namespace": "<Your Game Namespace>",
"BaseUrl": "https://prod.gamingservices.accelbyte.io",
"RedirectUri": "http://127.0.0.1"
}
} -
以下のファイル名で 2 つの JSON ファイルを作成します。
AccelByteSDKOAuthConfig.jsonAccelByteServerSDKOAuthConfig.json
次に、両方のファイルを Unity プロジェクトの
Assets/Resourcesディレクトリに追加します。これらの JSON ファイルの内容は以下のようになります。{
"Default": {
"ClientId": "<Your IAM Client ID>",
"ClientSecret": "<Your IAM Client Secret>"
}
}
Sample code implementation
次のステップでは、サンプルコードを使ってゲームに OIDC 認証方式を実装する方法について説明します。
OIDC API を呼び出すためのカスタム SDK を作成する必要があります。以下は例です。
public class OidcAuthCodeLoginHandler : MonoBehaviour
{
[SerializeField] Button OidcAuthCodeLoginButton;
[SerializeField] Text LoginStatus;
[SerializeField] private string clientId = "<YOUR_CLIENT_ID>";
[SerializeField] private string clientSecret = "<YOUR_CLIENT_SECRET>";
[SerializeField] private string redirectUri = "<YOUR_REDIRECT_URI>";
[SerializeField] private string authorizationUrl = "<YOUR_OIDC_AUTH_URL>";
[SerializeField] private string tokenUrl = "YOUR_OIDC_TOKEN_URL";
string[] scopes = { "<YOUR_OIDC_SCOPE>" };
// AccelByte's Multi Registry references
private ApiClient apiClient;
private User user;
private string serverURL = "<YOUR_SERVER_URL>"; // Replace with your server URL.
private HttpListener listener;
private void OnEnable()
{
LoginStatus.text = "Please Login";
OidcAuthCodeLoginButton.onClick.AddListener(() =>
{
LoginStatus.text = "Attempting Login";
StartLogin();
});
}
public void StartLogin()
{
string authorizationURL = $"{authorizationUrl}?response_type=code&client_id={clientId}&redirect_uri={redirectUri}&scope={string.Join(" ", scopes)}&state=foo";
// Start the local server
StartServer();
// Open the authorization URL in the default browser or WebView.
Application.OpenURL(authorizationURL);
// Wait for the response URL from the local server.
StartCoroutine(WaitForResponseURL());
}
private void StartServer()
{
listener = new HttpListener();
listener.Prefixes.Add(serverURL + "/");
listener.Start();
// Start a new thread to handle incoming HTTP requests.
ThreadPool.QueueUserWorkItem(HandleRequests);
}
private string authorizationCode;
private void HandleRequests(object state)
{
while (listener.IsListening)
{
try
{
HttpListenerContext context = listener.GetContext();
string responseURL = context.Request.Url.ToString();
// Check if the response URL contains the authorization code.
if (responseURL.Contains("code="))
{
// Extract the authorization code from the response URL.
authorizationCode = ExtractAuthorizationCode(responseURL);
if (authorizationCode != null)
{
// Continue with your authentication flow using the authorization code
// Send a response to the browser indicating success
byte[] responseBytes = System.Text.Encoding.UTF8.GetBytes("Authorization code received");
context.Response.OutputStream.Write(responseBytes, 0, responseBytes.Length);
context.Response.OutputStream.Close();
// Stop the local server
listener.Stop();
listener.Close();
// Call ExchangeAuthorizationCode on the main Unity thread
UnityMainThreadDispatcher.Instance().Enqueue(ExchangeAuthorizationCode());
break;
}
}
}
catch (Exception e)
{
Debug.LogError("Error handling request: " + e.Message);
}
}
}
private IEnumerator WaitForResponseURL()
{
while (true)
{
yield return new WaitForSeconds(1f); // Adjust the wait time as needed
// Send a request to the local server to check if the response URL has been received.
using (UnityWebRequest webRequest = UnityWebRequest.Get(serverURL))
{
yield return webRequest.SendWebRequest();
if (!webRequest.isNetworkError && !webRequest.isHttpError)
{
// Response received, break the loop.
Debug.Log("Response URL received: " + webRequest.downloadHandler.text);
break;
}
}
}
}
private string ExtractAuthorizationCode(string responseURL)
{
// Extract the code parameter from the response URL
Uri uri = new Uri(responseURL);
string query = uri.Query;
int codeIndex = query.IndexOf("code=");
int stateIndex = query.IndexOf("&state=");
if (codeIndex >= 0)
{
if (stateIndex > codeIndex)
{
// Extract the authorization code without the state parameter.
string authorizationCode = query.Substring(codeIndex + 5, stateIndex - (codeIndex + 5));
Debug.Log(authorizationCode);
return authorizationCode;
}
else
{
// State parameter not found; extract the authorization code until the end.
string authorizationCode = query.Substring(codeIndex + 5);
Debug.Log(authorizationCode);
return authorizationCode;
}
}
return null;
}
private IEnumerator ExchangeAuthorizationCode()
{
WWWForm form = new WWWForm();
form.AddField("code", authorizationCode);
form.AddField("client_id", clientId);
form.AddField("client_secret", clientSecret);
form.AddField("redirect_uri", redirectUri);
form.AddField("grant_type", "authorization_code");
using (UnityWebRequest www = UnityWebRequest.Post(tokenUrl, form))
{
yield return www.SendWebRequest();
if (www.result == UnityWebRequest.Result.Success)
{
string responseJson = www.downloadHandler.text;
string accessToken = ExtractAccessToken(responseJson);
LoginStatus.text = "Got Access Token" + accessToken;
var apiClient = AccelByteSDK.GetClientRegistry().GetApi();
var user = apiClient.GetUser();
Result<TokenData, OAuthError> loginResult = null;
user.LoginWithOtherPlatform(PlatformType.Oidcokta, accessToken, (Result<TokenData, OAuthError> loginResult) =>
{
if (loginResult.IsError)
{
// If it results in error, grab the Error Error and Description to print in the Log.
Debug.Log($"Login failed : {loginResult.Error.error} Description : {loginResult.Error.error_description}");
// Set the Status Text to display the Error, if any.
LoginStatus.text = $"Login failed : {loginResult.Error.error} Description : {loginResult.Error.error_description}";
}
else
{
Debug.Log($"Login successful : {loginResult.Value.ToJsonString()}");
LoginStatus.text = $"Success Login With oidc : {loginResult.Value.ToJsonString()} ";
}
// Enable interaction with the button again.
OidcAuthCodeLoginButton.interactable = true;
});
}
else
{
Debug.Log($"Token request error: {www.error}");
}
}
}
private string ExtractAccessToken(string responseJson)
{
// Parse the JSON response to extract the access token.
AccessTokenResponse accessTokenResponse = JsonUtility.FromJson<AccessTokenResponse>(responseJson);
// Extract the access token from the parsed response
string accessToken = accessTokenResponse.access_token;
return accessToken;
}
[System.Serializable]
public class AccessTokenResponse
{
public string access_token;
public int expires_in;
public string token_type;
}
}
以下のコードは LoginWithOtherPlatform を処理します。これは、GetAuthSessionTicket メソッドからの認証トークンを使用してサードパーティプラットフォームのログインを処理する AccelByte SDK の一部です。
var apiClient = AccelByteSDK.GetClientRegistry().GetApi();
var user = apiClient.GetUser();
Result<TokenData, OAuthError> loginResult = null;
user.LoginWithOtherPlatform(PlatformType.Oidcokta, accessToken, (Result<TokenData, OAuthError> loginResult) =>
{
if (loginResult.IsError)
{
// If it results in error, grab the Error Error and Description to print in the
Debug.Log($"Login failed : {loginResult.Error.error} Description : {loginResult.Error.error_description}");
// Set the Status Text to display the Error if there is any
LoginStatus.text = $"Login failed : {loginResult.Error.error} Description : {loginResult.Error.error_description}";
}
else
{
Debug.Log($"Login successful : {loginResult.Value.ToJsonString()}");
LoginStatus.text = $"Success Login With oidc : {loginResult.Value.ToJsonString()} ";
}
// Enable interaction with the button again.
OidcAuthCodeLoginButton.interactable = true;
});
OIDC 認証における platform_token は、IdP からのアクセストークンです。
Login Handler Script をプロジェクトに追加した後、コンパイルできます。
Sample code testing
プロジェクトをビルドして実行できます。以下のスクリーンショットは、コードが正常に動作し、OIDC とテストアプリを使用してログインできることを示しています。

Extend SDK login
Extend SDK を通じて OIDC を統合し、OIDC 認証情報を使用してサインインできます。
- C# Extend SDK
- Go Extend SDK
- Java Extend SDK
- Python Extend SDK
// OIDC provider credentials
public class ProviderSpecification
{
[JsonPropertyName("name")]
public string Name { get; set; } = String.Empty;
[JsonPropertyName("key")]
public string Key { get; set; } = String.Empty;
[JsonPropertyName("discovery_url")]
public string DiscoveryUrl { get; set; } = String.Empty;
[JsonPropertyName("client_id")]
public string ClientId { get; set; } = String.Empty;
[JsonPropertyName("client_secret")]
public string ClientSecret { get; set; } = String.Empty;
[JsonPropertyName("scope")]
public string Scope { get; set; } = String.Empty;
}
// Discovery data from provider
public class DiscoveryData
{
[JsonPropertyName("issuer")]
public string Issuer { get; set; } = String.Empty;
[JsonPropertyName("jwks_uri")]
public string JWKSUri { get; set; } = String.Empty;
[JsonPropertyName("authorization_endpoint")]
public string AuthEndpoint { get; set; } = String.Empty;
[JsonPropertyName("token_endpoint")]
public string TokenEndpoint { get; set; } = String.Empty;
[JsonPropertyName("registration_endpoint")]
public string RegistrationEndpoint { get; set; } = String.Empty;
[JsonPropertyName("introspection_endpoint")]
public string IntrospectionEndpoint { get; set; } = String.Empty;
[JsonPropertyName("userinfo_endpoint")]
public string UserInfoEndpoint { get; set; } = String.Empty;
[JsonPropertyName("scopes_supported")]
public List<string> SupportedScopes { get; set; } = new List<string>();
[JsonPropertyName("grant_types_supported")]
public List<string> SupportedGrantTypes { get; set; } = new List<string>();
[JsonPropertyName("claims_supported")]
public List<string> SupportedClaims { get; set; } = new List<string>();
public static DiscoveryData Retrieve(HttpClient httpClient, string discoveryUrl)
{
HttpRequestMessage req = new HttpRequestMessage(HttpMethod.Get, discoveryUrl);
HttpResponseMessage response = httpClient.Send(req);
string jsonString = response.Content.ReadAsStream().ReadToString();
return JsonSerializer.Deserialize<DiscoveryData>(jsonString)!;
}
}
// Method to get authorization token from provider
public OAuthTokens GetAuthorizedToken(ProviderSpecification spec, string authorizationToken)
{
DiscoveryData dData = DiscoveryData.Retrieve(DefaultHttpClient.Http, spec.DiscoveryUrl);
HttpRequestMessage req = new HttpRequestMessage(HttpMethod.Post, dData.TokenEndpoint);
req.Content = new FormUrlEncodedContent(new Dictionary<string, string>()
{
{"grant_type", "authorization_code" },
{"client_id", spec.ClientId },
{"client_secret", spec.ClientSecret },
{"redirect_uri", "http://localhost:9090/callback" },
{"code", authorizationToken }
});
HttpResponseMessage response = DefaultHttpClient.Http.Send(req);
string jsonString = response.Content.ReadAsStream().ReadToString();
return JsonSerializer.Deserialize<OAuthTokens>(jsonString)!;
}
using IAccelByteSdk sdk = AccelByteSdk.Builder
.UseDefaultHttpClient()
.UseDefaultConfigRepository()
.UseDefaultTokenRepository()
.UseDefaultCredentialRepository()
.Build();
var abPlatformId = "<ab platform id>"
var authCode = "<authorization code from provider>";
var spec = new ProviderSpecification(); //specify or load provider credentials
OAuthTokens tokens = GetAuthorizedToken(spec, authCode);
try
{
string output = String.Empty;
sdk.LoginPlatform(abPlatformId, tokens.ID, (otr) =>
{
output = JsonSerializer.Serialize(otr);
});
// Do something when login succeeds
}
catch (Exception x)
{
// Do something when login fails
}
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"`
}
// OIDC provider credentials
@Builder
public class ProviderSpecification {
@JsonProperty("name")
public String name;
@JsonProperty("key")
public String key;
@JsonProperty("discovery_url")
public String discoveryUrl;
@JsonProperty("client_id")
public String clientId;
@JsonProperty("client_secret")
public String clientSecret;
@JsonProperty("scope")
public String scope;
}
// Discovery data from provider
@Builder
public class DiscoveryData {
@JsonProperty("issuer")
public String issuer;
@JsonProperty("jwks_uri")
public String jwksUri;
@JsonProperty("authorization_endpoint")
public String authEndpoint;
@JsonProperty("token_endpoint")
public String TokenEndpoint;
@JsonProperty("registration_endpoint")
public String registrationEndpoint;
@JsonProperty("introspection_endpoint")
public String introspectionEndpoint;
@JsonProperty("userinfo_endpoint")
public String userInfoEndpoint;
@JsonProperty("scopes_supported")
public List<String> supportedScopes;
@JsonProperty("grant_types_supported")
public List<String> supportedGrantTypes;
@JsonProperty("claims_supported")
public List<String> SupportedClaims = new ArrayList<>();
}
public static DiscoveryData retrieveDiscoveryData(HttpClient httpClient, String discoveryUrl) throws Exception {
HttpRequest request = HttpRequest.newBuilder().uri(URI.create(discoveryUrl)).build();
HttpResponse<String> httpResponse;
try {
httpResponse = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
} catch (Exception e) {
// Do something when failed
throw new Exception("Encountered an exception when downloading");
}
if (httpResponse.statusCode() >= HttpURLConnection.HTTP_OK && httpResponse.statusCode() >= HttpURLConnection.HTTP_MULT_CHOICE) {
ObjectMapper objectMapper = new ObjectMapper();
DiscoveryData data = objectMapper.readValue(httpResponse.body(), DiscoveryData.class);
return data;
} else {
throw new Exception("Encountered unexpected http status: " + httpResponse.statusCode());
}
}
public static HttpRequest.BodyPublisher encodeContentToFormUrl(Map<Object, Object> data) throws UnsupportedEncodingException {
StringJoiner sj = new StringJoiner("&");
for (Map.Entry<Object, Object> entry : data.entrySet()) {
sj.add(URLEncoder.encode(entry.getKey().toString(), StandardCharsets.UTF_8.toString()) + "=" +
URLEncoder.encode(entry.getValue().toString(), StandardCharsets.UTF_8.toString()));
}
return HttpRequest.BodyPublishers.ofString(sj.toString());
}
// Method to get authorization token from provider
public Map<String, String> getAuthorizedToken(ProviderSpecification spec, String authorizationToken) throws Exception {
HttpClient httpClient = HttpClient.newHttpClient();
DiscoveryData dData = retrieveDiscoveryData(httpClient, spec.discoveryUrl);
Map<Object, Object> data = new HashMap<>();
data.put("grant_type", "authorization_code");
data.put("client_id", spec.clientId);
data.put("client_secret", spec.clientSecret);
data.put("redirect_uri", "http://localhost:9090/callback");
data.put("code", authorizationToken);
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(dData.TokenEndpoint))
.header("Content-Type", "application/x-www-form-urlencoded")
.POST(encodeContentToFormUrl(data))
.build();
HttpResponse<String> httpResponse;
try {
httpResponse = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
} catch (Exception e) {
// Do something when failed
throw new Exception("Encountered error when fetching the authorized token");
}
if (httpResponse.statusCode() >= HttpURLConnection.HTTP_OK && httpResponse.statusCode() >= HttpURLConnection.HTTP_MULT_CHOICE)
{
ObjectMapper objectMapper = new ObjectMapper();
return objectMapper.readValue(httpResponse.body(), new TypeReference<HashMap<String,String>>() {});
} else {
throw new Exception("Encountered unexpected http return status: " + httpResponse.statusCode());
}
}
...
final AccelByteSDK sdk = new AccelByteSDK(httpClient, tokenRepository, configRepository);
var abPlatformId = "<ab platform id>";
var authCode = "<authorization code from provider>";
var spec = new ProviderSpecification(...); //specify or load provider credentials
Map<String, String> authToken = getAuthorizedToken(spec, authCode);
final String idToken = authToken.get("id_token");
if (!sdk.loginPlatform(abPlatformId, idToken))
{
throw new Exception("Login platform failed!");
}
// Do something when login succeeds
import accelbyte_py_sdk.services.auth as auth_service
from accelbyte_py_sdk.core import get_http_client
from accelbyte_py_sdk.core import AccelByteSDK
from accelbyte_py_sdk.core import MyConfigRepository
host = "https://example.com"
response = get_http_client().get(
url="{host}/user/{username}/token/{tokenType}".format(host=host, username="YourUsername", tokenType="authorization")
)
code = response.text
response = get_http_client().post(
url="{host}/auth/token".format(host=host),
data={
"grant_type": "authorization_code",
"client_id": "YourClientId",
"client_secret": "YourClientSecret",
"redirect_uri": "http://localhost/callback",
"code": code,
}
)
token = response.json()
sdk = AccelByteSDK()
sdk.initialize(
options={
"config": MyConfigRepository(
base_url="https://****.accelbyte.io",
client_id="********************************",
client_secret="********************************",
namespace="********",
)
}
)
result, error = auth_service.login_platform(
platform_id="YourAbPlatformId",
platform_token=token["id_token"],
sdk=sdk,
)
if error:
exit(error)