Chapter 6 - jayharris/workshop-oidc GitHub Wiki
# From ./
dotnet new mvc --name WebClient --output ./src/WebClient/ --auth None
dotnet sln add ./src/WebClient/WebClient.csproj
Do not do this for production. Because this is a workshop about Identity Server and not SSL configuration, we will disable HTTPS and leave that for another workshop.
WebClient\Startup.cs
within the Configure
method
//app.UseHttpsRedirection();
The default template sets the HTTPS and HTTP ports to 5001 and 5000, respectively, but these are already in use by the Identity Provider. Change them to 5021 and 5020.
WebClient/Properties/launchSettings.json
within the Profiles.WebClient
values settings.
"WebClient": {
"commandName": "Project",
"launchBrowser": true,
"applicationUrl": "https://localhost:5021;http://localhost:5020",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
Before proceeding on to Identity Server configuration, make sure the site works as expected.
# From: ./src/WebClient/
dotnet run
Load the site in a browser, accessing it at http://localhost:5020/. A successful request will return the site's Welcome page.
Stop execution after you have completed testing.
Add a client to IdentityProfier\IdentityConfiguration.cs
within the GetClients
method:
new Client
{
ClientId = "WebClient",
ClientName = "Identity Resource Web Client",
ClientSecrets =
{
new Secret("secretKey".Sha256())
},
RequireConsent = false,
// where to redirect to after login
RedirectUris = { "http://localhost:5020/signin-oidc" },
PostLogoutRedirectUris = { "https://localhost:5020/signout-callback-oidc" },
AllowedGrantTypes = GrantTypes.Implicit,
AllowedScopes = { "openid","profile" }
}
WebClient\Startup.cs
using System.IdentityModel.Tokens.Jwt;
using Microsoft.AspNetCore.Authentication.Cookies;
Remove existing JWT Mappings
WebClient/Startup.cs
append to ConfigureServices
method:
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
WebClient/Startup.cs
append to ConfigureServices
method:
services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = "oidc";
})
.AddCookie("Cookies")
.AddOpenIdConnect("oidc", options =>
{
//options.SignInScheme = "Cookies";
options.Authority = "http://localhost:5000";
options.RequireHttpsMetadata = false;
options.ClientId = "WebClient";
options.ClientSecret = "secretKey";
options.SaveTokens = true;
//options.Scope.Clear();
//foreach (var scope in openIdConfig.Scope.Split(' '))
//{
// options.Scope.Add(scope);
//}
});
Enable Authentication middleware.
WebClient/Startup.cs
within Configure
method, add app.UseAuthentication()
above the Static Files middleware configuration:
//app.UseHttpsRedirection();
app.UseAuthentication();
app.UseStaticFiles();
WebClient/Controllers/HomeController.cs
within the HomeController
class:
public IActionResult Claims()
{
return View();
}
Add WebClient/Views/Home/Claims.cshtml
@{
ViewData["Title"] = "Claims";
}
<h2>@ViewData["Title"]</h2>
<p>The current user's claims:</p>
@if (!User.Claims.Any())
{
<p><i>None</i></p>
}
<dl>
@foreach (var claim in User.Claims)
{
<dt>@claim.Type</dt>
<dd>@claim.Value</dd>
}
</dl>
Add a link to the navigation bar.
WebClient/Views/Shared/_Layout.cshtml
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">Home</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Claims">Claims</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
</li>
The Values controller was generated by default within the project template.
WebClient\Controllers\HomeController.cs
using Microsoft.AspNetCore.Authorization;
WebClient\Controllers\HomeController.cs
adding an attribute to the Claims
method.
[Authorize]
public IActionResult Claims()
# From ./src/IdentityProvider/
dotnet run
# From: ./src/WebClient/
dotnet run
Load the site in a browser, accessing its Claims
url at http://localhost:5020/Home/Claims. A successful request will redirect to the Provider for login, and display User claims after login.
Add a Logout Link to the Navigation bar.
WebClient/Views/Shared/_Layout.cshtml
@if (User.Identity.IsAuthenticated)
{
<ul class="nav navbar-nav">
<li class="nav-item">@Html.ActionLink("Log out", "Logout", "Home") @User.Identity.Name</li>
</ul>
}
<ul class="navbar-nav flex-grow-1">
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">Home</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="CLaims">Claims</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
</li>
</ul>
Add a Logout Action.
WebClient\Controllers\HomeController.cs
using Microsoft.AspNetCore.Authentication;
WebClient\Controller\HomeController.cs
public async Task Logout()
{
await HttpContext.SignOutAsync("Cookies");
await HttpContext.SignOutAsync("oidc");
}
# From ./src/IdentityProvider/
dotnet run
# From: ./src/WebClient/
dotnet run
Load the site in a browser, accessing its Claims
url at http://localhost:5020/Home/Claims. After accessing the page, logout. A successful request will redirect to the Logged Out page of the Provider. NOTE: You will still be logged in to the provider, as indicated by the username / logout link on the Provider. Upon returning to the home page of the WebClient at http://localhost:5020/, there is no logout link.
The issue is that the default UI for an AspNetCore template requires that logout be done through a POST request, which blocks OIDC logouts.
Generate AspNetCore Identity Logout files to modify functionality.
# From: ./src/IdentityProvider
dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design
dotnet aspnet-codegenerator identity --files Account.Logout --dbContext IdentityProvider.Data.ApplicationDbContext
Replace the Logout action.
IdentityProvider/Areas/Identity/Pages/Account/Logout.cshtml.cs
replace the OnGet
method.
public async Task<IActionResult> OnGet()
{
await _signInManager.SignOutAsync();
ViewData["IsLoggedOut"] = true;
_logger.LogInformation("User logged out.");
return Page();
}
Modify _LoginPartial
IdentityProvider\Views\Shared\_LoginPartial.cshtml
@if (!(true.Equals(ViewData["IsLoggedOut"])) && SignInManager.IsSignedIn(User))
# From ./src/IdentityProvider/
dotnet run
# From: ./src/WebClient/
dotnet run
Load the site in a browser, accessing its Claims
url at http://localhost:5020/Home/Claims. After accessing the page, logout. A successful request will redirect to the unauthenticated Logged Out page of the Provider.