Introduction
When we are designing and implementing a new Alexa Custom Skill, one of the first questions we ask ourselves is whether we have access to user’s email or not. Recently (July 2018), Amazon Alexa’s Team released a new feature to allow Alexa Skill’s Developers to request users to access resources like email
, phone
and customer name
. Until that time, we only were able to request permissions for Device Address
and Lists
.
There is a blob post from Alexa's Team
explaining how to request customer contact information.
Account Linking vs Permissions on gathering user’s email
Until now, there were some scenarios like sending detailed information to customers over email that required Account Linking
either with Amazon
or Custom Authentication Provider
. But with this new feature if we only do Account Linking to gather the user email, then we don’t need it anymore and we can use this simpler approach to get permissions from the user and get their email.
Skill configuration to request customer permissions.
Using Amazon Developer Console we can manage our skill. Navigate to the Build -> Permissions
page in the console and select Customer Email Address
:
Note: If you are using
ask-cli
to configure and update your skill then you should add permissions in yourskill.json
:
Get user’s email in our code
In order to get the email to be used in our code, we have to do query Amazon’s API asking for it and we have to use the field apiAccessToken
provided under context -> System
in our handlerInput
object.
apiAccessToken
Before going forward and showing the code, I’d like to share the different scenarios we can have and the different outputs for the property apiAccessToken
.
User denies
Email Address
permission1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16"context": {
"System": {
"application": {
"applicationId": "amzn1.ask.skill.<skill-id>"
},
"user": {
"userId": "amzn1.ask.account.<user-id>"
},
"device": {
"deviceId": "amzn1.ask.device.<device-id>",
"supportedInterfaces": {}
},
"apiEndpoint": "https://api.amazonalexa.com",
"apiAccessToken": "eyJ0e....<rest-of-jwt-token>"
}
}Note that even if we have no permissions added to our skill, we still get
apiAccessToken
property. If we inspectapiAccessToken
JWT token using https://jwt.io, we can see the belowpayload data
whereprivateClaims.consentToken
isnull
:1
2
3
4
5
6
7
8
9
10
11
12
13{
"aud": "https://api.amazonalexa.com",
"iss": "AlexaSkillKit",
"sub": "amzn1.ask.skill.<skill-id>",
"exp": 1633532432,
"iat": 1633528832,
"nbf": 1633528832,
"privateClaims": {
"consentToken": null,
"deviceId": "amzn1.ask.device.<device-id>",
"userId": "amzn1.ask.account.<user-id>"
}
}User grants
Full Name
permission but deniesEmail Address
permission.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19"context": {
"System": {
"application": {
"applicationId": "amzn1.ask.skill.<skill-id>"
},
"user": {
"userId": "amzn1.ask.account.<user-id>",
"permissions": {
"consentToken": "eyJ0e...<rest-of-jwt-token>...lFkHDw"
}
},
"device": {
"deviceId": "amzn1.ask.device.<device-id>",
"supportedInterfaces": {}
},
"apiEndpoint": "https://api.amazonalexa.com",
"apiAccessToken": "eyJ0e...<rest-of-jwt-token>...rTyOD"
}
}Note, apart from having a different
apiAccessToken
, now we also have a newpermissions
property underuser
. Now, if we inspectapiAccessToken
using https://jwt.io, we can see howconsentToken
is notnull
because it has permissions forName
included on it. But still if we try to authenticate to Amazon’s API to query foremail
with that token, it will fail with 403 Forbidden:1
2
3
4
5
6
7
8
9
10
11
12
13{
"aud": "https://api.amazonalexa.com",
"iss": "AlexaSkillKit",
"sub": "amzn1.ask.skill.<skill-id>",
"exp": 1633532432,
"iat": 1633528832,
"nbf": 1633528832,
"privateClaims": {
"consentToken": "Atza|...<rest-of-amazon-token-authorizing-full-name>...",
"deviceId": "amzn1.ask.device.<device-id>",
"userId": "amzn1.ask.account.<user-id>"
}
}User grants
Email Address
permission1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19"context": {
"System": {
"application": {
"applicationId": "amzn1.ask.skill.<skill-id>"
},
"user": {
"userId": "amzn1.ask.account.<user-id>",
"permissions": {
"consentToken": "eyJ0e...<rest-of-jwt-token>...lFkHDw"
}
},
"device": {
"deviceId": "amzn1.ask.device.<device-id>",
"supportedInterfaces": {}
},
"apiEndpoint": "https://api.amazonalexa.com",
"apiAccessToken": "eyJ0e...<rest-of-jwt-token>...rTyOD"
}
}It is almost look a like of scenario’s 2 token, the difference is
consentToken
is different and now it does allow querying for email.1
2
3
4
5
6
7
8
9
10
11
12
13{
"aud": "https://api.amazonalexa.com",
"iss": "AlexaSkillKit",
"sub": "amzn1.ask.skill.<skill-id>",
"exp": 1633532432,
"iat": 1633528832,
"nbf": 1633528832,
"privateClaims": {
"consentToken": "Atza|...<rest-of-amazon-token-authorizing-email>...",
"deviceId": "amzn1.ask.device.<device-id>",
"userId": "amzn1.ask.account.<user-id>"
}
}User grants
Email Address
permission + User authenticate throughAccount Linking
as well We can have both working together, Account Linking + Permissions for email. The main difference in terms of thehandlerInput
schema is, we willuser.accessToken
property which we can use to authenticate and authorize against our custom Authentication Server:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20"context": {
"System": {
"application": {
"applicationId": "amzn1.ask.skill.<skill-id>"
},
"user": {
"userId": "amzn1.ask.account.<user-id>",
"accessToken": "eyJ0...<jwt-account-linking-access-token>...",
"permissions": {
"consentToken": "eyJ0e...<rest-of-jwt-token>...lFkHDw"
}
},
"device": {
"deviceId": "amzn1.ask.device.<device-id>",
"supportedInterfaces": {}
},
"apiEndpoint": "https://api.amazonalexa.com",
"apiAccessToken": "eyJ0e...<rest-of-jwt-token>...rTyOD"
}
}Note that giving permissions and doing account linking are two separate processes for Alexa Skill. So, they can be used together or by its own.
Calling Amazon’s API to get user’s email
Finally, after understanding what apiAccessToken
does for the different scenarios, let’s put our hands on and actually do the code that will access to Amazon’s API from our intent:
1 | const axios = require("axios"); |
Recap
- We can use either Amazon Development Console or ASK CLI to request access for
Email
permissions to our users. - Account Linking and Permissions are independent features and they can be used together or separately.
- Depending on the configuration and the user input we will have different outputs on
apiAccessToken
. - Accessing to user’s
email
will require to call Amazon’s API withapiAccessToken
as a Authentication Bearer header.