Validate and Use Access Tokens in Custom Skill Code
Once a user successfully enables your skill and links their Alexa account with the other service, requests sent to your skill include the user's access token (accessToken
). Add logic to the service for your skill to verify and use this access token.
- Decide which requests require account linking
- Get the access token from the request
- Validate the access token
- Respond to the user if the token is invalid or missing
- Related topics
This document covers account linking logic for custom skills. If you are working on a smart home or video skill, see Validate and Use Access Tokens in Smart Home and Video Skill Code.
Decide which requests require account linking
A custom skill can require authentication (and therefore a valid access token) on all requests, or on just some requests. This depends on your skill's design and functionality.
For example, the Ride Hailer skill might require user authentication to order a car, but does not require authentication to just ask whether the service is available in a particular city. Therefore, the code for this skill might do the following:
- The handler for the
OrderCar
intent checks for a validaccessToken
and uses that token to retrieve the user's Ride Hailer profile and order a car. - The handler for the
RideHailerAvailabilityByCity
intent looks up public information on the Ride Hailer service and returns a response. This handler does not need to check for anaccessToken
. - The handler for the
LaunchRequest
provides an intro to the skill and asks the user what they want to do. This handler does not need to check for anaccessToken
.
For requests that require authentication, do the following:
- Get the access token from the request
- Validate the token and authenticate the user
- Return a link account card and user-friendly output speech if the token is not present or is invalid.
For intents that do not require authentication, you can skip these steps and just provide a normal response.
Required account linking
Some skills have no alternate functionality to offer if the user has not linked their account. In this case, every request needs to check for the access token and return an appropriate prompt and link account card if the user has not linked their account:
User: Alexa, open Ride Hailer.
Alexa: Welcome to Ride Hailer. To use this skill, you must have a Ride Hailer account. I've sent some information to the Alexa app. Open your Alexa app and click on the link to connect your Ride Hailer account with Alexa.
Session ends.
If all of your requests require account linking, be sure turn off the Allow users to enable skill without account linking option.
Optional account linking
If your skill includes meaningful functionality that does not require the linked account, turn on the Allow users to enable skill without account linking option. This lets users skip the account linking flow when they enable the skill, which can reduce user friction and encourage users to try your skill. See Let Users Enable Your Skill without Account Linking.
One way to encourage users to link their accounts is to explain the benefits of connecting to the user account and ask the user if they want to proceed. The welcome message you return in your LaunchRequest
is a good place to do this. For example:
User: Alexa, open Ride Hailer.
Alexa: Welcome to Ride Hailer. If you have a Ride Hailer account, I can order you a ride. Do you want to set that up now?
User: Yes. (This sends the built-in AMAZON.YesIntent
)
The skill sends the link account card, as discussed in Respond to the User if the Token is Invalid or Missing
Alexa: OK, I've sent some information to the Alexa app. Open your Alexa app and click on the link to connect your Ride Hailer account with Alexa.
Session ends.
If the user responds with "no," your skill can offer alternate functionality that does not require an account:
User: Alexa, open Ride Hailer
Alexa: Welcome to Ride Hailer. If you have a Ride Hailer account, I can order you a ride. Do you want to set that up now?
User: No.
Alexa: OK, without a Ride Hailer account, I can give you driving directions and traffic reports. I can also tell you the cities where our service is available. You can ask for a specific city, or ask for our most recently added cities. Which do you want?
Conversation with the user continues…
Get the access token from the request
If the user has successfully linked their account, the user's access token (accessToken
) is included in each request
sent to your skill as long as their accessToken
is valid. If they have not yet linked their account, the accessToken
property is not included in the request.
If you configured the authorization code grant flow and the Alexa service is unable to obtain a new pair of tokens from your authorization server due to an invalid or expired refresh token for a user who is already linked, the accessToken
property isn't included in the request to your skill. In this scenario, the Alexa service deletes the existing invalid or expired tokens and sends a push notification and a re-linking home card to the user's Alexa app to have them link their account back.
The token is available in the accessToken
property of the user
object, which is available in the context
object. The context
object is included in all requests. You can access the accessToken
in context.System.user.accessToken
.
In this example, the user's accessToken
is "Atza|<accessToken>
". Note that a real access token is typically much longer than this. Some objects within the request are not shown for brevity:
{
"version": "1.0",
"session": {},
"context": {
"AudioPlayer": {
"playerActivity": "IDLE"
},
"Display": {},
"System": {
"application": {
"applicationId": "amzn1.ask.skill.1111111-2222-3333-4444-5555555"
},
"user": {
"userId": "amzn1.ask.account.XXXXXXXXXX",
"accessToken": "Atza|<accessToken>"
},
"device": {},
"apiEndpoint": "https://api.amazonalexa.com",
"apiAccessToken": "<apiToken>"
}
},
"request": {}
}
Do not confuse the accessToken
with the apiAccessToken
included on all requests. The apiAccessToken
is used for Alexa Skills Kit APIs, such as the Device Settings API and Progressive Response API.
user
object is also available in the session
. Note that some requests, such as AudioPlayer requests, can occur outside of a normal skill session and do not include the session
object. All requests always include context
.This example shows how you access the accessToken
in your request handler.
This code example uses the Alexa Skills Kit SDK for Node.js (v2).
const OrderCarIntentHandler = {
//...
handle(handlerInput){
// This intent requires an access token so that we can get the user's
// Ride Hailer user profile with payment information.
// The access token is in the Context object. Access the
// request in the HandlerInput object passed to the
// handler.
var accessToken = handlerInput.requestEnvelope.context.System.user.accessToken;
// ...
}
};
This code example uses the Alexa Skills Kit SDK for Java.
public class OrderCarIntentHandler implements RequestHandler {
// ...
@Override
public Optional<Response> handle(HandlerInput handlerInput) {
// This intent requires an access token so that we can get the user's
// Ride Hailer user profile with payment information.
// The access token is in the Context object. Access the
// request in the HandlerInput object passed to the
// handler.
String accessToken = handlerInput
.getRequestEnvelope()
.getContext()
.getSystem()
.getUser()
.getAccessToken();
// ...handler continues
}
}
Validate the access token
For a request that requires authentication, your code should do at least two checks:
- Verify that the
accessToken
exists. - Verify that the token represents a valid user in your resource server.
Verify that the token exists
Make sure that the accessToken
exists. If the accessToken
property is not included in the request, this means that the user has not yet successfully linked their account or that the tokens for the user's linked account are expired or invalid.
In the Alexa Skills Kit SDK for Node.js, the accessToken
property on the user
(for example, handlerInput.requestEnvelope.context.System.user.accessToken
) returns undefined
if the token does not exist. In the Alexa Skills Kit SDK v2 for Java, User.getAccessToken()
returns null
if the token does not exist.
Verify that the token is valid
If the accessToken
exists, verify that it identifies a user in your resource server. The token could become invalid for multiple reasons, for example:
- The user deleted or canceled their account with your service. For example, an Alexa user might have set up account linking with Ride Hailer, then later canceled their Ride Hailer account. At this point, the token stored by the Alexa service would identify a non-existent user.
- The token has expired, and the Alexa service was unable to obtain a new token. This can occur with an authorization code grant if your authorization server does not provide refresh tokens. This can also occur if you use an implicit grant, which does not support refresh tokens.
If the token is valid, handle the request normally. You can use the token to access data from your resource server as needed. In the Ride Hailer example, the skill would retrieve profile and payment information for the user from the Ride Hailer service, order a car, and return a confirmation to the user. For details, see Return a Response.
Respond to the user if the token is invalid or missing
If the access token does not exist or is invalid on a request that requires authentication, return a response containing the following:
- Output speech text explaining to the user that they need to link their account to use this feature. Succinctly explain the benefit of account linking in the speech text.
- A link account card. This is a special type of card that tells the user to link their account. When displayed in the Alexa app, this card displays a link to your authorization URI. The user can start the account linking process right from this card.
In your JSON response include the card
property with the type
set to LinkAccount
:
{
"version": "1.0",
"sessionAttributes": {},
"response": {
"outputSpeech": {
"type": "PlainText",
"text": "You must have a Ride Hailer account to order a car. Please use the Alexa app to link your Amazon account with your Ride Hailer Account."
},
"card": {
"type": "LinkAccount"
},
"shouldEndSession": true
}
}
Any additional card
properties, such as cardTitle
, are ignored.
The following examples show how to return a link account card with either the Alexa Skills Kit SDK for Node.js or the Alexa Skills Kit SDK v2 for Java.
This code example uses the Alexa Skills Kit SDK for Node.js (v2).
const OrderCarIntentHandler = {
// ...
handle(handlerInput){
// This intent requires an access token so that we can get the user's
// Ride Hailer user profile with payment information.
// The access token is in the Context object. Access the
// request in the HandlerInput object passed to the
// handler.
var accessToken = handlerInput.requestEnvelope.context.System.user.accessToken;
if (accessToken == undefined){
// The request did not include a token, so tell the user to link
// accounts and return a LinkAccount card
var speechText = "You must have a Ride Hailer account to order a car. " +
"Please use the Alexa app to link your Amazon account " +
"with your Ride Hailer Account.";
return handlerInput.responseBuilder
.speak(speechText)
.withLinkAccountCard()
.getResponse();
} else {
// Use the token to access the user's profile. This should also verify that the
// token represents a valid Ride Hailer user.
// ...
}
}
};
This code example uses the Alexa Skills Kit SDK for Java.
public class OrderCarIntentHandler implements RequestHandler {
// ...
@Override
public Optional<Response> handle(HandlerInput handlerInput) {
// This intent requires an access token so that we can get the user's
// Ride Hailer user profile with payment information.
// The access token is in the Context object. Access the
// request in the HandlerInput object passed to the
// handler.
String accessToken = handlerInput
.getRequestEnvelope()
.getContext()
.getSystem()
.getUser()
.getAccessToken();
if (accessToken != null) {
// Call a method to validate the token, get the user's Ride Hailer
// profile and order a ride
return orderCarForUser(accessToken, handlerInput);
} else {
// The request did not include a token, so tell the user to link
// accounts and return a LinkAccount card
String speechText = "You must have a Ride Hailer account to order a car. "
+ "Please use the Alexa app to link your Amazon account "
+ "with your Ride Hailer Account.";
// Build a response with output speech and a LinkAccount card.
return handlerInput.getResponseBuilder()
.withSpeech(speechText)
.withLinkAccountCard()
.build();
}
}
}
In most cases, this response should end the session, since the user cannot continue with their request until after they have linked their account. For a better user experience, save any relevant user data in a persistent data store so that the user can pick up where they left off after they link the account and return to the skill.
If your skill includes some intents that don't require authentication, it may make sense to ask the user a different question and keep the session open instead.
Related topics
- Quick Reference: Add Account Linking to an Alexa Skill
- Add Account Linking to Your Alexa Skill
- Account Linking Concepts for Alexa Skills
- The OAuth 2.0 Authorization Framework (RFC 6749)
- OAuth.com
Last updated: Sep 10, 2024