< Summary

Line coverage
100%
Covered lines: 125
Uncovered lines: 0
Coverable lines: 125
Total lines: 226
Line coverage: 100%
Branch coverage
100%
Covered branches: 6
Total branches: 6
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

/home/runner/work/ProjectRead.ing/ProjectRead.ing/Api/Auth/Mutations/LoginInstanceService.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;
 6using System.Security.Cryptography;
 7using System.Text;
 8using System.Text.Unicode;
 9using Api.Auth.Models;
 10using Api.Auth.Utils;
 11using Api.Database;
 12using Api.Database.Utils;
 13using Api.Utils;
 14using FluentValidation;
 15using HotChocolate.Authorization;
 16using NodaTime;
 17
 18namespace Api.Auth.Mutations;
 19
 20/// <param name="CurrentSecond">Unix timestamp in seconds (GraphQL Long).</param>
 21public record LoginInstanceServiceInput(
 22    string AuthProof,
 23    string Salt,
 24    long CurrentSecond
 25);
 26
 27public record LoginInstanceServicePayload(string Token);
 28
 29[MutationType]
 30public static partial class LoginInstanceServiceMutations
 31{
 32    [AllowAnonymous]
 33    public static async Task<LoginInstanceServicePayload> LoginTheInstanceService(
 34        [Service] PGContext db,
 35        [Service] IEFTransactionDIAccessorService txAccessor,
 36        [Service] IConfiguration config,
 37        [Service] IValidator<LoginInstanceServiceInput> validator,
 38        LoginInstanceServiceInput input,
 39        CancellationToken ct
 40    )
 141    {
 142        await validator.ValidateOrThrowAsync(input, ct);
 43
 144        var proof = HKDF.DeriveKey(
 145            HashAlgorithmName.SHA256,
 146            Base64Url.DecodeFromChars(config["ProjectReadingSecretSeed"]),
 147            720 / 8,
 148            Base64Url.DecodeFromChars(input.Salt),
 149            Encoding.UTF8.GetBytes("The Instance Service Auth : " + input.CurrentSecond)
 150        );
 51
 152        if (Geralt.ConstantTime.Equals(proof, Base64Url.DecodeFromChars(input.AuthProof)))
 153        {
 154            string token = await AuthUtils.CreateRemoteServiceSession(
 155                RemoteService.TheInstanceServiceId,
 156                db
 157            );
 158            await db.SaveChangesAsync(ct);
 59
 160            await txAccessor.CommitTX(ct);
 161            return new(token);
 62        }
 63
 164        throw new GraphQLException(
 165            ErrorBuilder
 166                .New()
 167                .SetMessage("Invalid Credentials")
 168                .SetCode(ErrorCodes.INVALID_CREDS)
 169                .Build()
 170        );
 171    }
 72}
 73
 74public class LoginInstanceServiceInputValidator
 75    : AbstractValidator<LoginInstanceServiceInput>
 76{
 77    public LoginInstanceServiceInputValidator()
 78    {
 79        RuleFor(i => i.AuthProof).Must(str => Base64Url.IsValid(str)).Length(120);
 80
 81        RuleFor(i => i.Salt).Must(str => Base64Url.IsValid(str)).MinimumLength(22);
 82
 83        RuleFor(i => i.CurrentSecond)
 84            .LessThan((_) => Now().Plus(Duration.FromMinutes(5)).ToUnixTimeSeconds())
 85            .GreaterThan((_) => Now().Minus(Duration.FromMinutes(5)).ToUnixTimeSeconds());
 86    }
 87}

/home/runner/work/ProjectRead.ing/ProjectRead.ing/Api/obj/Debug/net10.0/generated/HotChocolate.Types.Analyzers/HotChocolate.Types.Analyzers.GraphQLServerGenerator/LoginInstanceServiceMutations.hs33rHgC5AHky1fX2QOBKQ.hc.g.cs

#LineLine coverage
 1// <auto-generated/>
 2
 3#nullable enable
 4#pragma warning disable
 5
 6using System;
 7using System.Runtime.CompilerServices;
 8using HotChocolate;
 9using HotChocolate.Types;
 10using HotChocolate.Execution.Configuration;
 11using Microsoft.Extensions.DependencyInjection;
 12using HotChocolate.Internal;
 13
 14namespace Api.Auth.Mutations
 15{
 16    public static partial class LoginInstanceServiceMutations
 17    {
 18        internal static void Initialize(global::HotChocolate.Types.IObjectTypeDescriptor descriptor)
 119        {
 120            var extension = descriptor.Extend();
 121            var configuration = extension.Configuration;
 122            var thisType = typeof(global::Api.Auth.Mutations.LoginInstanceServiceMutations);
 123            var bindingResolver = extension.Context.ParameterBindingResolver;
 124            var resolvers = new __Resolvers(bindingResolver);
 25
 126            HotChocolate.Internal.ConfigurationHelper.ApplyConfiguration(
 127                extension.Context,
 128                descriptor,
 129                typeof(global::Api.Auth.Mutations.LoginInstanceServiceMutations),
 130                new global::HotChocolate.Types.MutationTypeAttribute());
 131            configuration.ConfigurationsAreApplied = true;
 32
 133            var naming = descriptor.Extend().Context.Naming;
 34
 135            descriptor
 136                .Field(naming.GetMemberName("LoginTheInstanceService", global::HotChocolate.Types.MemberKind.ObjectField
 137                .ExtendWith(static (field, context) =>
 138                {
 139                    var configuration = field.Configuration;
 140                    var typeInspector = field.Context.TypeInspector;
 141                    var bindingResolver = field.Context.ParameterBindingResolver;
 142                    var naming = field.Context.Naming;
 143
 144                    configuration.Type = global::HotChocolate.Types.Descriptors.TypeReference.Create(
 145                        typeInspector.GetTypeRef(typeof(global::Api.Auth.Mutations.LoginInstanceServicePayload), HotChoc
 146                        new global::HotChocolate.Language.NonNullTypeNode(new global::HotChocolate.Language.NamedTypeNod
 147                    configuration.ResultType = typeof(global::Api.Auth.Mutations.LoginInstanceServicePayload);
 148                    configuration.DeclaringType = context.ThisType;
 149
 150                    configuration.SetSourceGeneratorFlags();
 151
 152                    var bindingInfo = field.Context.ParameterBindingResolver;
 153                    var parameter = context.Resolvers.CreateParameterDescriptor_LoginTheInstanceService_input();
 154                    var parameterInfo = bindingInfo.GetBindingInfo(parameter);
 155
 156                    if(parameterInfo.Kind is global::HotChocolate.Internal.ArgumentKind.Argument)
 157                    {
 158                        var argumentConfiguration = new global::HotChocolate.Types.Descriptors.Configurations.ArgumentCo
 159                        {
 160                            Name = naming.GetMemberName("input", global::HotChocolate.Types.MemberKind.Argument),
 161                            Type = global::HotChocolate.Types.Descriptors.TypeReference.Create(
 162                                typeInspector.GetTypeRef(typeof(global::Api.Auth.Mutations.LoginInstanceServiceInput), H
 163                                new global::HotChocolate.Language.NonNullTypeNode(new global::HotChocolate.Language.Name
 164                            RuntimeType = typeof(global::Api.Auth.Mutations.LoginInstanceServiceInput)
 165                        };
 166
 167                        configuration.Arguments.Add(argumentConfiguration);
 168                    }
 169
 170                    configuration.Member = context.ThisType.GetMethod(
 171                        "LoginTheInstanceService",
 172                        global::HotChocolate.Utilities.ReflectionUtils.StaticMemberFlags,
 173                        new global::System.Type[]
 174                        {
 175                            typeof(global::Api.Database.PGContext),
 176                            typeof(global::Api.Database.Utils.IEFTransactionDIAccessorService),
 177                            typeof(global::Microsoft.Extensions.Configuration.IConfiguration),
 178                            typeof(global::FluentValidation.IValidator<global::Api.Auth.Mutations.LoginInstanceServiceIn
 179                            typeof(global::Api.Auth.Mutations.LoginInstanceServiceInput),
 180                            typeof(global::System.Threading.CancellationToken)
 181                        })!;
 182
 183                    var fieldDescriptor = global::HotChocolate.Types.Descriptors.ObjectFieldDescriptor.From(field.Contex
 184                    HotChocolate.Internal.ConfigurationHelper.ApplyConfiguration(
 185                        field.Context,
 186                        fieldDescriptor,
 187                        configuration.Member,
 188                        new global::HotChocolate.Authorization.AllowAnonymousAttribute());
 189
 190                    bindingResolver.ApplyConfiguration(
 191                        context.Resolvers.CreateParameterDescriptor_LoginTheInstanceService_input(),
 192                        fieldDescriptor);
 193
 194                    configuration.ConfigurationsAreApplied = true;
 195                    fieldDescriptor.CreateConfiguration();
 196
 197                    configuration.Resolvers = context.Resolvers.LoginTheInstanceService();
 198                },
 199                (Resolvers: resolvers, ThisType: thisType));
 100
 101            Configure(descriptor);
 1102        }
 103
 104        static partial void Configure(global::HotChocolate.Types.IObjectTypeDescriptor descriptor);
 105
 106        private sealed class __Resolvers
 107        {
 108            private readonly global::HotChocolate.Internal.IParameterBinding _binding_LoginTheInstanceService_input;
 109
 1110            public __Resolvers(global::HotChocolate.Resolvers.ParameterBindingResolver bindingResolver)
 1111            {
 1112                _binding_LoginTheInstanceService_input = bindingResolver.GetBinding(CreateParameterDescriptor_LoginTheIn
 1113            }
 114
 115            public global::HotChocolate.Internal.ParameterDescriptor CreateParameterDescriptor_LoginTheInstanceService_i
 1116                => new HotChocolate.Internal.ParameterDescriptor(
 1117                    "input",
 1118                    typeof(global::Api.Auth.Mutations.LoginInstanceServiceInput),
 1119                    isNullable: false,
 1120                    []);
 121
 122            public HotChocolate.Resolvers.FieldResolverDelegates LoginTheInstanceService()
 1123                => new global::HotChocolate.Resolvers.FieldResolverDelegates(resolver: LoginTheInstanceService);
 124
 125            private async global::System.Threading.Tasks.ValueTask<global::System.Object?> LoginTheInstanceService(globa
 1126            {
 1127                var args0 = context.Service<global::Api.Database.PGContext>();
 1128                var args1 = context.Service<global::Api.Database.Utils.IEFTransactionDIAccessorService>();
 1129                var args2 = context.Service<global::Microsoft.Extensions.Configuration.IConfiguration>();
 1130                var args3 = context.Service<global::FluentValidation.IValidator<global::Api.Auth.Mutations.LoginInstance
 1131                var args4 = _binding_LoginTheInstanceService_input.Execute<global::Api.Auth.Mutations.LoginInstanceServi
 1132                var args5 = context.RequestAborted;
 1133                var result = await global::Api.Auth.Mutations.LoginInstanceServiceMutations.LoginTheInstanceService(args
 1134                return result;
 1135            }
 136        }
 137    }
 138}
 139