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.  Lasted updated, June 2018.


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.

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 populates your Azure AD information including: Instnace, 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;

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.

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 select your app registration.

You can copy the application ID from the app registration list or click the "Click to Copy" icon next to the application ID on the app registration details page.

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<SkillRequest>(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<AlexaJWTMiddleware>();
}

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 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. Navigate to Alexa Skills Kit -> Get Started -> Alexa Skills Kit.

Select Start a Skill then Create Skill.

Give the skill a name, click next. Select the Custom model then click Create Skill.

Invocation

Select Invocation and enter a phrase to invoke your skill. Click Save then Next to move onto the Interaction Model.

Intents

Select the Add button next to Intents and give the intent a name. We will use Tutorial.

Type in a sample utterance such as Can you greet {FirstName} and click Add to create the sample utterance and the FirstName slot.

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.

Make sure the FirstName slot has a type of Amazon.DE_FIRST_NAME.

In the JSON Editor your skill should look like

{
    "interactionModel": {
        "languageModel": {
            "invocationName": "Tutorial",
            "intents": [
                {
                    "name": "Tutorial",
                    "slots": [
                        {
                            "name": "FirstName",
                            "type": "AMAZON.DE_FIRST_NAME"
                        }
                    ],
                    "samples": [
                        "Can you greet {FirstName}"
                    ]
                },
                {
                    "name": "AMAZON.HelpIntent",
                    "samples": []
                },
                {
                    "name": "AMAZON.StopIntent",
                    "samples": []
                }
            ],
            "types": []
        }
    }
}

Click Save Model and then Build Model

Interfaces

Here you can add various interfaces. We won't be using any of these. The various options for these are Audio Player, Display Interface, Video App, Alexa Gadget, etc.

Endpoint

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

Select My development endpoint is a sub-domain of a domain that has a wildcard certificate from a certificate authority from the drop down.

Account Linking

In the Account Linking section enable the slider. Enter the following information into the fields:

  • Authorization Grant Type: Auth Code Grant

  • Authorization URI: https://login.microsoftonline.com/**{TenantId/DirectoryId}**/oauth2/authorize?resource=**{Application ID}**
    The TenandId/DirectoryId is the same GUID that is in the appsettings.json config file. The Application ID is the Application ID from the app registration. This parameter is required by Azure AD and it tells AAD which resource Alexa is requesting access to and defines the audience property in the JWT token returned from AAD. According to the documentation here the resource parameter is only recommended, but if you debug account linking with Postman you will notice it fails and states the resource parameter is required.

  • Client Id: This is the Application Id from the app registration

  • 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. Select a 1 or 2 year duration. A key with "never expires" duration may fail account linking. 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 app registration. This replaces the one we used earlier that had in it.

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:

If you are unsuccessful linking your account see the troubleshooting steps below.

Step 7: Testing

We have multiple options by which to test the Alexa Integration. We can use an actual device or the Amazon Developers Portal Test client.

Amazon Developer Portal

  1. In the Amazon Developer portal where we configured the Alexa skill Note: If you left this tab open from the prior instructions, you will have to refresh the page after performing 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: ask Tutorial to greet
  • = any name

  • This is derived in the Invocation Model section for the skill.
  1. If all goes well you should see something that looks like:
  • if you get a 401: Unauthorized result check that the skill is account linked or see some debugging tips below.

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)

    • Check the skill is successfully account linked.
  • 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
  • Use Postman to troubleshoot account linking problems. A video on how to do this can be found here.

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



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.

Comments (2)

  1. Dave Rendón says:

    Great info, thanks for sharing!

  2. Nick Cipollina says:

    Any chance that you have an update for ASP.Net Core 2.1? The solution generated by Visual Studio is slightly different and I’m running into issues in getting this to work.

Skip to main content