Account Login Integration Guide
The Vega Media Account Login API provides a way for apps to report their authentication status to the system.
Apps need to 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 have access to appropriate content and features. The interface allows apps to communicate a boolean value indicating whether a user is 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 services where the user is already authenticated, allowing for 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 Content Launcher API and Account Login API Overview.
Vega Media Account Login common use cases
Apps should update the system with their login status in the following situations:
- During the app's initial launch
- When there's a change in the user's subscription status
- Upon request from other services
Install and setup 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, which are 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. The Account Login API is typically implemented as a service to provide account login status, especially during read operations. Don't require users to open an interactive interface-based app. Media provider apps are most often implemented as interactive apps to handle the user interface. Your interactive and service apps should exchange Account Login information. You can use shared files or other inter-process communication mechanisms to do this.
The example below 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, which is identified 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 privileges are not added, then 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, we have both Content Launcher and Account Login interfaces defined.
# Since the Account Login cluster should be handled by a service component instead of an
# interactive one for responsiveness, we use "override_attribute_component" to redirect
# 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 be changed 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
The Content Launcher and Vega Media Control APIs are most often implemented as an interactive component, which handles 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 is implemented as a service app, allowing the system to query Account Login status without waking up the entire app. The Account Login API uses a single "Status" attribute, which is handled by the service component. To ensure proper routing of login status attribute interactions, the app manifest must specify the service component using the "override_attribute_component" setting, as demonstrated in this TOML example and in the example above:
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 will also globally create 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, we ensure that the service app can asynchronously return 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. To ensure reliable communication between the two components, use a shared mechanism for persisting and transferring the login status. Login status must be read asynchronously from this persistent storage. Currently the headless service supports React's AsyncStorage
and the Vega File System. MMKV is not supported. In this example, we will use 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 it's invoked by the system. 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
IAccountLoginServerAsync
and store it in the member variableaccountLoginServer
. - Next, invoke
setHandlerForComponent()
, passing in thehandleReadStatus()
handler created in Step 4, andComponentInstance
already 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, 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 can be 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.sample
with your app's actual package ID. - The entry point naming convention is:
<app_package_id>.interface.provider::onStartService
or::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 app is launched.
- When the user's login status changes
Each time, the status must be stored in persistent storage 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 allows 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
. We have established a link between the handleReadStatus()
method and the IComponentInstance
, so that any read requests are directed to the service component. Doing this will handle Account Login status queries. It also calls the updateStatus()
method, which serves a dual purpose. It should be called either 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()
calls being picked up. This confirms the service component can be started without crashing.
To do this, begin capturing logs before running kepler device launch-app com.your.app.id.interface.provider
and you should be able to watch your service component get through its start-up sequence. You should see logs corresponding to your service component name, and see if any crashes occur during this phase. The logs are located after registerHeadlessEntryPoint()
(onStartService
) entrypoint is reached.
If you don't see this, you may see logs indicating a failure or crash. In that event, Vega Crash Debugging Documentation is 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 will see equivalent logs from for handle_set_response_handler
for your service component ID to confirm.
You should also add logs around handleReadStatus()
to confirm it is functioning as expected.
Related topics
Last updated: Sep 30, 2025