< Summary

Information
Class: Api.Auth.Utils.AuthUtils.LoginResultDTO
Assembly: Api
File(s): /home/runner/work/ProjectRead.ing/ProjectRead.ing/Api/Auth/Utils/AuthUtils.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 1
Coverable lines: 1
Total lines: 118
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/AuthUtils.cs

#LineLine coverage
 1// SPDX-FileCopyrightText: 2026 Alper Çelik <alper@alper-celik.dev>
 2//
 3// SPDX-License-Identifier: AGPL-3.0-or-later
 4
 5using System.Buffers.Text;
 6using Api.Auth.Models;
 7using Api.Database;
 8using Api.Database.Utils;
 9using Geralt;
 10using LinqKit;
 11using Microsoft.AspNetCore.Http.HttpResults;
 12using Microsoft.EntityFrameworkCore;
 13using NodaTime;
 14
 15namespace Api.Auth.Utils;
 16
 17public static class AuthUtils
 18{
 19    public const char PrefixSeparator = '_';
 20
 21    public const string UserTokenPrefixName = "user";
 22    public const string RemoteServiceTokenPrefixName = "service";
 23    public static List<string> TokenPrefixesNames =>
 24        [UserTokenPrefixName, RemoteServiceTokenPrefixName];
 25    public const string TokenCookieName = "auth_token";
 26
 27    public static string UserTokenPrefix => UserTokenPrefixName + PrefixSeparator;
 28    public static string RemoteServiceTokenPrefix =>
 29        RemoteServiceTokenPrefixName + PrefixSeparator;
 30
 31    // see https://www.rfc-editor.org/rfc/rfc9106.html#name-recommendations
 32    public const int ARGON2ID_ITER = 3;
 33    public const int ARGON2ID_MEM_BYTES = 64 * 1024 * 1024;
 34
 35    public static async Task<string> CreateRemoteServiceSession(
 36        Guid serviceId,
 37        PGContext db
 38    )
 39    {
 40        byte[] apiToken = new byte[32];
 41        SecureRandom.Fill(apiToken);
 42
 43        byte[] tokenHash = new byte[32];
 44        BLAKE2b.ComputeHash(tokenHash, apiToken);
 45
 46        var serviceToken = new RemoteServiceToken
 47        {
 48            RemoteServiceId = serviceId,
 49            TokenHash = tokenHash,
 50            CreationTime = Now(),
 51        };
 52
 53        await db.RemoteServiceTokens.AddAsync(serviceToken);
 54
 55        return RemoteServiceTokenPrefix + Base64Url.EncodeToString(apiToken);
 56    }
 57
 58    public static async Task<string> CreateUserSession(
 59        Guid userId,
 60        string sessionName,
 61        PGContext ctx
 62    )
 63    {
 64        var apiToken = new byte[32];
 65        SecureRandom.Fill(apiToken);
 66
 67        byte[] tokenHash = new byte[32];
 68        BLAKE2b.ComputeHash(tokenHash, apiToken);
 69
 70        var dbToken = new UserTokenEF
 71        {
 72            UserId = userId,
 73            SessionName = sessionName,
 74            TokenHash = tokenHash,
 75            Permissions = UserPermissionBits.All,
 76            CreationTime = Now(),
 77        };
 78
 79        await ctx.UserTokens.AddAsync(dbToken);
 80
 81        return UserTokenPrefix + Base64Url.EncodeToString(apiToken);
 82    }
 83
 84    public static async Task<bool> CanAdminRegister(PGContext db) =>
 85        !await db.Users.AnyAsync(u => u.Admin == true);
 86
 87    public static Ok<LoginResultDTO> LogUserIn(HttpContext ctx, string token)
 88    {
 89        ctx.Response.Cookies.Append(
 90            TokenCookieName,
 91            token,
 92            new CookieOptions
 93            {
 94                IsEssential = true,
 95                SameSite = SameSiteMode.Strict,
 96                Secure = ctx.Request.Scheme == "https",
 97                HttpOnly = true,
 98            }
 99        );
 100        return TypedResults.Ok(new LoginResultDTO(token));
 101    }
 102
 103    public static T UpdateLastUsedTokens<T>(T token)
 104        where T : class, ITokenTime
 105    {
 106        var currentTime = SystemClock.Instance.GetCurrentInstant();
 107        if (
 108            token.LastUsed is null
 109            || token.LastUsed + Duration.FromMinutes(10) <= currentTime
 110        )
 111        {
 112            token.LastUsed = currentTime;
 113        }
 114        return token;
 115    }
 116
 0117    public record LoginResultDTO(string AuthToken);
 118}

Methods/Properties

.ctor(string)