Skip to content

Commit 2f32155

Browse files
committed
Fixed issue where redirects triggered by AdditionalSignOutType were ignored. Updated IdentityServer sample
1 parent fd969e8 commit 2f32155

9 files changed

Lines changed: 177 additions & 43 deletions

File tree

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Security.Claims;
5+
using System.Threading.Tasks;
6+
using Hosts.Shared.InMemory;
7+
using IdentityManager2;
8+
using IdentityServer4;
9+
using IdentityServer4.Services;
10+
using Microsoft.AspNetCore.Authentication;
11+
using Microsoft.AspNetCore.Http;
12+
using Microsoft.AspNetCore.Mvc;
13+
14+
namespace Hosts.IdentityServerAuthentication
15+
{
16+
public class LoginController : Controller
17+
{
18+
private readonly ICollection<InMemoryUser> users;
19+
private readonly IIdentityServerInteractionService interactionService;
20+
21+
public LoginController(ICollection<InMemoryUser> users, IIdentityServerInteractionService interactionService)
22+
{
23+
this.users = users ?? throw new ArgumentNullException(nameof(users));
24+
this.interactionService = interactionService ?? throw new ArgumentNullException(nameof(interactionService));
25+
}
26+
27+
[HttpGet("login")]
28+
public IActionResult Login(string returnUrl)
29+
{
30+
return View(new LoginModel {ReturnUrl = returnUrl});
31+
}
32+
33+
[HttpPost("login")]
34+
[ValidateAntiForgeryToken]
35+
public async Task<IActionResult> Login(LoginModel model)
36+
{
37+
var user = users.FirstOrDefault(x => x.Username == model.Username && x.Password == model.Password);
38+
39+
if (user != null)
40+
{
41+
var claims = new List<Claim>
42+
{
43+
new Claim("sub", user.Subject),
44+
new Claim("name", user.Username)
45+
};
46+
47+
foreach (var role in user.Claims.Where(x => x.Type == IdentityManagerConstants.ClaimTypes.Role))
48+
{
49+
claims.Add(new Claim(IdentityManagerConstants.ClaimTypes.Role, role.Value));
50+
}
51+
52+
var identityServerUser = new IdentityServerUser(user.Subject) {AdditionalClaims = claims};
53+
await HttpContext.SignInAsync(identityServerUser);
54+
55+
if (Url.IsLocalUrl(model.ReturnUrl)) return LocalRedirect(model.ReturnUrl);
56+
return LocalRedirect("~/");
57+
}
58+
59+
ModelState.AddModelError("", "Invalid username or password");
60+
return View(model);
61+
}
62+
63+
[HttpGet("logout")]
64+
public async Task<IActionResult> Logout(string logoutId)
65+
{
66+
await HttpContext.SignOutAsync();
67+
68+
var logoutContext = await interactionService.GetLogoutContextAsync(logoutId);
69+
if (logoutContext.PostLogoutRedirectUri != null) return Redirect(logoutContext.PostLogoutRedirectUri);
70+
71+
return Redirect("/idm");
72+
}
73+
}
74+
75+
public class LoginModel
76+
{
77+
public string Username { get; set; }
78+
public string Password { get; set; }
79+
public string ReturnUrl { get; set; }
80+
}
81+
}

src/Hosts/Hosts.IdentityServerAuthentication/Hosts.IdentityServerAuthentication.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
</PropertyGroup>
66

77
<ItemGroup>
8-
<PackageReference Include="IdentityServer4" Version="3.1.0-preview.1.4" />
8+
<PackageReference Include="IdentityServer4" Version="3.1.0" />
99
</ItemGroup>
1010

1111
<ItemGroup>

src/Hosts/Hosts.IdentityServerAuthentication/Properties/launchSettings.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
"Host.IdentityServerAuthentication": {
44
"commandName": "Project",
55
"launchBrowser": true,
6-
"applicationUrl": "http://localhost:5000",
6+
"launchUrl": "https://localhost:5000/idm",
7+
"applicationUrl": "https://localhost:5000",
78
"environmentVariables": {
89
"ASPNETCORE_ENVIRONMENT": "Development"
910
}

src/Hosts/Hosts.IdentityServerAuthentication/Startup.cs

Lines changed: 29 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,12 @@
11
using System;
22
using System.Collections.Generic;
33
using System.IdentityModel.Tokens.Jwt;
4-
using System.Security.Claims;
5-
using System.Threading.Tasks;
4+
using System.Linq;
65
using Hosts.Shared.InMemory;
76
using IdentityManager2.Configuration;
8-
using IdentityServer4;
97
using IdentityServer4.Models;
108
using IdentityServer4.Test;
11-
using Microsoft.AspNetCore.Authentication;
12-
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
139
using Microsoft.AspNetCore.Builder;
14-
using Microsoft.AspNetCore.Routing;
1510
using Microsoft.Extensions.DependencyInjection;
1611

1712
namespace Hosts.IdentityServerAuthentication
@@ -26,32 +21,42 @@ public void ConfigureServices(IServiceCollection services)
2621
new SecurityConfiguration
2722
{
2823
HostAuthenticationType = "cookie",
29-
HostChallengeType = "oidc"
24+
HostChallengeType = "oidc",
25+
AdditionalSignOutType = "oidc"
3026
})
3127
.AddIdentityMangerService<InMemoryIdentityManagerService>();
3228

33-
var admin = new TestUser
34-
{
35-
SubjectId = "123",
36-
Username = "scott",
37-
Password = "scott",
38-
Claims = {new Claim("role", "IdentityManagerAdministrator")}
39-
};
40-
29+
var rand = new Random();
30+
var identityManagerUsers = Users.Get(rand.Next(5000, 20000));
31+
services.AddSingleton(x => identityManagerUsers);
32+
services.AddSingleton(x => Roles.Get(rand.Next(15)));
33+
4134
var client = new Client
4235
{
4336
ClientId = "identitymanager2",
4437
ClientName = "IdentityManager2",
4538
AllowedGrantTypes = GrantTypes.Implicit,
46-
RedirectUris = {"http://localhost:5000/idm/signin-oidc"},
39+
RedirectUris = {"https://localhost:5000/idm/signin-oidc"},
4740
AllowedScopes = {"openid", "profile", "roles"},
4841
RequireConsent = false
4942
};
5043

5144
var roles = new IdentityResource("roles", new List<string> {"role"});
5245

53-
services.AddIdentityServer()
54-
.AddTestUsers(new List<TestUser> {admin})
46+
var identityServerUsers = identityManagerUsers.Select(x => new TestUser
47+
{
48+
SubjectId = x.Subject,
49+
Username = x.Username,
50+
Password = x.Password,
51+
Claims = x.Claims
52+
}).ToList();
53+
54+
services.AddIdentityServer(options =>
55+
{
56+
options.UserInteraction.LoginUrl = "/login";
57+
options.UserInteraction.LogoutUrl = "/logout";
58+
})
59+
.AddTestUsers(identityServerUsers)
5560
.AddInMemoryIdentityResources(new List<IdentityResource> {new IdentityResources.OpenId(), new IdentityResources.Profile(), roles})
5661
.AddInMemoryApiResources(new List<ApiResource>())
5762
.AddInMemoryClients(new List<Client> {client})
@@ -63,7 +68,7 @@ public void ConfigureServices(IServiceCollection services)
6368
.AddCookie("cookie")
6469
.AddOpenIdConnect("oidc", opt =>
6570
{
66-
opt.Authority = "http://localhost:5000/auth";
71+
opt.Authority = "https://localhost:5000/auth";
6772
opt.ClientId = "identitymanager2";
6873

6974
// default: openid & profile
@@ -72,16 +77,7 @@ public void ConfigureServices(IServiceCollection services)
7277
opt.RequireHttpsMetadata = false; // dev only
7378
opt.SignInScheme = "cookie";
7479
opt.CallbackPath = "/signin-oidc";
75-
76-
opt.Events = new OpenIdConnectEvents
77-
{
78-
OnTokenValidated = context => Task.CompletedTask
79-
};
8080
});
81-
82-
var rand = new Random();
83-
services.AddSingleton(x => Users.Get(rand.Next(5000, 20000)));
84-
services.AddSingleton(x => Roles.Get(rand.Next(15)));
8581
}
8682

8783
public void Configure(IApplicationBuilder app)
@@ -90,16 +86,11 @@ public void Configure(IApplicationBuilder app)
9086

9187
app.Map("/auth", auth =>
9288
{
89+
auth.UseRouting();
90+
9391
auth.UseIdentityServer();
94-
95-
// Force authentication
96-
auth.Map("/account/login",
97-
login => login.Use(async (context, func) =>
98-
{
99-
await context.SignInAsync(IdentityServerConstants.DefaultCookieAuthenticationScheme,
100-
new ClaimsPrincipal(new ClaimsIdentity(new List<Claim> {new Claim("sub", "123")}, IdentityServerConstants.DefaultCookieAuthenticationScheme)));
101-
context.Response.Redirect(context.Request.Query["returnUrl"]);
102-
}));
92+
93+
auth.UseEndpoints(x => x.MapDefaultControllerRoute());
10394
});
10495

10596
app.Map("/idm", idm =>
@@ -111,10 +102,7 @@ await context.SignInAsync(IdentityServerConstants.DefaultCookieAuthenticationSch
111102

112103
idm.UseIdentityManager();
113104

114-
idm.UseEndpoints(x =>
115-
{
116-
x.MapDefaultControllerRoute();
117-
});
105+
idm.UseEndpoints(x => x.MapDefaultControllerRoute());
118106
});
119107

120108
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
@model Hosts.IdentityServerAuthentication.LoginModel
2+
3+
@{
4+
ViewBag.Title = "Login";
5+
}
6+
7+
<div class="row pt-3">
8+
<div class="col-12">
9+
<h1 class="h2">Login using Local Cookie <small class="text-muted">"cookie" scheme</small></h1>
10+
<div class="row">
11+
<div class="col-lg-8">
12+
<form asp-action="Login" method="post">
13+
<input asp-for="ReturnUrl" type="hidden"/>
14+
<div class="form-group">
15+
<label asp-for="Username"></label>
16+
<input asp-for="Username" class="form-control" required />
17+
</div>
18+
<div class="form-group">
19+
<label asp-for="Password"></label>
20+
<input asp-for="Password" class="form-control" required type="password" />
21+
</div>
22+
<button class="btn btn-primary" type="submit">Submit</button>
23+
</form>
24+
</div>
25+
<div class="col-lg-4">
26+
<div class="alert alert-info text-break">
27+
<p>
28+
Authorized IdentityManager users:
29+
</p>
30+
<ul>
31+
<li>alice:alice</li>
32+
<li>bob:bob</li>
33+
<li><em>any other user with the IdentityManagerAdministrator role</em></li>
34+
</ul>
35+
</div>
36+
</div>
37+
</div>
38+
</div>
39+
</div>
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta charset="utf-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
6+
<title>@ViewBag.Title</title>
7+
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css"
8+
integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
9+
</head>
10+
<body>
11+
<div class="container">
12+
@RenderBody()
13+
</div>
14+
<script src="https://code.jquery.com/jquery-3.4.1.slim.min.js" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script>
15+
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
16+
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>
17+
</body>
18+
</html>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
@{
2+
Layout = "_Layout";
3+
}

src/IdentityManager2/Api/Controllers/PageController.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,9 @@ public async Task<IActionResult> Logout()
7676

7777
await config.SecurityConfiguration.SignOut(HttpContext);
7878

79+
// if a signout scheme has started a redirect
80+
if (HttpContext.Response.StatusCode == 302) return StatusCode(302);
81+
7982
return RedirectToRoute(IdentityManagerConstants.RouteNames.Home, null);
8083
}
8184
}

0 commit comments

Comments
 (0)