Authorize from an AVS Product (Android/iOS)
To access the Alexa Voice Service (AVS), your Alexa Built-in Android or iOS app needs to obtain a Login with Amazon (LWA) access token to send with each request to the Alexa Voice Service. In this scenario, you don't have a headless device, such as a smart speaker, that requires you to authorize and associate the device with a user account.
This document explains how to obtain an access token with the LWA SDK.
Using the Login with Amazon SDK
Use the following tabs to select your operating system:
Use the following instructions to add LWA to your Android app:
- Register a mobile app in the Alexa developer portal and generate an API key for your app. For instructions on how to create an account and register an app see Getting Started with AVS.
- Navigate to the Login with Amazon Getting Started for Android and complete steps 1, 4, and 5.
- Then complete the following steps to obtain an LWA access token to use with AVS:
-
Add LWA to your Android project. For installation instructions, see Create a Login with Amazon Project: Install the Login with Amazon Library.
-
Import the LWA API to your source file. To import the LWA API, add the following import statements to your source file:
import com.amazon.identity.auth.device.AuthError; import com.amazon.identity.auth.device.api.Listener; import com.amazon.identity.auth.device.api.authorization.AuthCancellation; import com.amazon.identity.auth.device.api.authorization.AuthorizationManager; import com.amazon.identity.auth.device.api.authorization.AuthorizeListener; import com.amazon.identity.auth.device.api.authorization.AuthorizeRequest; import com.amazon.identity.auth.device.api.authorization.AuthorizeResult; import com.amazon.identity.auth.device.api.authorization.Scope; import com.amazon.identity.auth.device.api.authorization.ScopeFactory; import com.amazon.identity.auth.device.api.workflow.RequestContext;
-
Initialize
RequestContext
.Declare a
RequestContext
member variable and create a new instance of the class. To create the instance, pass in the current application context to the static factory method. The best place to initialize theRequestContext
is in theonCreate
method of your Activity. For example:private RequestContext mRequestContext; @Override protected void onCreate(Bundle savedInstance) { super.onCreate(savedInstance); mRequestContext = RequestContext.create(this); }
-
Create an
AuthorizeListenerImpl
.AuthorizeListenerImpl
extends theAuthorizeListener
abstract class and processes the result of the authorize call. It contains three methods:onSuccess
,onError
, andonCancel
. TheAuthorizeResult
object passed into theonSuccess
method contains the access token.private class AuthorizeListenerImpl extends AuthorizeListener { /* Authorization was completed successfully. */ @Override public void onSuccess(final AuthorizeResult authorizeResult) { } /* There was an error during the attempt to authorize the application. */ @Override public void onError(final AuthError authError) { } /* Authorization was cancelled before it could be completed. */ @Override public void onCancel(final AuthCancellation authCancellation) { } }
-
Create and register an instance of
AuthorizeListenerImpl
.Create an instance of your
AuthorizeListenerImpl
and register it with yourRequestContext
instance. When making theAuthorizeManager.authorize
call, yourRequestContext
instance will invoke the appropriate callback method in yourAuthorizeListener
with the result of the authorize request. The best place to register yourAuthorizeListenerImpl
instance is in theonCreate
method.@Override protected void onCreate(Bundle savedInstance) { super.onCreate(savedInstance); mRequestContext = RequestContext.create(this); mRequestContext.registerListener(new AuthorizeListenerImpl()); }
-
Override the
onResume
method.Override the
onResume
method in your Activity. Callsuper.onResume()
as well as theonResume
method on yourRequestContext
instance. This notifies theRequestContext
to invoke yourAuthorizeListener
when your app resumes if there is a callback ready from yourAuthorizeManager.authorize
call.@Override protected void onResume() { super.onResume(); mRequestContext.onResume(); }
-
Call
AuthorizeManager.authorize
.In the
onClick
handler for your Login with Amazon button, call authorize to prompt the user to log in and authorize your app. This method authorizes the customer in one of the following ways:- Switches to the system browser and lets the customer sign in and consent to the requested information.
-
Switches to web view in a secure context to let the customer sign in and consent to the requested information.
The secure context is available through the Amazon Shopping app on Android devices. Amazon devices running Fire OS, such as Kindle tablets and Fire TV devices, always use this option even if the device doesn't have the Amazon Shopping app installed. Because of this, if the customer is already signed in to the Amazon Shopping app, this API skips the sign-in page, leading to a Single Sign On (SSO) experience for the customer.
Your app is then authorized for one or more data sets called "scopes." Scopes are an OAuth mechanism for a client to tell an authorization server what resource permissions they need. You pass an array of scopes as a parameter to the authorize method.
The first time a user logs in to your app, they receive a prompt that contains the list of data you require access to, and request consent before proceeding. LWA when used with AVS requires the
alexa:all
scope. Thealexa:voice_service:pre_auth
scope enables the AVS Hosted Splash for the user.Important: To use AVS hosted splash, you must include thealexa:voice_service:pre_auth
scope, and remove your existing splash screen. All new products are required to utilize the AVS hosted splash scope.The call to authorize is asynchronous, and the result of your call invokes an
AuthorizeListenerImpl
instance.private RequestContext mRequestContext; private static final String PRODUCT_ID = "INSERT YOUR PRODUCT ID FROM AMAZON DEVELOPER CONSOLE"; private static final String PRODUCT_DSN = "INSERT UNIQUE DSN FOR YOUR DEVICE"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mRequestContext = RequestContext.create(this); mRequestContext.registerListener(new AuthorizeListenerImpl()); // Find the button with the login_with_amazon ID // and set up a click handler mLoginButton = (Button) findViewById(R.id.login_with_amazon); mLoginButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { final JSONObject scopeData = new JSONObject(); final JSONObject productInstanceAttributes = new JSONObject(); try { productInstanceAttributes.put("deviceSerialNumber", PRODUCT_DSN); scopeData.put("productInstanceAttributes", productInstanceAttributes); scopeData.put("productID", PRODUCT_ID); AuthorizationManager.authorize(new AuthorizeRequest.Builder(mRequestContext) .addScopes(ScopeFactory.scopeNamed("alexa:voice_service:pre_auth"), ScopeFactory.scopeNamed("alexa:all", scopeData)) .forGrantType(AuthorizeRequest.GrantType.ACCESS_TOKEN) .shouldReturnUserData(false) .build()); } catch (JSONException e) { // handle exception here } } }); }
-
Create a
TokenListener
.Implement the
Listener<AuthorizeResult, AuthError>
interface. Your listener processes the result of thegetToken
call. This Listener contains two methods,onSuccess
andonError
(it doesn't supportonCancel
because there is no way to cancel agetToken
call).onSuccess
receives anAuthorizeResult
object with token data, andonError
receives anAuthError
object with information about the error.public class TokenListener implements Listener<AuthorizeResult, AuthError> { /* getToken completed successfully. */ @Override public void onSuccess(AuthorizeResult authorizeResult) { } /* There was an error during the attempt to get the token. */ @Override public void onError(AuthError authError) { } }
-
Implement
onSuccess
for yourAuthorizeListener
.In
onSuccess
, callAmazonAuthorizationManager.getToken
to retrieve the access token.getToken
, like authorize, uses an asynchronous listener interface. ForgetToken
, that interface isListener<AuthorizeResult, AuthError>
, notAuthorizationListener
.@Override public void onSuccess(Bundle response) { AuthorizationManager.getToken(this, new Scope[] { ScopeFactory.scopeNamed("alexa:all") }, new TokenListener()); }
-
Implement
onSuccess
for yourTokenListener
.onSuccess
has two tasks:- To retrieve the access token from the Bundle.
- To set the UI state to logged in, which indicates that a user is logged in and provides a way to log out.
/* getToken completed successfully. */ @Override public void onSuccess(AuthorizeResult authorizeResult) { String accessToken = authorizeResult.getAccessToken(); }
-
In the
onStart
method of your Activity, callgetToken
to see if the app is still authorized.getToken
retrieves the raw access token to use when making requests to AVS.getToken
requires the same scopes you requested in your call to authorize.
private static final Scope ALEXA_ALL_SCOPE = ScopeFactory.scopeNamed("alexa:all"); @Override protected void onStart(){ super.onStart(); AuthorizationManager.getToken(this, new Scope[] { ALEXA_ALL_SCOPE }, new TokenListener()); }
-
Now you have an access token to use for requests to AVS.
Use the following instructions to integrate LWA with your iOS app.
- Register a mobile app in the Alexa developer portal and generate an API key for your app. For instructions on how to create an account and register an app see Getting Started with AVS.
- Navigate to Login with Amazon Getting Started for iOS and complete steps 1, 4, and 5.
- Then complete the following steps to obtain an LWA access token to use with AVS:
-
Add LWA to your iOS project. For instructions, see Create a Login with Amazon Project: Install the Login with Amazon Library.
-
Import the LWA API to your source file.
To import the LWA API, add the following
#import
statements to your source file:#import <LoginWithAmazon/LoginWithAmazon.h>
-
Call
authorize:withHandler:
inonLoginButtonClicked
.If you followed the steps in Add a Login with Amazon Button to Your App, you should have an
onLoginButtonClicked:
method linked to a Login with Amazon button. In that method, callauthorize:withHandler:
to prompt the user to login and authorize your app.This method enables the user to sign in and consent to the requested information in one of the following ways:
- Switches to web view in a secure context (if the device has the Amazon Shopping app installed).
- Switches to Safari View Controller (on iOS 9 and later).
- Switches to the system browser (on iOS 8 and earlier).
The secure context for the first option is available when the Amazon Shopping app is installed to the device. If the user is already signed in to the Amazon Shopping app, this API skips the sign in page, leading to a Single Sign-On (SSO) experience.
The first parameter to
authorize:withHandler:
is anAMZNAuthorizeRequest
object that indicates the authorization scope for your app. A scope encompasses the user data you are requesting from Login with Amazon. The first time a user logs in to your app, the app displays a list of the data you are requesting and asking for approval.The second parameter to
authorize:withHandler:
isAMZNAuthorizationRequestHandler
, described in the next step. -
Create an
AMZNAuthorizationRequestHandler
block object.AMZNAuthorizationRequestHandler
processes the result of theauthorize:withHandler:
call. To learn more about objective-c blocks, see Working with Blocks on developer.apple.com.Your app is then authorized for one or more data sets known as "scopes." Scopes are an OAuth mechanism for a client to tell an authorization server what resource permissions they need. You pass an array of scopes as a parameter to the authorize method.
The first time a user logs in to your app, they receive a prompt that contains the list of data you require access to, and request consent before proceeding. LWA when used with AVS requires the
alexa:all
scope. Thealexa:voice_service:pre_auth
scope enables the AVS Hosted Splash for the user.Important: To use AVS hosted splash, you must include thealexa:voice_service:pre_auth
scope, and remove your existing splash screen. All new products are required to utilize the AVS hosted splash scope.The first parameter of
AMZNAuthorizationRequestHandler
is anAMZNAuthorizeResult
object. After authorizing the user,AMZNAuthorizeResult
contains an access token to access user profile data and anAMZNUser
object, which contains the user profile data.The second parameter of
AMZNAuthorizationRequestHandler
is a Boolean calleduserDidCancel
. This parameter becomes true if the user performs any of the following actions:- Closes the Safari View Controller during login and authorization (on iOS 9 and later).
- Closes the web view in the Amazon Shopping app.
- Cancels the login or reject authorization.
The third parameter of
AMZNAuthorizationRequestHandler
is anNSError
object which contains error details if the login or authorization fails due to the SDK or the authorization server.```java NSString *productId = @"INSERT YOUR PRODUCT ID FROM AMAZON DEVELOPER CONSOLE"; NSString *productDsn = @"INSERT UNIQUE DSN FOR YOUR DEVICE";
-
(IBAction) onLogInButtonClicked:(id)sender { NSDictionary *scopeData = @{@"productID": productId, @"productInstanceAttributes": @{@"deviceSerialNumber": productDsn}};
id
alexaAllScope = [AMZNScopeFactory scopeWithName:@"alexa:all" data:scopeData]; id
alexaSplashScope = [AMZNScopeFactory scopeWithName:@"alexa:voice_service:pre_auth"]; AMZNAuthorizeRequest *request = [[AMZNAuthorizeRequest alloc] init]; request.scopes = @[alexaSplashScope, alexaAllScope]; request.grantType = AMZNAuthorizationGrantTypeToken;
//Make an Authorize call to the LWA SDK. AMZNAuthorizationManager *authManager = [AMZNAuthorizationManager sharedManager]; [authManager authorize:request withHandler:^(AMZNAuthorizeResult *result, BOOL userDidCancel, NSError *error) { if (error) { // Notify the user that authorization failed [[[UIAlertView alloc] initWithTitle:@"" message:[NSString stringWithFormat:@"User authorization failed due to an error: %@", error.localizedDescription] delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil] show]; } else if (userDidCancel) { // Notify the user that the authorization was cancelled [[[UIAlertView alloc] initWithTitle:@"" message:@"Authorization was cancelled prior to completion. To continue, you will need to try logging in again." delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil] show]; } else { // Fetch the access token and return to controller self.token = result.token; [self userSuccessfullySignedIn];
} }]; } ``
-
Check for user login at startup.
If a user logs into your app, closes the app, and restarts the app later, the app is still authorized to retrieve data. The user isn't logged out automatically. At startup, you can show the user as logged in if your app is still authorized. This section explains how to use
authorize:withHandler:
to see if the app is still authorized.- Create an
AMZNAuthorizeRequest
object and specify scopes that indicate the user data your application is requesting authorization for. For more information, see step 3. -
Set
AMZNAuthorizeRequest.interactiveStrategy
toAMZNInteractiveStrategyNever
.AMZNAuthorizeRequest
supports multiple strategies for prompting user login:AMZNInteractiveStrategyAuto
(default): The SDK looks for a locally stored authorization grant from previousauthorize:withHandler:
responses. If one is available, valid, and contains all requested scopes, the SDK will return a successful response viaAMZNAuthorizationRequestHandler
, and doesn't prompt the user to login. Otherwise, the user will be prompted to login.AMZNInteractiveStrategyAlways
: The SDK always prompts the user to login regardless of whether they were already authorized to use the app. When the user is prompted, the SDK removes all locally cached authorization grants for the app.AMZNInteractiveStrategyNever
: The SDK looks for a locally stored authorization grant from previousauthorize:withHandler
responses. If one is available, valid, and contains all requested scopes, the SDK will return anAMZNAuthorizeResult
object that contains an access token and user profile data. Otherwise, it returns anNSError
object viaAMZNAuthorizationRequestHandler
.
// Build an authorize request. AMZNAuthorizeRequest *request = [[AMZNAuthorizeRequest alloc] init]; request.scopes = @[alexaAllScope]; request.grantType = AMZNAuthorizationGrantTypeToken; request.interactiveStrategy = AMZNInteractiveStrategyNever; [AMZNAuthorizationManager sharedManager] authorize:request withHandler:^(AMZNAuthorizeResult *result BOOL userDidCancel NSError *error) { if (error) { // Error from the SDK, indicating the user was not previously authorized to your app for the requested scopes. } else { // Fetch the access token and return to controller self.token = result.token; [self userSuccessfullySignedIn]; } }];
- Create an
-
Implement
application:openURL:sourceApplication:annotation:
in the class in your project that handles theUIApplicationDelegate
protocol.By default this will be the
AppDelegate
class in your project. When the app presents the Amazon login page, if the user completes log in, it will redirect to the app using the URL Scheme the app registered earlier. That redirect is passed toapplication:openURL:sourceApplication:annotation:application:openURL:sourceApplication:annotation:
returns YES if the URL was successfully handled.handleOpenURL:sourceApplication:
is an SDK library function that will handle LWA redirect URLs for you. IfhandleOpenURL:sourceApplication:
returns YES, then the URL was handled.- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation { // Pass on the url to the SDK to parse authorization code from the url. BOOL isValidRedirectSignInURL = [AIMobileLib handleOpenURL:url sourceApplication:sourceApplication]; if(!isValidRedirectSignInURL) return NO; // App may also want to handle url return YES; }
-
Now you have an access token to use for requests to AVS.
Resources
Last updated: Jan 22, 2021