Skip to content

Commit

Permalink
Ticket #624 : Can end multiple sessions
Browse files Browse the repository at this point in the history
  • Loading branch information
thabart committed Nov 24, 2023
1 parent 3781898 commit 88a63c5
Show file tree
Hide file tree
Showing 34 changed files with 10,280 additions and 90 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -214,4 +214,5 @@ wwwroot/

# Ignore docfx

docs/_site/
docs/_site/
/src/IdServer/SimpleIdServer.IdServer.MySQLMigrations/Migrations/StoreDbContextModelSnapshot.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.IdentityModel.JsonWebTokens;

namespace Website.Controllers
{
public class BackChannelLogoutController : Controller
{
private readonly IDistributedCache _distributedCache;

public BackChannelLogoutController(IDistributedCache distributedCache)
{
_distributedCache = distributedCache;
}

[HttpPost]
public async Task<IActionResult> Logout([FromForm] BackChannelLogoutRequest request)
{
if (request == null ||string.IsNullOrWhiteSpace(request.LogoutToken)) return BadRequest();
var logoutToken = request.LogoutToken;
var handler = new JsonWebTokenHandler();
var jwt = handler.ReadJsonWebToken(request.LogoutToken);
var subject = jwt.Claims.First(c => c.Type == System.IdentityModel.Tokens.Jwt.JwtRegisteredClaimNames.Sub).Value;
var sessionId = jwt.Claims.First(c => c.Type == System.IdentityModel.Tokens.Jwt.JwtRegisteredClaimNames.Sid).Value;
await _distributedCache.SetStringAsync($"{subject}_{sessionId}", "disconnected");
return Ok();
}
}

public class BackChannelLogoutRequest
{
[FromForm(Name = "logout_token")]
public string LogoutToken { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.Extensions.Caching.Distributed;
using System.IdentityModel.Tokens.Jwt;

namespace Website
{
public class CustomCookieEventHandler : CookieAuthenticationEvents
{
private readonly IDistributedCache _distributedCache;

public CustomCookieEventHandler(IDistributedCache distributedCache)
{
_distributedCache = distributedCache;
}

public override async Task ValidatePrincipal(CookieValidatePrincipalContext context)
{
if (context.Principal.Identity.IsAuthenticated)
{
var subject = context.Principal.FindFirst(JwtRegisteredClaimNames.Sub)?.Value;
var sessionId = context.Principal.FindFirst(JwtRegisteredClaimNames.Sid)?.Value;
var str = await _distributedCache.GetStringAsync($"{subject}_{sessionId}");
if(!string.IsNullOrWhiteSpace(str))
{
context.RejectPrincipal();
await context.HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
}
}
}
}
}
9 changes: 6 additions & 3 deletions samples/ProtectWebsiteServerside/src/Website/Program.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder;
using static System.Net.WebRequestMethods;
using Website;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddControllersWithViews();
builder.Services.AddTransient<CustomCookieEventHandler>();
builder.Services.AddAuthentication(options =>
{
options.DefaultScheme = "Cookies";
options.DefaultChallengeScheme = "sid";
})
.AddCookie("Cookies")
.AddCookie("Cookies", options =>
{
options.EventsType = typeof(CustomCookieEventHandler);
})
.AddOpenIdConnect("sid", options =>
{
options.SignInScheme = "Cookies";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ public class UserSessionNames
public const string ExpirationDateTime = "exp_datetime";
public const string State = "state";
public const string Realm = "realm";
public const string ClientIds = "client_ids";
}
42 changes: 39 additions & 3 deletions src/IdServer/SimpleIdServer.IdServer.Domains/User.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

namespace SimpleIdServer.IdServer.Domains
{
public class User : IEquatable<User>
public class User : IEquatable<User>, ICloneable
{
private static Dictionary<string, KeyValuePair<Action<User, string>, Func<User, object>>> _userClaims = new Dictionary<string, KeyValuePair<Action<User, string>, Func<User, object>>>
{
Expand Down Expand Up @@ -163,12 +163,15 @@ public void RejectConsent(string consentId)
Consents.Remove(consent);
}

public bool AddSession(string realm, DateTime expirationDateTime)
public bool AddSession(string realm, string clientId, DateTime expirationDateTime)
{
foreach (var session in Sessions.Where(s => s.Realm == realm))
session.State = UserSessionStates.Rejected;

Sessions.Add(new UserSession { SessionId = Guid.NewGuid().ToString(), AuthenticationDateTime = DateTime.UtcNow, ExpirationDateTime = expirationDateTime, State = UserSessionStates.Active, Realm = realm });
var clientIds = new List<string>();
if (!string.IsNullOrWhiteSpace(clientId))
clientIds.Add(clientId);
Sessions.Add(new UserSession { SessionId = Guid.NewGuid().ToString(), AuthenticationDateTime = DateTime.UtcNow, ExpirationDateTime = expirationDateTime, State = UserSessionStates.Active, Realm = realm, ClientIds = clientIds });
return true;
}

Expand Down Expand Up @@ -292,5 +295,38 @@ public override int GetHashCode()
{
return Id.GetHashCode();
}

public object Clone()
{
return new User
{
Name = Name,
Email = Email,
EmailVerified = EmailVerified,
Firstname = Firstname,
Lastname = Lastname,
CreateDateTime = CreateDateTime,
UpdateDateTime= UpdateDateTime,
Source = Source,
Id = Id,
OAuthUserClaims = OAuthUserClaims.Select(c => new UserClaim
{
Id = c.Id,
Name = c.Name,
Type = c.Type,
Value = c.Value
}).ToList(),
Groups = Groups.Select(g => new Group
{
CreateDateTime = g.CreateDateTime,
Name = g.Name,
FullPath = g.FullPath,
ParentGroupId = g.ParentGroupId,
UpdateDateTime = g.UpdateDateTime,
Description = g.Description,
Id = g.Id
}).ToList()
};
}
}
}
20 changes: 20 additions & 0 deletions src/IdServer/SimpleIdServer.IdServer.Domains/UserSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,26 @@ public class UserSession : ICloneable
public UserSessionStates State { get; set; }
[JsonPropertyName(UserSessionNames.Realm)]
public string Realm { get; set; }
[JsonPropertyName(UserSessionNames.ClientIds)]
public List<string> ClientIds
{
get
{
return SerializedClientIds.Split(",").ToList();
}
set
{
if(value == null)
{
SerializedClientIds = string.Empty;
return;
}

SerializedClientIds = string.Join(",", value);
}
}
[JsonIgnore]
public string SerializedClientIds { get; set; } = string.Empty;
[JsonIgnore]
public User User { get; set; }

Expand Down
Loading

0 comments on commit 88a63c5

Please sign in to comment.