Unique identifier for your application. Copy from Hub and use in SDK initialization.
Publishable Key
Provided by Hub
Public key safe for client-side code. Copy from Hub project settings.
Application Type
You configure
Select Web for TypeScript/web apps, Native for Unity/Unreal games.
Application Name
You configure
Identifier for your project inside Passport (e.g., “My Game”).
Redirect URIs
You configure
Where users land after successful authentication. Must exactly match redirectUri in your code. Examples: http://localhost:3000/redirect (web), mygame://callback (native).
Logout URIs
You configure
Where users land after logout. Must exactly match logoutRedirectUri in your code. Examples: http://localhost:3000/logout (web), mygame://logout (native).
Passport uses OpenID Connect (OIDC). Redirect URIs must be exact matches—wildcards aren’t supported for security reasons. Register multiple URIs for different environments (localhost, staging, production).
The useLogin hook provides embedded, popup, and redirect login flows. All functions accept an optional config; when omitted, sandbox defaults are used.
For a more streamlined user experience, the standard Passport login prompt can be bypassed by providing the directLoginOptions parameter to the login method. This allows you to render a customised authentication prompt within your own application.Once an authentication option (email, Google, Apple, or Facebook) is passed to the login method, a popup will be opened so that the user can authenticate securely.
// Headless login with emailconst user = await auth.login({ directLoginOptions: { directLoginMethod: 'email', email: 'user@example.com', marketingConsentStatus: 'opted_in' }});// Headless login with specific providerconst user = await auth.login({ directLoginOptions: { directLoginMethod: 'google', // or 'apple', 'facebook' marketingConsentStatus: 'opted_in' }});
Option
Description
directLoginMethod
The authentication provider (email, google, apple, or facebook)
email
Required when directLoginMethod is email, specifies the user’s email address
marketingConsentStatus
Marketing consent setting (opted_in or unsubscribed)
This implementation uses EthersJS’s BrowserProvider to interact with the Passport provider. It requests user accounts and manages the connection state, displaying the connected account address when successful.
The PassportUI prefab provides a seamless, embedded authentication experience that keeps users within your application. This approach offers better user experience and higher conversion rates.
Volt Unity Web Browser (UWB) - no additional packages required
iOS/Android/macOS
Vuplex WebView (paid third-party product)
1. Add the PassportUI Prefab to Your SceneChoose the appropriate prefab for your target platform:
PassportLogin_Windows.prefab - For Windows builds using UWB
PassportLogin_Vuplex.prefab - For iOS, Android, and macOS builds using Vuplex
2. Initialize PassportUI
public class MyAuthScript : MonoBehaviour{ public PassportUI passportUI; async void Start() { // Initialize with existing Passport instance await passportUI.InitializeWithPassport(Passport.Instance); // Or initialize PassportUI with automatic Passport creation // (uses the clientId/environment/URIs set in the Inspector) await passportUI.InitializeWithPassport(); }}
3. Handle Authentication Events
void Start(){ // Subscribe to authentication events Passport.Instance.OnAuthEvent += OnAuthEvent;}private void OnAuthEvent(PassportAuthEvent authEvent){ switch (authEvent) { case PassportAuthEvent.LoginPKCESuccess: Debug.Log("User logged in successfully"); break; case PassportAuthEvent.LogoutPKCESuccess: Debug.Log("User logged out"); break; case PassportAuthEvent.ReloginSuccess: Debug.Log("User re-logged in with cached credentials"); break; }}
For iOS, Android, and macOS, the embedded WebView requires Vuplex WebView, a paid third-party package. Alternatively, use Standard Login which doesn’t require any WebView.
For a more streamlined user experience, bypass the standard Passport login prompt by providing the DirectLoginOptions parameter:
// Customized login with emailawait passport.Login(directLoginOptions: new DirectLoginOptions( DirectLoginMethod.Email, "user@example.com", MarketingConsentStatus.OptedIn));// Customized login with specific providerawait passport.Login(directLoginOptions: new DirectLoginOptions( DirectLoginMethod.Google // or Apple, Facebook));
Option
Description
DirectLoginMethod
The authentication provider (Email, Google, Apple, or Facebook)
email
Required when DirectLoginMethod is Email, specifies the user’s email address
marketingConsentStatus
Marketing consent setting (OptedIn or Unsubscribed)
Use the basic Login() method to show the standard Passport login prompt:
await passport.Login();
This will open an external browser window on desktop or an in-app browser on mobile devices, prompting the user to select an authentication option and complete the authentication process securely.
Once the gamer is connected to Passport, the SDK will store credentials (access, ID, and refresh tokens). Use Login(useCachedSession: true) to re-login using saved credentials.
For a more streamlined user experience, bypass the standard Passport login prompt by providing the DirectLoginOptions parameter. Once an authentication option (email, Google, Apple, or Facebook) is passed, a popup will be opened so that the user can authenticate securely.
// Add to your subsystem:public: void PerformHeadlessLogin() { auto Passport = GetPassport(); if (!Passport) { return; } // Customised login with email Passport->Connect ( true, UImmutablePassport::FImtblPassportResponseDelegate::CreateUObject(this, &UPassportQuickStartSubsystem::OnLoginComplete), {.DirectLoginMethod = EImmutableDirectLoginMethod::Email} ); }private: void OnLoginComplete(FImmutablePassportResult Result) { if (!Result.Success) { UE_LOG(LogTemp, Error, TEXT("Login Failed: %s"), *Result.Error); return; } UE_LOG(LogTemp, Log, TEXT("Login Successful")); }void OnPassportInitialized(FImmutablePassportResult Result){ if (!Result.Success) return; UE_LOG(LogTemp, Log, TEXT("Passport Initialized Successfully")); PerformHeadlessLogin();}
Option
Description
DirectLoginMethod
The authentication provider (Email, Google, Apple, or Facebook)
Email
Required when DirectLoginMethod is Email, specifies the user’s email address
MarketingConsentStatus
Marketing consent setting (Opted_In or Unsubscribed)
This will open an external browser window on desktop or an in-app browser on mobile devices, prompting the user to select an authentication option and complete the authentication process securely.
Once the gamer is connected to Passport, the SDK will store credentials (access, ID, and refresh tokens).You can use the HasStoredCredentials async action to check if the gamer has stored credentials before deciding whether to show login options in your UI.
The HasStoredCredentials async action is currently Blueprint-compatible only. For C++, use the Passport instance methods directly.
The CallbackPage component handles both redirect and popup flows automatically. For popup logins, it communicates tokens back to the parent window and closes itself.
// Get user info from the ID tokenconst userInfo = await auth.getUserInfo();console.log({ userId: userInfo.sub, // Unique Passport user ID email: userInfo.email, // If email scope granted emailVerified: userInfo.email_verified,});
var userInfo = await passport.GetUserInfo();Debug.Log($"User ID: {userInfo.Sub}");Debug.Log($"Email: {userInfo.Email}");
// Add to your subsystem:private: void OnLoginComplete(FImmutablePassportResult Result) { if (!Result.Success) { UE_LOG(LogTemp, Error, TEXT("Login Failed: %s"), *Result.Error); return; } UE_LOG(LogTemp, Log, TEXT("Login Successful")); auto Passport = GetPassport(); // Get user email Passport->GetEmail(UImmutablePassport::FImtblPassportResponseDelegate::CreateWeakLambda(this, [this](FImmutablePassportResult EmailResult) { if (EmailResult.Success) { FString Email = UImmutablePassport::GetResponseResultAsString(EmailResult.Response); UE_LOG(LogTemp, Log, TEXT("User Email: %s"), *Email); } })); // Get user wallet address Passport->GetAddress(UImmutablePassport::FImtblPassportResponseDelegate::CreateWeakLambda(this, [this](FImmutablePassportResult AddressResult) { if (AddressResult.Success) { FString Address = UImmutablePassport::GetResponseResultAsString(AddressResult.Response); UE_LOG(LogTemp, Log, TEXT("Wallet Address: %s"), *Address); } })); }
Note: The Unreal SDK provides separate methods (GetEmail, GetAddress) rather than a unified GetUserInfo() method. See Get ID Token and Get Access Token sections for retrieving tokens.
Always use isAuthenticated from useImmutableSession to determine if a user is logged in.
'use client';import { useImmutableSession } from '@imtbl/auth-next-client';function ProtectedContent() { const { isAuthenticated, isLoading } = useImmutableSession(); if (isLoading) return <div>Loading...</div>; if (!isAuthenticated) return <div>Please log in</div>; return <div>Protected content</div>;}
Do not use !!session or status === 'authenticated' to check auth state. A session object can exist with expired or invalid tokens, and status does not account for token-level errors like RefreshTokenError.
isAuthenticated validates all of the following:
NextAuth reports 'authenticated' status
The session object exists
A valid access token is present in the session
No session-level error exists (such as RefreshTokenError)
It also handles transient states gracefully — during session refetches (window focus) or manual refreshes (after wallet registration via getUser(true)), isAuthenticated remains true if the user was previously authenticated, preventing UI flicker.
// Correctconst { isAuthenticated } = useImmutableSession();if (!isAuthenticated) return <div>Please log in</div>;// Incorrect -- session can exist with expired/invalid tokensconst { session } = useImmutableSession();if (!session) return <div>Please log in</div>;// Incorrect -- status doesn't account for token errorsconst { status } = useImmutableSession();if (status !== "authenticated") return <div>Please log in</div>;
async function isLoggedIn(): Promise<boolean> { return auth.isLoggedIn();}
getAccessToken() returns a guaranteed-fresh access token. If the current token is valid it returns immediately; if expired, it triggers a server-side refresh and blocks until the fresh token is available. Multiple concurrent calls share a single refresh request.
The ID token is not stored in the session cookie (to stay within CDN header size limits). Use getUser() to access it — the client persists it in localStorage automatically.
'use client';import { useImmutableSession } from '@imtbl/auth-next-client';function GetIdToken() { const { getUser } = useImmutableSession(); async function handleGetToken() { const user = await getUser(); console.log('ID Token:', user?.idToken); } return <button onClick={handleGetToken}>Get ID Token</button>;}
The useLogout hook performs federated logout — it clears both the local NextAuth session and the upstream Immutable/Auth0 session by redirecting to the logout endpoint. This is important when using social logins like Google: without federated logout, the auth server caches the social session, so users clicking “Login” again would be automatically logged in with the same account instead of being prompted to choose.