public async Task UpdatePresenceAsync( Session session, CancellationToken cancellationToken = default) { var sessionInfo = await GetSessionInfoAsync(session, cancellationToken).ConfigureAwait(false); var now = Clock.Now.ToDateTime(); var delta = now - sessionInfo.LastSeenAt; if (delta < MinUpdatePresencePeriod) { return; // We don't want to update this too frequently } var command = new SetupSessionCommand(session).MarkServerSide(); await SetupSessionAsync(command, cancellationToken).ConfigureAwait(false); }
// [CommandHandler] inherited public virtual Task <SessionInfo> SetupSession( SetupSessionCommand command, CancellationToken cancellationToken = default) { var(session, ipAddress, userAgent) = command; var context = CommandContext.GetCurrent(); if (Computed.IsInvalidating()) { _ = GetSessionInfo(session, default); var invIsNew = context.Operation().Items.GetOrDefault(false); if (invIsNew) { _ = GetAuthInfo(session, default); _ = GetOptions(session, default); } var invSessionInfo = context.Operation().Items.Get <SessionInfo>(); if (invSessionInfo is { IsAuthenticated : true })
public virtual async Task UpdateAuthState(HttpContext httpContext, CancellationToken cancellationToken = default) { var httpUser = httpContext.User; var httpAuthenticationSchema = httpUser.Identity?.AuthenticationType ?? ""; var isAuthenticated = !string.IsNullOrEmpty(httpAuthenticationSchema); var session = SessionResolver.Session; var ipAddress = httpContext.Connection.RemoteIpAddress?.ToString() ?? ""; var userAgent = httpContext.Request.Headers.TryGetValue("User-Agent", out var userAgentValues) ? userAgentValues.FirstOrDefault() ?? "" : ""; var setupSessionCommand = new SetupSessionCommand(session, ipAddress, userAgent).MarkServerSide(); var sessionInfo = await AuthService.SetupSession(setupSessionCommand, cancellationToken).ConfigureAwait(false); var userId = sessionInfo.UserId; var userIsAuthenticated = sessionInfo.IsAuthenticated && !sessionInfo.IsSignOutForced; var user = userIsAuthenticated ? (await AuthService.TryGetUser(userId, cancellationToken).ConfigureAwait(false) ?? throw new KeyNotFoundException()) : new User(session.Id); // Guest try { if (isAuthenticated) { if (userIsAuthenticated && IsSameUser(user, httpUser, httpAuthenticationSchema)) { return; } var(newUser, authenticatedIdentity) = CreateOrUpdateUser(user, httpUser, httpAuthenticationSchema); var signInCommand = new SignInCommand(session, newUser, authenticatedIdentity).MarkServerSide(); await AuthService.SignIn(signInCommand, cancellationToken).ConfigureAwait(false); } else { if (userIsAuthenticated) { var signOutCommand = new SignOutCommand(session); await AuthService.SignOut(signOutCommand, cancellationToken).ConfigureAwait(false); } } } finally { // Ideally this should be done once important things are completed Task.Run(() => AuthService.UpdatePresence(session, default), default).Ignore();
public virtual async Task <SessionInfo> SetupSessionAsync( SetupSessionCommand command, CancellationToken cancellationToken = default) { var(session, ipAddress, userAgent) = command; var context = CommandContext.GetCurrent(); if (Computed.IsInvalidating()) { GetSessionInfoAsync(session, default).Ignore(); var invSessionInfo = context.Operation().Items.Get <SessionInfo>(); if (invSessionInfo.IsAuthenticated) { GetUserSessionsAsync(invSessionInfo.UserId, default).Ignore(); } return(null !); } await using var dbContext = await CreateCommandDbContextAsync(cancellationToken).ConfigureAwait(false); var dbSessionInfo = await Sessions.FindAsync(dbContext, session.Id, cancellationToken).ConfigureAwait(false); var now = Clock.Now; var oldSessionInfo = dbSessionInfo?.ToModel() ?? new SessionInfo(session.Id, now); var newSessionInfo = oldSessionInfo with { LastSeenAt = now, IPAddress = string.IsNullOrEmpty(ipAddress) ? oldSessionInfo.IPAddress : ipAddress, UserAgent = string.IsNullOrEmpty(userAgent) ? oldSessionInfo.UserAgent : userAgent, }; dbSessionInfo = await Sessions.CreateOrUpdateAsync(dbContext, newSessionInfo, cancellationToken).ConfigureAwait(false); var sessionInfo = dbSessionInfo.ToModel(); context.Operation().Items.Set(sessionInfo); return(sessionInfo); }
public async Task LongFlowTest() { var authServer = Services.GetRequiredService <IServerSideAuthService>(); var sessionFactory = ClientServices.GetRequiredService <ISessionFactory>(); var sessionA = sessionFactory.CreateSession(); var sessionB = sessionFactory.CreateSession(); var sessions = await authServer.GetUserSessions(sessionA); sessions.Length.Should().Be(0); sessions = await authServer.GetUserSessions(sessionB); sessions.Length.Should().Be(0); var bob = new User("", "Bob").WithIdentity("g:1"); var signInCmd = new SignInCommand(sessionA, bob).MarkServerSide(); await authServer.SignIn(signInCmd); var user = await authServer.GetUser(sessionA); user.Name.Should().Be(bob.Name); bob = await authServer.TryGetUser(user.Id) ?? throw new NullReferenceException(); sessions = await authServer.GetUserSessions(sessionA); sessions.Select(s => s.Id).Should().BeEquivalentTo(new[] { sessionA.Id }); sessions = await authServer.GetUserSessions(sessionB); sessions.Length.Should().Be(0); signInCmd = new SignInCommand(sessionB, bob).MarkServerSide(); await authServer.SignIn(signInCmd); user = await authServer.GetUser(sessionB); user.Name.Should().Be(bob.Name); sessions = await authServer.GetUserSessions(sessionA); sessions.Select(s => s.Id).Should().BeEquivalentTo(new[] { sessionA.Id, sessionB.Id }); sessions = await authServer.GetUserSessions(sessionB); sessions.Select(s => s.Id).Should().BeEquivalentTo(new[] { sessionA.Id, sessionB.Id }); var signOutCmd = new SignOutCommand(sessionA); await authServer.SignOut(signOutCmd); (await authServer.IsSignOutForced(sessionB)).Should().BeFalse(); user = await authServer.GetUser(sessionA); user.IsAuthenticated.Should().BeFalse(); sessions = await authServer.GetUserSessions(sessionA); sessions.Length.Should().Be(0); sessions = await authServer.GetUserSessions(sessionB); sessions.Select(s => s.Id).Should().BeEquivalentTo(new[] { sessionB.Id }); signInCmd = new SignInCommand(sessionA, bob).MarkServerSide(); await authServer.SignIn(signInCmd); user = await authServer.GetUser(sessionA); user.Name.Should().Be(bob.Name); sessions = await authServer.GetUserSessions(sessionA); sessions.Select(s => s.Id).Should().BeEquivalentTo(new[] { sessionA.Id, sessionB.Id }); sessions = await authServer.GetUserSessions(sessionB); sessions.Select(s => s.Id).Should().BeEquivalentTo(new[] { sessionA.Id, sessionB.Id }); signOutCmd = new SignOutCommand(sessionB, true); await authServer.SignOut(signOutCmd); (await authServer.IsSignOutForced(sessionB)).Should().BeTrue(); (await authServer.GetSessionInfo(sessionB)).IsSignOutForced.Should().BeTrue(); user = await authServer.GetUser(sessionB); user.IsAuthenticated.Should().BeFalse(); sessions = await authServer.GetUserSessions(sessionA); sessions.Select(s => s.Id).Should().BeEquivalentTo(new[] { sessionA.Id }); sessions = await authServer.GetUserSessions(sessionB); sessions.Length.Should().Be(0); await Assert.ThrowsAsync <SecurityException>(async() => { var sessionInfo = await authServer.GetSessionInfo(sessionB); var setupSessionCmd = new SetupSessionCommand(sessionB).MarkServerSide(); await authServer.SetupSession(setupSessionCmd); }); await Assert.ThrowsAsync <SecurityException>(async() => { signInCmd = new SignInCommand(sessionB, bob).MarkServerSide(); await authServer.SignIn(signInCmd); }); }
// [CommandHandler] inherited public override async Task <SessionInfo> SetupSession( SetupSessionCommand command, CancellationToken cancellationToken = default) { var(session, ipAddress, userAgent) = command; var context = CommandContext.GetCurrent(); if (Computed.IsInvalidating()) { var invSessionInfo = context.Operation().Items.Get <SessionInfo>(); if (invSessionInfo == null) { return(null !); } _ = GetSessionInfo(session, default); var invIsNew = context.Operation().Items.GetOrDefault(false); if (invIsNew) { _ = GetAuthInfo(session, default); _ = GetOptions(session, default); } if (invSessionInfo.IsAuthenticated) { _ = GetUserSessions(invSessionInfo.UserId, default); } return(null !); } var dbContext = await CreateCommandDbContext(cancellationToken).ConfigureAwait(false); await using var _1 = dbContext.ConfigureAwait(false); var dbSessionInfo = await Sessions.Get(dbContext, session.Id, true, cancellationToken).ConfigureAwait(false); var isNew = dbSessionInfo == null; var now = Clocks.SystemClock.Now; var sessionInfo = SessionConverter.ToModel(dbSessionInfo) ?? SessionConverter.NewModel() with { Id = session.Id }; sessionInfo = sessionInfo with { LastSeenAt = now, IPAddress = string.IsNullOrEmpty(ipAddress) ? sessionInfo.IPAddress : ipAddress, UserAgent = string.IsNullOrEmpty(userAgent) ? sessionInfo.UserAgent : userAgent, }; try { dbSessionInfo = await Sessions.Upsert(dbContext, sessionInfo, cancellationToken).ConfigureAwait(false); sessionInfo = SessionConverter.ToModel(dbSessionInfo); context.Operation().Items.Set(sessionInfo); // invSessionInfo context.Operation().Items.Set(isNew); // invIsNew return(sessionInfo !); } catch (DbUpdateException) { var scope = context.Items.Get <DbOperationScope <TDbContext> >(); await scope !.Rollback().ConfigureAwait(false); var readDbContext = CreateDbContext(); await using var __ = readDbContext.ConfigureAwait(false); dbSessionInfo = await Sessions.Get(readDbContext, session.Id, false, cancellationToken).ConfigureAwait(false); if (dbSessionInfo == null) { throw; // Something is off: it is supposed to be created concurrently } sessionInfo = SessionConverter.ToModel(dbSessionInfo); return(sessionInfo !); } }
public abstract Task <SessionInfo> SetupSession(SetupSessionCommand command, CancellationToken cancellationToken = default);