Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/arjunkrishna/Blazor-WASM-Identity-gRPC-Alexa
Blazor WASM, IdentityServer4, Kestrel Web Server, Entity Framework Code First SQLite Database with Multiple Roles, Additional User Claims, gRPC with Roles Authorization, & Alexa Skill Integration
https://github.com/arjunkrishna/Blazor-WASM-Identity-gRPC-Alexa
alexa blazor claims efcore grpc grpc-web identityserver4 kestrel roles sqllite wasm webassembly
Last synced: 20 days ago
JSON representation
Blazor WASM, IdentityServer4, Kestrel Web Server, Entity Framework Code First SQLite Database with Multiple Roles, Additional User Claims, gRPC with Roles Authorization, & Alexa Skill Integration
- Host: GitHub
- URL: https://github.com/arjunkrishna/Blazor-WASM-Identity-gRPC-Alexa
- Owner: arjunkrishna
- License: mit
- Fork: true (JeepNL/Blazor-WASM-Identity-gRPC)
- Created: 2020-12-21T16:30:55.000Z (almost 4 years ago)
- Default Branch: Main
- Last Pushed: 2023-03-01T10:59:59.000Z (over 1 year ago)
- Last Synced: 2024-08-01T02:26:57.971Z (4 months ago)
- Topics: alexa, blazor, claims, efcore, grpc, grpc-web, identityserver4, kestrel, roles, sqllite, wasm, webassembly
- Language: C#
- Homepage:
- Size: 3.56 MB
- Stars: 12
- Watchers: 1
- Forks: 1
- Open Issues: 21
-
Metadata Files:
- Readme: README.md
- License: LICENSE
- Security: SECURITY.md
Awesome Lists containing this project
README
This repo has been forked from https://github.com/JeepNL/Blazor-WASM-Identity-gRPC and extended to accept alexa requests.
It uses Kestrel as the default webserver, a SQLite database and is "*CTRL-F5'able*" without any further configuration.
You can delete de SQLite database and migrations folder if you want and use the following commands in Visual Studio's Package Manager Console to re-create the db.
1. Add-Migration InitialCreate
2. Update-DatabaseAt first run the app will create 2 users (_if they don't exist, see: Server/[SeedData.cs](BlazorTemplate/Server/Data/SeedData.cs)_)
1. `[email protected]` / `Qwerty1234#`
2. `[email protected]` / `Qwerty1234#`and 2 roles:
1. Users
2. AdministratorsThe 'Administrators' & 'Users' roles will be assigned to: `[email protected]`
The 'Users' role will be assigned to: `[email protected]`
_Server/[ProfileService.cs](BlazorTemplate/Server/ProfileService.cs)_
```csharp
public ProfileService(UserManager userManager, IUserClaimsPrincipalFactory claimsFactory)
{
_userManager = userManager;
_claimsFactory = claimsFactory;
}public async Task GetProfileDataAsync(ProfileDataRequestContext context)
{
var sub = context.Subject.GetSubjectId();
var user = await _userManager.FindByIdAsync(sub);
var principal = await _claimsFactory.CreateAsync(user);
var claims = principal.Claims.ToList();
var nameClaim = context.Subject.FindAll(JwtClaimTypes.Name);
var roles = await _userManager.GetRolesAsync(user);
var roleClaims = roles.Select(role => new Claim(JwtClaimTypes.Role, role));
claims = claims.Where(claim => context.RequestedClaimTypes.Contains(claim.Type)).ToList();// Add custom claims in token here based on user properties or any other source
claims.Add(new Claim("username", user.UserName ?? string.Empty));
claims.AddRange(nameClaim);
claims.AddRange(roleClaims);
context.IssuedClaims = claims;
}
```# Alexa Integration
## Code Setup
_Server/Controllers/[AlexaSkillController.cs](BlazorTemplate/Server/Controllers/AlexaSkillController.cs)_```csharp
[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 pull requests
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;
}```
_Server/[Startup.cs](BlazorTemplate/Server/Startup.cs)_
changed made in StartUp.cs
```csharp
var alexaVendor = Configuration["Alexa:BlazorNews:VendorId"];
var alexaSecretText = "AlexaBlazorNewsSecret"; // I use this secret under the Alexa configuration.
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,
"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();
var configClients = Configuration.GetSection("IdentityServer:Clients")
.Get();clients.Add(client);
clients.AddRange(configClients);
services.AddIdentityServer()
.AddApiAuthorization(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();
services.AddControllersWithViews().AddNewtonsoftJson(); // newtonsoftjson is needed because alexa.net has not been migrated to Text.Json yet.
```## NGrok Setup
![Download Grok](img/2020-12-21_11-49-35.png)
![NGrok Token Setup](img/2020-12-21_13-55-45.png)
![NGROK command](img/2020-12-21_13-54-17.png)
![NGrok url for forwarding](img/2020-12-21_13-56-31.png)## Alexa Console Setup
![Alexa Create New Skill](img/2020-12-21_11-45-28.png)
![Alexa Choose Template](img/2020-12-21_11-45-57.png)
![Skill Builder Checklist](img/2020-12-21_11-46-32.png)
![Skill Invocation](img/2020-12-21_11-47-33.png)
![Create Intent](img/2020-12-21_11-48-11.png)
![Utterances](img/2020-12-21_11-48-43.png)```
Default Region: https://a9afa2d4182f.ngrok.io/api/AlexaSkill/Request
```![Endpoint](img/2020-12-21_11-54-46.png)
NGrok url was changed as I had restarted the Ngrok. VendorId is picked from here.
```
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: I changed it to Credentials in request body. Still did not work. Need to read more.
```
![Account Linking](img/2020-12-21_12-03-15.png)## User Secret Setup
VendorId setup
![User Secret Setup](img/2020-12-21_12-50-42.png)## Blazor WASM
Run Server Solution File from Visual Studio, NGrok will automatically pick it up.
## Testing
### Request
![Test Development](img/2020-12-21_12-59-07.png)### Acount Linking via Alexa App
Linking Screen in Alexa App on iOS
![Alexa App](img/20201221_210439000_iOS.png)
Login Screen in the App
![Alexa App Login](img/20201223_153742000_iOS.png)
Alexa Account Linking Successful
![Alexa Linking Successful](img/2020-12-21_16-03-10.png)