< Summary - Syki

Information
Class: Syki.Back.Features.Identity.GoogleOneTapLogin.GoogleOneTapLoginService
Assembly: Back
File(s): /home/runner/work/syki/syki/Back/Features/Identity/GoogleOneTapLogin/GoogleOneTapLoginService.cs
Tag: 97_27801654829
Line coverage
0%
Covered lines: 0
Uncovered lines: 42
Coverable lines: 42
Total lines: 77
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 16
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%210%
.ctor()100%210%
.cctor()100%210%
Login()0%272160%

File(s)

/home/runner/work/syki/syki/Back/Features/Identity/GoogleOneTapLogin/GoogleOneTapLoginService.cs

#LineLine coverage
 1using Syki.Back.Google;
 2using Syki.Back.Domain.Identity;
 3using Syki.Back.Domain.Institutions;
 4using Syki.Back.Features.Cross.SignIn;
 5
 6namespace Syki.Back.Features.Identity.GoogleOneTapLogin;
 7
 08public class GoogleOneTapLoginService(
 09    SykiDbContext ctx,
 010    SignInService signInService,
 011    IGoogleService googleService,
 012    UserManager<SykiUser> userManager,
 013    SocialLoginSettings socialLoginSettings) : ISykiService
 14{
 15    private class Validator : AbstractValidator<GoogleOneTapLoginIn>
 16    {
 017        public Validator()
 18        {
 019            RuleFor(x => x.Credential).NotEmpty().WithError(GoogleOneTapLoginInvalidToken.I);
 020        }
 21    }
 022    private static readonly Validator V = new();
 23
 24    public async Task<OneOf<GoogleOneTapLoginOut, SykiError>> Login(GoogleOneTapLoginIn data)
 25    {
 026        if (V.Run(data, out var error)) return error;
 27
 028        if (!socialLoginSettings.Google.Enabled) return GoogleOneTapLoginDisabled.I;
 29
 030        var payload = await googleService.ValidateIdTokenAsync(data.Credential, socialLoginSettings.Google.ClientId);
 031        if (payload == null) return GoogleOneTapLoginInvalidToken.I;
 32
 033        if (!payload.EmailVerified) return SocialLoginEmailNotVerified.I;
 34
 035        var email = payload.Email.ToLowerInvariant();
 036        if (await ctx.EmailRequiresSsoAsync(email)) return SocialLoginSsoRequired.I;
 37
 038        var provider = SocialLoginProvider.Google;
 039        var providerKey = payload.Subject;
 40
 41        // 1. Look up existing social login link by (provider, providerKey)
 042        var existingLink = await ctx.UserSocialLogins.FirstOrDefaultAsync(x => x.Provider == provider && x.ProviderKey =
 043        if (existingLink != null)
 44        {
 045            var jwt = await signInService.SignIn(existingLink.Email);
 046            return jwt.ToGoogleOneTapLoginOut();
 47        }
 48
 49        // 2. Look up existing user by email
 050        var existingUser = await ctx.Users.FirstOrDefaultAsync(u => u.Email == email);
 051        if (existingUser != null)
 52        {
 053            existingUser.ConfirmEmail();
 054            ctx.Add(new UserSocialLogin(existingUser.Id, provider, providerKey, email));
 055            await ctx.SaveChangesAsync();
 056            var jwt = await signInService.SignIn(email);
 057            return jwt.ToGoogleOneTapLoginOut();
 58        }
 59
 60        // 3. Auto-provision new user on public and web schemes
 061        var name = payload.Name;
 062        if (name.IsEmpty()) name = email;
 63
 064        var directorRole = await ctx.GetDirectorRole();
 65
 066        var institution = Institution.NewForUserRegister();
 067        var user = new SykiUser(institution, name, email);
 068        var userRole = new SykiUserRole(institution, user, directorRole.Id);
 069        var socialLogin = new UserSocialLogin(user.Id, provider, providerKey, email) { User = user };
 70
 071        ctx.AddRange(institution, userRole, socialLogin);
 072        await userManager.CreateAsync(user, $"Syki@{Guid.NewGuid()}");
 73
 074        var jwtResult = await signInService.SignIn(email);
 075        return jwtResult.ToGoogleOneTapLoginOut();
 076    }
 77}