Account Login Integration Guide
The Vega Media Account Login API provides a way for apps to report their authentication status to the system.
Refresh the user's login status upon each launch, which is done by the Account Login API. There are two Account Login states: signed in, and signed out. By keeping the login status up-to-date, apps make sure that users can access appropriate content and features. The interface enables apps to communicate a boolean value indicating whether a user logged in, both at launch and whenever the login state changes. The system can also query this status as needed, enabling a personalized user experience.
Products can leverage this information to feature or filter content from user authenticated services, giving instant access to content.
The Account Login functionality offers two key components for managing authentication status: the updateStatus method, and the handleReadStatus callback function. The updateStatus method reports an app's current login status for real-time updates when the authentication state changes. The handleReadStatus callback function can respond to system queries about the app's current login status. Both methods use the StatusType for account status, with two possible values: SIGNED_IN and SIGNED_OUT.
Vega Media Account Login API reference
For reference documentation about this API, see Account Login API.
Vega Media Account Login common use cases
Apps should update the system with their login status in the following situations:
- When the app initially launches.
- When there's a change in the user's subscription status.
- When requested from other services.
Install and set up Vega Media Account Login
Step 1: Update the app manifest
Use the service component to handle a portion of the Account Login interactions. Familiarize yourself with service components, also called headless components. Get a service running alongside your interactive component to make this integration easier to perform and troubleshoot. See the service component documentation.
First, update your app manifest to use the Vega Media Account Login API. When modifying the manifest entries, replace com.amazondeveloper.media.sample with your app's package ID, as seen in the following example. Developers most often implement the Account Login API as a service, to provide the account login status, especially during read operations. Don't require users to open an interactive interface-based app. Developers usually implement media provider apps as interactive apps to handle the user interface. Your interactive and service apps should exchange Account Login information. You can exchange Account Login information by using shared files or other inter-process communication mechanisms.
The following example shows a sample manifest for both the Content Launcher and Account Login APIs. It defines both interactive and service components.
schema-version = 1
[package]
title = "<Your app title>"
id = "com.amazondeveloper.media.sample"
[components]
[[components.interactive]]
id = "com.amazondeveloper.media.sample.main"
runtime-module = "/com.amazon.kepler.keplerscript.runtime.loader_2@IKeplerScript_2_0"
launch-type = "singleton"
# The category "com.amazon.category.kepler.media" is only necessary for the primary component. You can find "com.amazon.category.kepler.media"
# in the [[extras]] section of the manifest using the "component-id" value.
categories = ["com.amazon.category.main", "com.amazon.category.kepler.media"]
[[components.service]]
id = "com.amazondeveloper.media.sample.interface.provider"
runtime-module = "/com.amazon.kepler.headless.runtime.loader_2@IKeplerScript_2_0"
launch-type = "singleton"
[processes]
[[processes.group]]
component-ids = ["com.amazondeveloper.media.sample.main"]
[[processes.group]]
component-ids = ["com.amazondeveloper.media.sample.interface.provider"]
[offers]
[[offers.interaction]]
id = "com.amazondeveloper.media.sample.main"
[[offers.service]]
id = "com.amazondeveloper.media.sample.interface.provider"
required-privileges = ["com.amazon.multimedia.privilege.session.manage"]
[[message]]
uri = "pkg://com.amazondeveloper.media.sample.main"
# Match the privileges used in [[offers.interaction]]. If you do not have privileges, use `*`.
sender-privileges = ["*"]
receiver-privileges = ["self"]
[[offers.module]]
id = "/com.amazondeveloper.media.sample.module@ISomeUri1"
includes-messages = ["pkg://com.amazondeveloper.media.sample.main"]
[[extras]]
key = "interface.provider"
component-id = "com.amazondeveloper.media.sample.main"
[extras.value.application]
[[extras.value.application.interface]]
interface_name = "com.amazon.kepler.media.IContentLauncherServer"
attribute_options = ["partner-id"]
static-values = { "partner-id" = "<Your partner id>" }
[[extras.value.application.interface]]
interface_name = "com.amazon.kepler.media.IAccountLoginServer"
attribute_options = ["Status"]
# In this manifest example, Amazon defines both the Content Launcher and the Account Login interface.
# Since the service component should handle the Account Login cluster instead of an
# interactive one for responsiveness, "override_attribute_component" redirects
# calls for the "Status" attribute to the service component.
override_attribute_component = { Status = "com.amazondeveloper.media.sample.interface.provider" }
[needs]
[[needs.module]]
# The dot (.) after "media" is intentional in this format. This notation will change in a
# future release.
id = "/com.amazon.kepler.media.@IAccountLogin1"
[[needs.module]]
id = "/com.amazon.kepler.media@IContentLauncher1"
Media apps often require integration with three key APIs:
- Content Launcher Cluster API for finding and launching content
- Vega Media Controls API for controlling launched media
- Account Login API for communicating Account Login status to the system
Developers most often implement the Content Launcher and Vega Media Control APIs as interactive components to handle Vega media queries. This component is specified in the app manifest's extras section using the interface.provider key, as shown in the following TOML snippet and in the previous example.
[[extras]]
key = "interface.provider"
component-id = "<primary interactive component-id>"
The Account Login API functions as a service app, enabling the system to query Account Login status without waking up the entire app. The Account Login API uses a single "Status" attribute, which the service component handles. For proper routing of login status attribute interactions, the app manifest must specify the service component using the 'override_attribute_component' setting. The following TOML is an example of how to use the 'override_attribute_component':
override_attribute_component = { Status = "<service component-id implementing Account Login api>" }
Use this approach to optimize system performance and provide a clear separation between media control and account management functionalities.
Step 2: Include package dependencies
The Vega Media Account Login API is offered to TypeScript developers using the system turbo module amzn/kepler-media-account-login. This turbo module is part of the SDK package. To use the API, update the package.json file to take the turbo module as a dependency, and add amzn/headless-task-manager to create a headless service app.
"dependencies": {
"@amazon-devices/kepler-media-account-login": "^1.1.0",
"@amazon-devices/headless-task-manager": "^1.1.0"
}
Step 3: Add the Vega Media Account Login business logic
Create a file named AccountLoginWrapper.ts to contain the core business logic for the Vega Media Account Login API. In this file, create and export a class named AccountLoginWrapper. This class has a single member variable: an instance of the IAccountLoginServerAsync interface, used to interact with the Account Login server. This file also globally creates an instance of AccountLoginServerComponent and store it in a variable named accountLoginServerComponent.
import { IComponentInstance } from '@amazon-devices/react-native-kepler';
import {
AccountLoginServerComponent,
IAccountLoginHandlerAsync,
StatusType,
IAccountLoginServerAsync,
IStatus,
} from '@amazon-devices/kepler-media-account-login';
import AsyncStorage from '@react-native-async-storage/async-storage';
const accountLoginServerComponent = new AccountLoginServerComponent();
export class AccountLoginWrapper {
accountLoginServer?: IAccountLoginServerAsync;
}
Step 4: Asynchronously return the Account Login status
Now using the IAccountLoginHandlerAsync interface, implement the handleReadStatus() method that takes no arguments and returns a Promise<IStatus>. By designing the method in this way, the service app asynchronously returns the Account Login status without blocking the caller. The Promise<IStatus> return type indicates that the method eventually resolves with an IStatus object, which likely contains information about the current login state.
The headless service created later in Step 8 invokes the handleReadStatus() handler. Since the interactive component (UI app) and the headless service run in separate processes, they do not share in-memory state. Relying on in-memory variables is insufficient. For reliable communication between the two components, use a shared mechanism for persisting and transferring the login status. Read the login status asynchronously from this persistent storage. Currently the headless service supports React's AsyncStorage and the Vega File System. MMKV is not supported. This example uses AsyncStorage.
Here are some examples of component implementations that leverages AsyncStorage.
// Interactive Component
// Inside "com.amazondeveloper.media.sample.main".
const onLoginChange = async (newStatus: boolean) => {
await AsyncStorage.setItem('loginStatus', newStatus);
};
// Service Component
// Inside "com.amazondeveloper.media.sample.interface.provider".
const getLoginStatus = async (): boolean => {
return await AsyncStorage.getItem('loginStatus');
};
Register the handleReadStatus() handler to return the login status when the system invokes it. getLoginStatus() must read the status through persistent storage as seen previously.
getLoginStatus() is app specific.
export class AccountLoginWrapper {
async getAccountLoginStatus(): IStatus {
return accountLoginServerComponent.makeStatusBuilder()
.status(
// Fetch status from persistent storage.
(await getLoginStatus())
? StatusType.SIGNED_IN
: StatusType.SIGNED_OUT)
.build();
}
createAccountLoginHandler(): IAccountLoginHandlerAsync {
return {
handleReadStatus: async (): Promise<IStatus> => {
console.log('handleReadStatus() invoked.')
return await this.getAccountLoginStatus();
}
};
}
}
Step 5: Initialize and link the Account Login server to the component handler
Next, introduce the setupAccountLoginServer() method in the AccountLoginWrapper class. This method accepts an IComponentInstance object as a parameter.
Implement it in the following manner:
- Create a singleton instance of
IAccountLoginServerAsyncand store it in the member variableaccountLoginServer. - Next, invoke
setHandlerForComponent(), passing in thehandleReadStatus()handler created in Step 4, andComponentInstancealready provided.
export class AccountLoginWrapper {
setupAccountLoginServer(componentInstance: IComponentInstance) {
console.log('setupAccountLoginServer() invoked.');
try {
this.accountLoginServer = accountLoginServerComponent.getOrMakeServer();
} catch (error) {
this.accountLoginServer = undefined;
console.error(
'setupAccountLoginServer() failed creating Account Login server: ',
error,
);
return;
}
try {
this.accountLoginServer?.setHandlerForComponent(this.createAccountLoginHandler(), componentInstance);
} catch (error) {
console.error(
'setupAccountLoginServer() failed to set handler: ',
error,
);
}
console.log('setupAccountLoginServer() completed.');
}
}
Step 6: Update the Account Login status
To update the Account Login status, use the updateStatus() method asynchronously.
Be sure to store the login status in persistent storage before calling this function.
export class AccountLoginWrapper {
async updateStatus(loginStatus: boolean) {
console.log('updateStatus() invoked.');
const status = accountLoginServerComponent.makeStatusBuilder()
.status(loginStatus ? StatusType.SIGNED_IN : StatusType.SIGNED_OUT)
.build();
try {
this.accountLoginServer?.updateStatus(status);
} catch (error) {
console.error(
'updateStatus() Failed updating login status: ',
error,
);
}
}
}
Step 7: Integrate the Account Login server with the service lifecycle
To make this integration, the AccountLoginWrapper class exposes two methods: onStart() and onStop().
onStart()
The onStart() method initializes the Account Login server by calling setupAccountLoginServer() with the provided IComponentInstance.
onStop()
The onStop() method adds the cleanup logic needed when the service stops.
After implementing these methods, create a singleton instance called AccountLoginWrapperInstance and use it to expose two equivalent top-level methods: onStartService() and onStopService(). Use these methods for both the service and interactive components to start and stop the Account Login server.
export class AccountLoginWrapper {
onStart(componentInstance: IComponentInstance): Promise<void> {
this.setupAccountLoginServer(componentInstance);
return Promise.resolve();
}
onStop(): Promise<void> {
// Add any cleanup code here.
return Promise.resolve();
}
}
export const AccountLoginWrapperInstance = new AccountLoginWrapper();
export const onStartService = (componentInstance: IComponentInstance): Promise<void> => {
return AccountLoginWrapperInstance.onStart(componentInstance);
};
export const onStopService = (): Promise<void> => {
return AccountLoginWrapperInstance.onStop();
};
Step 8: Implement a headless service
A headless service is a background process that runs without a user interface, utilizing its own React Native runtime. This service is key for responding to login status requests and is created by the following steps.
- Create a new file named service.js in your app's root directory.
- Import
AccountLoginWrapper, created in the previous steps, and also importheadless-task-manager.import { HeadlessEntryPointRegistry } from '@amazon-devices/headless-task-manager'; import { onStartService, onStopService } from './src/AccountLoginWrapper'; - Define the start and stop entry points.
HeadlessEntryPointRegistry.registerHeadlessEntryPoint2( 'com.amazondeveloper.media.sample.interface.provider::onStartService', () => onStartService ); HeadlessEntryPointRegistry.registerHeadlessEntryPoint2( 'com.amazondeveloper.media.sample.interface.provider::onStopService', () => onStopService );- Replace
com.amazondeveloper.media.samplewith your app's actual package ID. - The entry point naming convention is:
<app_package_id>.interface.provider::onStartServiceor::onStopService.
- Replace
Fire TV system components automatically invoke onStartService() to initialize the Account Login server as a headless service. During this process, the handleReadStatus()
function is registered for the specific service component instance.
When the system invokes this handler later, handleReadStatus() returns the current login state (signed-in or signed-out) as reported by the app.
Step 9: Initialize the Account Login server to report the login state
The interactive component (UI app) must also initialize the Account Login server to report the user's login state using the updateStatus() method.
// Inside "com.amazondeveloper.media.sample.main".
useEffect(() => {
AccountLoginWrapperInstance.onStart(componentInstance);
return () => {
AccountLoginWrapperInstance.onStop();
};
}, []);
Step 10: Send login status updates
The interactive component (UI app) must call the updateStatus() method with the current login status in the following scenarios:
- When the user launches the app.
- When the user's login status changes.
Store the status in persistent storage each time before calling updateStatus(), so that handleReadStatus() retrieves and returns the correct value. This way, the system consistently reflects the user's authentication state, providing a seamless user experience.
This setup lets your app to manage login status requests, even when the main app is not actively running.
You have now created a single instance of IAccountLoginServerComponentAsync. The system creates a link between the handleReadStatus() method and the IComponentInstance, so that any read requests are directed to the service component. Doing this handles Account Login status queries. Call the updateStatus() method to force a login status update, or whenever the status changes.
Troubleshooting Vega Media Account Login
There are several possible issues that can arise when integrating with Account Login, but here are some more common ones.
Confirm The service component starts
See if your updateStatus() call is picked up. If it is, the service component will start without crashing.
You can check on your updateStatus() call by capturing logs before running vega device launch-app com.your.app.id.interface.provider, and watch your service component get through its start-up sequence. Check the logs corresponding to your service component name, and see if any crashes occur during this phase. Find the logs after you reach the registerHeadlessEntryPoint() (onStartService) entrypoint.
If you have a failure or crash according to the logs, you may find the Vega Crash Debugging Documentation a useful reference.
Confirm the handler is called
Once you are confident your service is starting successfully, confirm that your service properly registers a handle using setHandlerForComponent(). You can add logs around this invocation to confirm this path is getting hit. You can find equivalent logs from for handle_set_response_handler for your service component ID to confirm.
Add logs around handleReadStatus() to confirm it is functioning as expected.
Related topics
Last updated: Feb 17, 2026

