Get Even More Visitors To Your Blog, Upgrade To A Business Listing >>

Amazon Alexa Skills authenticated by Azure Active Directory and backed by ASP.NET Core 2.0 Web API hosted on Azure

This post is provided by Premier Field Engineer, Nathan Vanderby, who walks us through how to write an Alexa Skill using .NET core and AAD.


Amazon's Echo devices and Alexa assistant need no introduction. In this post I lay out how you can write an Alexa skill and use a .NET core 2.0 API backend service to handle the requests. We will also show how to leverage Azure Active Directory (AAD) so these calls can be authenticated and your users can get a personalized experience. Rob Reilly has a great write up on how to accomplish this using .NET Core 1.0. Here we'll update the tutorial to .NET Core 2.0, simplify the code with Alexa.NET NuGet package and fix a bug in his JWT middleware.

There are four main items that need to be configured, the Alexa Skill in Amazon's developer portal, an Azure App. Service, your .NET Core 2.0 API, and Azure Active Directory. Once these items are configured a user just needs to add the skill in their Alexa app, use Alexa app or website to link their devices to their AAD and then they can start sending skill requests!

Components of an Alexa Skill

  • A set of intents that represent actions that users can do with your skill. These intents represent the core functionality for your skill.
  • A set of sample utterances that specify the words and phrases users can say to invoke those intents. You map these utterances to your intents. This mapping forms the interaction model for the skill.
  • An invocation name that identifies the skill. The user includes this name when initiating a conversation with your skill.
  • A cloud-based service that accepts these intents as structured requests and then acts upon them. This service must be accessible over the Internet. You provide an endpoint for your service when configuring the skill.
  • A configuration that brings all of the above together so that Alexa can route requests to the service for your skill. You create this configuration in the Alexa developer portal.

Step-by-step tutorial

Step 1: Create an Azure Subscription

If you haven't already signed up for Azure you can do so here. This tutorial can be done using only free services. There is a free tier for Azure App. Service, Azure Active Directory App. registrations are free and Amazon doesn't charge for creating a skill.

Step 2: Create your API

In this step we'll create the API app and configure it to authenticate with Azure Active Directory. You can always configure the authentication manually, or simply use the Visual Studio API App template wizard to handle the heavy lifting for you. I'm going to use the wizard and I'll point out all of the changes it is doing behind the scenes.

First create a new project and select ASP.NET Core Web Application as the template.

Make sure your creating an ASP.NET Core 2.0 template, then select Change Authentication

Select from one of your Azure AD domains. Check Read directory data and optionally give your App ID URI a unique value. This URI is a unique identifier for your application as well as the audience URI when it comes to authentication later on. Normally the default is sufficient.

The authentication wizard automatically makes changes to 4 files, Startup.cs, AzureAdAuthenticaitonBuilderExtensions.cs, appsettings.json and ValuesController.cs.

In Startup it tells the app to use AAD with the JWT bearer authentication scheme and IIS to use authentication middleware.

In AzureAdServiceCollectionExtensions it defines the AddAzureAdBearer as a JwtBearer and sets the Audience and Authority properties for this application to trust and validate the token against.

In the appsettings.json it automatically puts your Azure AD information for the login URI, Domain, TenantId and ClientId. From the screenshot above we can see that that ClientId is being used as the audience value.

options.Audience = _azureOptions.ClientId;

We need to update this parameter to be the app URI instead of its Id. This is important otherwise you will get 401: Unauthorized result if the audience in the app code does not match the audience string in the authentication token from AAD. Note: In a production scenario I would probably add another setting called Audience to the appsettings & AzureAdOptions class then assign that property to the options.Audience property in the ConfigureAzureOptions class.

If you choose to configure authentication manually these settings come from as follows:

Instance - Azure AD login URL. This is dependent on the Azure cloud you are in. Commercial Azure, Azure Government, China & Germany have different URLs.
Domain - This is the AD tenant name where the app is registered.
Tenant ID - This is your Azure subscription tenant id/Azure AD Directory ID. This can be found in the Properties blade of Azure Active Directory resource.
Client ID - This is unique to the application. It is found in the app registration blade of Azure Active Directory. Remember though the template assigns the audience to the client id so instead of the actual client id we are assigning the app id URI to this setting.

To find out your Tenant ID go to Azure Active Directory resource and select Properties

To get to the client ID (Audience value), go to Azure Active Directory resource and select App registrations and click on your registration

Then select All Settings -> Properties and there will be an App ID URI. This should match the value from the Visual Studio new project wizard.

In the ValuesController class it's important to note the [Authorize] attribute on the controller. This attribute means only authenticated (and authorized users if roles are defined) can call these methods. This attribute can also be applied at the method level.

Now that your application is configured to leverage Azure Active Directory we need to add code to handle the Alexa request. To start, add the Alexa.NET NuGet Package https://github.com/timheuer/alexa-skills-dotnet

Then, alter your ValuesController to accept a SkillRequest and return a SkillRepsonse from a Post method like so:

[Authorize]
[Route("api/[controller]")]
public class ValuesController : Controller
{
    [HttpPost]
    public SkillResponse Post([FromBody]SkillRequest request)
    {
        SkillResponse response = null;
        if (request != null)
        {
            PlainTextOutputSpeech outputSpeech = new PlainTextOutputSpeech();
            string firstName = (request.Request as IntentRequest)?.Intent.Slots.FirstOrDefault(s => s.Key == "FirstName").Value.Value;
            outputSpeech.Text = "Hello " + firstName;
            response = ResponseBuilder.Tell(outputSpeech);
        }

        return response;
    }
}

Without the [Authorize] attribute this code will work without account linking. Alexa sends the authentication token in the body of the request whereas the Authorize attribute expects it to be in the Authorization header. There are numerous ways to handle this. We will address this by adding a piece of middleware to automatically look at each request and if the body contains an authorization token then move it to the header. This allows the default authentication middleware to work seamlessly.

Create a new class called AlexaJwtMiddleware and copy the code below in. It's fairly straightforward and heavily commented. Its sole responsibility is to search through the request and move the authentication token from the body to the authorization header.

public class AlexaJWTMiddleware
{
    private readonly RequestDelegate _next;

    public AlexaJWTMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task Invoke(HttpContext context)
    {
        if (context.Request.Headers.Keys.Contains("Authorization"))
        {
            await _next(context);
            return;
        }

        // Keep the original stream in a separate
        // variable to restore it later if necessary.
        var stream = context.Request.Body;

        // Optimization: don't buffer the request if
        // there was no stream or if it is rewindable.
        if (stream == Stream.Null || stream.CanSeek)
        {
            await _next(context);
            return;
        }

        try
        {
            using (var buffer = new MemoryStream())
            {
                // Copy the request stream to the memory stream.
                await stream.CopyToAsync(buffer);
                byte[] bodyBuffer = new byte[buffer.Length];
                buffer.Position = 0L;
                buffer.Read(bodyBuffer, 0, bodyBuffer.Length);
                string body = Encoding.UTF8.GetString(bodyBuffer);

                if (!string.IsNullOrEmpty(body))
                {
                    SkillRequest request = JsonConvert.DeserializeObject(body);
                    if (request.Session.User.AccessToken != null)
                    {
                        context.Request.HttpContext.Request.Headers["Authorization"] = $"Bearer {request.Session.User.AccessToken}";
                    }
                }

                // Rewind the memory stream.
                buffer.Position = 0L;

                // Replace the request stream by the memory stream.
                context.Request.Body = buffer;

                // Invoke the rest of the pipeline.
                await _next(context);
            }
        }
        finally
        {
            // Restore the original stream.
            context.Request.Body = stream;
        }
    }
}

// Extension method used to add the middleware to the HTTP request pipeline.
public static class AlexaJWTMiddlewareExtensions
{
    public static IApplicationBuilder UseAlexaJWTMiddleware(this IApplicationBuilder builder) => builder.UseMiddleware();
}

Finally we need to tell the Startup class to also use this middleware. Inside of Startup.cs Configure() method add a line before the authentication middleware. Order is important here as we need our new middleware to move the token before the authentication middleware searches for it to validate it.

Step 3: Publish to Azure

Now that your application has the proper code we need to publish it to Azure. There are a myriad ways to do this. One of the simplest is to right click on the project and select publish. Then follow the wizard to deploy to an App Service.

On the first screen either create a new app service or use an existing one if you already have one setup.

Then fill in the info for your existing or new app service. When finished click Ok/Create then Publish

Step 4: Configure Azure Active Directory for the Alexa Front End

With our API registered in AAD and deployed to Azure we need to integrate it with Amazon's Alexa service. The first step is to create another application registration for the Alexa service to leverage for user authentication.

Create another app registration in AAD and call it AlexaFrontEnd. It's type will be Native and the RedirectURI will be https://pitangui.amazon.com/api/skill/link/{lookuplater}. We will find out the last part of the RedirectURI after we create an Alexa skill in a later step.

Now that this app registration is setup we need to give it delegate permissions into our API. This allows user access to our backend API if they get their token from the front end (Alexa service) app. This is required since the users are not directly calling our API from a web browser.

Inside the newly created FrontEnd registration navigate to the app registration settings and select Required permissions.

Select Add and start typing in AlexaBackendAPI. Select the app registration we created earlier.

That’s it for now. We’ll come back to both app registrations later to update the Redirect URI (front end) and get a Client Secret (AKA Key) from the backend API after we have created our new Alexa Skill in the Alexa Developer Portal.

Step 5: Configure the Alexa Skill

Now that we have our code in Azure we need to configure the Alexa skill. Navigate to Amazon's developer page for Alexa, you will have to create an account. Under Alexa Skills Kit click Get Started.

Then select Add a New Skill from the upper right corner.

Skill Information

In the Skill Information screen select Custom Interaction Model, give the skill a name and an invocation name that people will use when talking to an Echo. Click Save then Next to move onto the Interaction Model.

Interaction Model

Under Intent Schema fill in this JSON:

{
"intents" :
[
	{
		"intent" : "Tutorial",
		"slots" :
		[
			{
				"name" : "FirstName",
				"type" : "AMAZON.DE_FIRST_NAME"
			}
		]
	},
	{
		"intent" : "AMAZON.HelpIntent"
	},
	{
		"intent" : "AMAZON.StopIntent"
	}
]
}

The intent here is so Alexa knows that when we talk to "Tutorial" it should also expect a parameter called FirstName. If you look back at the code in the API Post method you will see how we can read the FirstName slot from the Tutorial intent on the request.

Leave Custom Slot Types blank.

Under Sample Utterances add Tutorial Can you greet {FirstName}
Notice how this sample utterance uses both the intent and the FirstName slot.

Click Save then Next to move onto Configuration.

Configuration

Select HTTPS for the endpoint. Under the Default text box enter the URL for your API method.

In the Account Linking section select Yes. Enter the following information into the fields:

  • Authorization URL: https://login.windows.net/**{TenantId (DirectoryId)}/oauth2/authorize?resource=** The TenandId/DirectoryId is the same GUID that is in the appsettings.json config file. The App ID URI is the URI we entered when we created the API. This can be found in the Properties settings of the app registration for your API. In this tutorial it is https://AlexaToAzure.onmicrosoft.com/AlexaBackendAPI This parameter is needed as it tells AAD which resource Alexa is requesting access to and defines the audience property in the JWT token returned from AAD.
  • Client Id: This is the Application Id from the native (FrontEnd) app registration
  •  
  • Authorization Grant Type: Auth Code Grant
  • Access Token URI: https://login.windows.net/**{TenantId (DirectoryId)}**/oauth2/token This URI can be found in the Endpoints settings of the app registration blade. Copy the OAUTH 2.0 TOKEN ENDPOINT
  • Client Secret: This is effectively the password the Alexa service will use to allow account linking to your API backend. We will generate one now. Navigate to the Keys setting in the API app registration.
    Give a key a description and a duration then click Save. The key will be generated on the save. This will be the one time this key is displayed, so copy it.
  • Client Authentication Scheme: HTTP Basic (Recommended)

In the end your Account Linking settings should look similar to this:

Before we move onto the next screen, notice the Redirect URIs in the settings. Copy these URLs into the Redirect URLs setting for the native app (FrontEnd). This replaces the one we used earlier that had in it.

Click Save then Next

SSL Certificate

In this section select My development endpoint is a sub-domain of a domain that has a wildcard certificate from a certificate authority and click next.

Test

Testing won't work until you perform account linking in the next step or remove the [Authorize] attribute from the API controller. We'll come back to this later.

Step 6: Link Accounts and Test
  1. Login to https://alexa.amazon.com
  2. Navigate to Skills -> Your Skills
  3. You should see our newly created skill. Click on it.
  4. Select SETTINGS
  5. Click on the Link Account button in the upper right corner
  6. You will be redirected to login to Azure Active Directory and then asked to accept the access being requested. Once you get through that result and if all goes well you should see:
Step 7: Testing

We have multiple options by which to test the Alexa Integration. We can use an actual device, the Amazon Developers Portal Test client or there is an Alexa Skill Testing Emulator. I'll go over the last two since they don’t require you to purchase anything.

Amazon Developer Portal

  1. In the Amazon Developer portal where we configured the Alexa skill Note: If you left this tab over from the prior instructions, you will have to refresh the page after perofrming the account linking
  2. Make sure you are under the Skill you are wanting to test
  3. Click on the test section
  4. In the Enter Utterance Section enter the following: Tutorial Can you greet
  • = your name or any name
  • This is derived in the Interaction Model section for the skill.
  1. Click the Ask AlexaToAzure button.
  • If all goes well you should see something that looks like:
  • if you get a 401: Unauthorized result, see some debugging tips below.

Alexa Skill Testing Tool (Emulator)

The Alexa emulator is a simulated version of using an Alexa device that is surfaced in a web page. You need to have a microphone and speaker to use this tool.

  1. Open browser and go to following URL: https://echosim.io/welcome?next=%2F
  2. Click on the Login with Amazon button.
  • Use your Amazon Developer account
  1. Once logged in you’re ready to test

Troubleshooting

  • Double check Web App settings in Azure aren't overriding the web.config settings
  • F12 browser tool
    • If you need to look at the HTTP Request Response stream pressing F12 from Explorer, Edge, Chrome will open integrated developer tools.
  • JWT.IO - https://jwt.io/
    • This is a browser based tool where you can take your JWT token from the request and decode it and verify the signature
  • If account linking works and your still getting a 401 unauthorized response try the following:
    • Refresh the Amazon develop page and try again (this makes Amazon request a new token)
  • Enabling diagnostic logs, detailed error messages and leveraging live streaming of logs in the Azure portal for the web app is very useful for debugging

Sample Code

The companion source code for the web API portion of this tutorial can be located on GitHub at https://github.com/vanderby/AlexaToAzureAPI

Links

  • Visual Studio and .NET Core API’s
    • https://www.microsoft.com/net
  • Azure SDKs and Tools
    • https://azure.microsoft.com/en-us/downloads/
    • https://blogs.msdn.microsoft.com/premier_developer/2016/12/12/amazon-alexa-skills-development-with-azure-active-directory-and-asp-net-core-1-0-web-api/
  • ASP.NET Authentication attribute documentation
    • https://msdn.microsoft.com/en-us/library/system.web.mvc.authorizeattribute(v=vs.118).aspx
  • Amazon developer documentation
    • https://developer.amazon.com/alexa-skills-kit

Premier Support for Developers provides strategic technology guidance, critical support coverage, and a range of essential services to help teams optimize development lifecycles and improve software quality.  Contact your Application Development Manager (ADM) or email us to learn more about what we can do for you.

Share the post

Amazon Alexa Skills authenticated by Azure Active Directory and backed by ASP.NET Core 2.0 Web API hosted on Azure

×

Subscribe to Msdn Blogs | Get The Latest Information, Insights, Announcements, And News From Microsoft Experts And Developers In The Msdn Blogs.

Get updates delivered right to your inbox!

Thank you for your subscription

×