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);
        }
Exemple #2
0
    // [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);
            });
        }
Exemple #6
0
    // [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 !);
        }
    }
Exemple #7
0
 public abstract Task <SessionInfo> SetupSession(SetupSessionCommand command, CancellationToken cancellationToken = default);