< Summary

Information
Class: Api.Auth.Utils.LoginUtils.LoginResultDTO
Assembly: Api
File(s): /home/runner/work/ProjectRead.ing/ProjectRead.ing/Api/Auth/Utils/LoginUtils.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 1
Coverable lines: 1
Total lines: 76
Line coverage: 0%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%210%

File(s)

/home/runner/work/ProjectRead.ing/ProjectRead.ing/Api/Auth/Utils/LoginUtils.cs

#LineLine coverage
 1// SPDX-FileCopyrightText: 2026 Alper Çelik <[email protected]>
 2//
 3// SPDX-License-Identifier: AGPL-3.0-or-later
 4
 5using System.Buffers.Text;
 6
 7using Api.Auth.Models;
 8using Api.Database;
 9
 10using Geralt;
 11
 12using Microsoft.AspNetCore.Http.HttpResults;
 13using Microsoft.EntityFrameworkCore;
 14
 15using NodaTime;
 16
 17namespace Api.Auth.Utils;
 18
 19public static class LoginUtils
 20{
 21
 22    public const char PrefixSeparator = '_';
 23    public const string UserTokenPrefixName = "user";
 24    public const string TokenCookieName = "auth_token";
 25
 26    public static string UserTokenPrefix => UserTokenPrefixName + PrefixSeparator;
 27
 28
 29    public static async Task<string> CreateUserSession(Guid userId, string sessionName, PGContext ctx)
 30    {
 31
 32        var apiToken = new byte[32];
 33        SecureRandom.Fill(apiToken);
 34
 35        Span<byte> tokenHash = stackalloc byte[32];
 36        BLAKE2b.ComputeHash(tokenHash, apiToken);
 37
 38        var dbToken = new UserToken
 39        {
 40            UserId = userId,
 41            TokenHash = tokenHash.ToArray(),
 42            Permissions = UserPermissionBits.All,
 43            CreationTime = SystemClock.Instance.GetCurrentInstant(),
 44        };
 45
 46        await ctx.UserTokens.AddAsync(dbToken);
 47
 48        return UserTokenPrefix + Base64Url.EncodeToString(apiToken);
 49    }
 50
 51    public static Ok<LoginResultDTO> LogUserIn(HttpContext ctx, string token)
 52    {
 53        ctx.Response.Cookies.Append(TokenCookieName, token, new CookieOptions
 54        {
 55            IsEssential = true,
 56            SameSite = SameSiteMode.Strict,
 57            Secure = ctx.Request.Scheme == "https",
 58            HttpOnly = true,
 59        });
 60        return TypedResults.Ok(new LoginResultDTO(token));
 61    }
 62
 63    public static async Task UpdateLastUsedForUserToken(UserToken token, PGContext ctx)
 64    {
 65        var currentTime = SystemClock.Instance.GetCurrentInstant();
 66        if (token.LastUsed is null ||
 67                token.LastUsed + Duration.FromMinutes(1) <= currentTime)
 68        {
 69            await ctx.UserTokens.Where(ut => ut.TokenHash.SequenceEqual(token.TokenHash))
 70                .ExecuteUpdateAsync(setter =>
 71                        setter.SetProperty(ut => ut.LastUsed, currentTime));
 72        }
 73    }
 74
 075    public record LoginResultDTO(string AuthToken);
 76}

Methods/Properties

.ctor(string)