Configure Alexa Skill to use Blazor Backend
Configure Alexa Skill to use Blazor Backend
![](https://www.arjunkrishna.us/img//amazon-alexa.jpg)
I have an Alexa Skill for a personal project that utilizes azure functions for its backend. I had started dabbling into blazor wasm and wanted to try connecting an alexa skill to it with account linking functionality. I found a sample public repo which utilizes Identity server for managing users, so it was a perfect test bed for alexa account linking functionality. I faced some issues while doing that. I jotted down the steps which finally made it work and added them into a sample repository as a README.md file. I am adding the same here. This will help anyone setup their blazor backend to accept alexa skill requests. I still prefer Azure functions for this, but this was a fun little exercise.
Github Repo
The extended sample repo is available on github : Blazor-WASM-Identity-gRPC-Alexa
StartUp.cs
var alexaVendor = Configuration["Alexa:BlazorNews:VendorId"]; // this key setup is shown later in this blog post.
var alexaSecretText = "AlexaBlazorNewsSecret"; // I use this secret under the Alexa configuration. This could also be added to the config secret like above
//all of these setting can be saved in a db store as well.
var client = new IdentityServer4.Models.Client
{
ClientId = "AlexaBlazorNews",
ClientName = "AlexaBlazorNews",
Enabled = true,
AllowedGrantTypes = GrantTypes.Code,
AllowAccessTokensViaBrowser = true,
RequireConsent = false,
RequirePkce = false,
RequireClientSecret = true,
AllowRememberConsent = true,
ClientSecrets = {new Secret(alexaSecretText.Sha256()) },
RedirectUris =
{
"https://pitangui.amazon.com/api/skill/link/" + alexaVendor, // all these can be setup earlier and then assigned here...
"https://layla.amazon.com/api/skill/link/" + alexaVendor,
"https://alexa.amazon.co.jp/api/skill/link/"+alexaVendor
},
PostLogoutRedirectUris =
{
"https://pitangui.amazon.com/api/skill/link/" + alexaVendor,
"https://layla.amazon.com/api/skill/link/" + alexaVendor,
"https://alexa.amazon.co.jp/api/skill/link/"+alexaVendor
},
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Email,
IdentityServerConstants.StandardScopes.Phone,
"alexa"
},
AllowOfflineAccess = true,
AccessTokenType = AccessTokenType.Jwt,
};
var clients = new List<IdentityServer4.Models.Client>();
var configClients = Configuration.GetSection("IdentityServer:Clients") //showing how you can combine config as well as code settings
.Get<IdentityServer4.Models.Client[]>();
clients.Add(client);
clients.AddRange(configClients);
services.AddIdentityServer()
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>(options =>
{
options.IdentityResources["openid"].UserClaims.Add("role"); // Roles
options.ApiResources.Single().UserClaims.Add("role");
options.IdentityResources["openid"].UserClaims.Add("email");
options.ApiResources.Single().UserClaims.Add("email");
options.IdentityResources["openid"].UserClaims.Add("name");
options.ApiResources.Single().UserClaims.Add("name");
options.Clients.AddRange(clients.ToArray());
});
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("role");
services.AddTransient<IProfileService, ProfileService>();
services.AddControllersWithViews().AddNewtonsoftJson(); // newtonsoftjson is needed because alexa.net has not been migrated to Text.Json yet.
AlexaSkillController.cs
[HttpPost("api/AlexaSkill/Request")]
public IActionResult HandleResponse([FromBody] SkillRequest input)
{
var requestType = input.GetRequestType();
SkillResponse response = null;
var name = "";
var jwtEncodedString = input.Session.User.AccessToken;
if (jwtEncodedString is null)
{
response = ResponseBuilder.TellWithLinkAccountCard("You are not currently linked to this skill. Please go into your Alexa app and sign in.");
response.Response.ShouldEndSession = true;
return new OkObjectResult(response);
}
var token = new JwtSecurityToken(jwtEncodedString: jwtEncodedString);
var claims = token.Claims;
name = claims.First(c => c.Type == "name").Value;
if (requestType == typeof(LaunchRequest))
{
response = ResponseBuilder.Tell($"Welcome to Blazor News {name}!");
response.Response.ShouldEndSession = false;
}
// return information from an intent
else if (requestType == typeof(IntentRequest))
{
// do some intent-based stuff
var intentRequest = input.Request as IntentRequest;
if (intentRequest.Intent.Name.Equals("news"))
{
// get the news articles
var news = GetNews();
if (news == 0)
response = ResponseBuilder.Tell("We have no blazor news at this time.");
else
response = ResponseBuilder.Tell("There are " + news.ToString() + " blazor news articles.");
response.Response.ShouldEndSession = false;
}
else
{
response = ResponseBuilder.Ask("I don't understand. Can you please try again?", null);
response.Response.ShouldEndSession = false;
}
}
else if (requestType == typeof(SessionEndedRequest))
{
response = ResponseBuilder.Tell("See you next time!");
response.Response.ShouldEndSession = true;
}
return new OkObjectResult(response);
}
private static int GetNews()
{
return 3; //sample code to return the count of articles.
}
NGrok Setup
NGrok Setup: NGrok Dashboard Link
![Download Grok](https://www.arjunkrishna.us/img/2020/12/2020-12-21_11-49-35.jpg)
![NGrok Token Setup](https://www.arjunkrishna.us/img/2020/12/2020-12-21_13-55-45.jpg)
![NGROK command](https://www.arjunkrishna.us/img/2020/12/2020-12-21_13-54-17.jpg)
![NGrok url for forwarding](https://www.arjunkrishna.us/img/2020/12/2020-12-21_13-56-31.jpg)
Alexa Console Setup
Website to configure Alexa Skill : Alexa Developer Console
![Alexa Create New Skill](https://www.arjunkrishna.us/img/2020/12/2020-12-21_11-45-28.jpg)
![Alexa Choose Template](https://www.arjunkrishna.us/img/2020/12/2020-12-21_11-45-57.jpg)
![Skill Builder Checklist](https://www.arjunkrishna.us/img/2020/12/2020-12-21_11-46-32.jpg)
![Skill Invocation](https://www.arjunkrishna.us/img/2020/12/2020-12-21_11-47-33.jpg)
![Create Intent](https://www.arjunkrishna.us/img/2020/12/2020-12-21_11-48-11.jpg)
![Utterances](https://www.arjunkrishna.us/img/2020/12/2020-12-21_11-48-43.jpg)
Default Region: https://a9afa2d4182f.ngrok.io/api/AlexaSkill/Request
![Endpoint](https://www.arjunkrishna.us/img/2020/12/2020-12-21_11-54-46.jpg)
NGrok url was changed as I had restarted the Ngrok. VendorId that is used in the redirect urls is picked from this page.
Web Authotization URI: https://a9afa2d4182f.ngrok.io/connect/authorize
Access Token URI: https://a9afa2d4182f.ngrok.io/connect/token
Client ID: AlexaBlazorNews
Your Secret: AlexaBlazorNewsSecret (this will be used in the code)
Your Authentication Scheme: HTTP Basic
![Account Linking](https://www.arjunkrishna.us/img/2020/12/2020-12-21_12-03-15.jpg)
User Secret Setup
VendorId setup
![User Secret Setup](https://www.arjunkrishna.us/img/2020/12/2020-12-21_12-50-42.jpg)
Blazor WASM
Run Server Solution File from Visual Studio, NGrok will automatically pick it up.
Testing
Request
![Test Development](https://www.arjunkrishna.us/img/2020/12/2020-12-21_12-59-07.jpg)
Acount Linking via Alexa App
Linking Screen in Alexa App on iOS
![Alexa App](https://www.arjunkrishna.us/img/2020/12/20201221_210439000_iOS.jpg)
Login Screen in the App
![Alexa App Login](https://www.arjunkrishna.us/img/2020/12/20201223_153742000_iOS.jpg)
Alexa Account Linking Successful
![Alexa Linking Successful](https://www.arjunkrishna.us/img/2020/12/2020-12-21_16-03-10.jpg)
Share this post
Twitter
Facebook
Reddit
LinkedIn
Email