Back to "Hinzufügen von Google Auth ohne ASPA.-NET-Identitätsdatenbank"

This is a viewer only at the moment see the article on how this works.

To update the preview hit Ctrl-Alt-R (or ⌘-Alt-R on Mac) or Enter to refresh. The Save icon lets you save the markdown file to disk

This is a preview from the server running through my markdig pipeline

ASP.NET Google Auth

Hinzufügen von Google Auth ohne ASPA.-NET-Identitätsdatenbank

Monday, 05 August 2024

Einleitung

In dieser App wollte ich einen einfachen Mechanismus der Anmeldung hinzufügen, um Kommentare hinzuzufügen.( und einige Admin-Aufgaben...). zur App hinzufügen... Ich wollte Google Auth für diesen Zweck verwenden.MSC3. Ich habe nicht die ASPC..NET Identity-Datenbank zu diesem Zweck nutzen wollen.MSZ5. Ich möchte die App so einfach wie möglich halten.

Datenbanken sind ein leistungsfähiger Bestandteil jeder Anwendung, aber sie fügen auch Komplexität hinzu... Ich wollte diese Komplexität vermeiden, bis ich es wirklich brauchte.

Schritte

Zuerst müssen Sie Google Auth in der Google Developer Console einrichten. Verknüpfung um Ihre Daten für Sie eingestellt Google Client ID und Secret-.

Sobald Sie Ihre Google-Client-ID und geheim haben, können Sie sie zu Ihren Appsettings hinzufügen.

    "Auth" :{
"GoogleClientId": "",
"GoogleClientSecret": ""
}

WIE auch immer du diese nicht überprüfen solltest, um die Kontrolle zu übernehmen.Stattdessen kannst du für lokale Entwicklung die Secrets-Datei verwenden.

secrets.png

Dort können Sie Ihre Google Client-ID und Secret hinzufügen.Beachten Sie, dass Ihr Client Id tatsächlich vertraulich ist.

    "Auth" :{
  "GoogleClientId": "ID",
  "GoogleClientSecret": "CLIENTSECRET"
}

Google Auth mit POCO konfigurieren

Anmerkung: Ich verwende eine modifizierte Version von Steve Smith-'s IConfigSection-Version, die kürzlich von Phil Haack berühmt gemacht wurde. Dies ist, um die IOptions Sachen zu vermeiden, die ich ein wenig klobig finde -( und selten brauchen, da ich fast nie ändern Konfiguration nach der Bereitstellung in meinen Szenarien-MSC1

In meinem tue ich dies, was mir erlaubt, den Abschnitt Namen von der Klasse selbst zu erhalten.

Click to expand


namespace Mostlylucid.Config;

public static class ConfigExtensions {
    public static TConfig ConfigurePOCO<TConfig>(this IServiceCollection services, IConfiguration configuration)
        where TConfig : class, new() {
        if (services == null) throw new ArgumentNullException(nameof(services));
        if (configuration == null) throw new ArgumentNullException(nameof(configuration));
        
        var config = new TConfig();
        configuration.Bind(config);
        services.AddSingleton(config);
        return config;
    }
    
    public static TConfig Configure<TConfig>(this WebApplicationBuilder builder)
        where TConfig : class, IConfigSection, new() {
        var services = builder.Services;
        var configuration = builder.Configuration;
        var sectionName = TConfig.Section;
        return services.ConfigurePOCO<TConfig>(configuration.GetSection(sectionName));
    }
    

    public static TConfig GetConfig<TConfig>(this WebApplicationBuilder builder)
        where TConfig : class, IConfigSection, new() {
        var configuration = builder.Configuration;
        var sectionName = TConfig.Section;
        var section = configuration.GetSection(sectionName).Get<TConfig>();
        return section;
        
    }
    
    public static Dictionary<string, object> GetConfigSection(this IConfiguration configuration, string sectionName) {
        var section = configuration.GetSection(sectionName);
        var result = new Dictionary<string, object>();
        foreach (var child in section.GetChildren()) {
            var key = child.Key;
            var value = child.Value;
            result.Add(key, value);
        }
        
        return result;
    }
    
    public static Dictionary<string, object> GetConfigSection<TConfig>(this WebApplicationBuilder builder)
        where TConfig : class, IConfigSection, new() {
        var configuration = builder.Configuration;
        var sectionName = TConfig.Section;
        return configuration.GetConfigSection(sectionName);
    }
}

public interface IConfigSection {
    public static abstract string Section { get; }
}
Also sieht mein Auth aus wie
public class Auth : IConfigSection
{
    public static string Section => "Auth";
    public string GoogleClientId { get; set; }
    public string GoogleClientSecret { get; set; }
    
    public string AdminUserGoogleId { get; set; }
    
}

Wo ich eine statische Schnittstelle-Methode verwenden, um den Abschnitt Namen zu erhalten.

Dann in meinem Startup kann ich dies tun

var auth = builder.GetConfig<Auth>();

ANYWAY zurück zu den Google-Sachen

Programm einrichten

UM dies tatsächlich hinzuzufügen

services.AddCors(options =>
{
    options.AddPolicy("AllowMostlylucid",
        builder =>
        {
            builder.WithOrigins("https://www.mostlylucid.net")
                .WithOrigins("https://mostlylucid.net")
                .WithOrigins("https://localhost:7240")
                .AllowAnyHeader()
                .AllowAnyMethod();
        });
});

builder.Services
    .AddAuthentication(options =>
    {
        options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = GoogleDefaults.AuthenticationScheme;
   
      
    })
    .AddCookie()
    .AddGoogle(options =>
    {
        options.ClientId = auth.GoogleClientId;
        options.ClientSecret = auth.GoogleClientSecret;
    });

Sie müssen auch diese in der Google-Identitätskonsole einrichten

googleidentity.png

Dies stellt sicher, dass der Google Auth nur von den Domains verwendet werden kann, die Sie angeben.

Google Auth In Razor

In meinem _Layout-.Cshtml Ich habe dieses Javascript-M,. Hier stelle ich meine Google Buttons ein und löse einen Rückruf aus, der die ASP-MSA-App protokolliert.

Google JS

<script src="https://accounts.google.com/gsi/client" async defer></script>

Dies ist der Dlow für den Code unten



        
        function renderButton(element)
        {
            google.accounts.id.renderButton(
                element,
                {
                    type: "standard",
                    size: "large",
                    width: 200,
                    theme: "filled_black",
                    text: "sign_in_with",
                    shape: "rectangular",
                    logo_alignment: "left"
                }
            );
        }
        function initGoogleSignIn() {
            google.accounts.id.initialize({
                client_id: "839055275161-u7dqn2oco2729n6i5mk0fe7gap0bmg6g.apps.googleusercontent.com",
                callback: handleCredentialResponse
            });
            const element = document.getElementById('google_button');
            if (element) {
                renderButton(element);
            }
            const secondElement = document.getElementById('google_button2');
            if (secondElement) {
                renderButton(secondElement);
            }
           
        }

        function handleCredentialResponse(response) {
            if (response.credential) {
                const xhr = new XMLHttpRequest();
                xhr.open('POST', '/login', true);
                xhr.setRequestHeader('Content-Type', 'application/json');
                xhr.onload = function () {
                    if (xhr.status === 200) {
                        window.location.reload();
                    } else {
                        console.error('Failed to log in.');
                    }
                };
                xhr.send(JSON.stringify({ idToken: response.credential }));
            } else {
                console.error('No credential in response.');
            }
        }

        window.onload = initGoogleSignIn;

Hier können Sie sehen, dass ich bis zu zwei div Elemente in der Seite mit dem id google--Button und googles--button-2.-Dies sind die Elemente, die die Google JS rendern wird die Tasten in...

TIPP-:. Wenn Sie Tailwind verwenden, können Sie die Taste div shink, um im dunklen Modus korrekt zu arbeiten.Übrigens gibt es einen weißen Hintergrund rund um den Button wieder.

<div class="w-[200px] h-[39px] overflow-hidden rounded">
    <div id="google_button">
    </div>
</div>

Im obigen JavaScript poste ich dies zurück zu einer Controller-Aktion namens Login-.. Hier handle ich mit dem Google Auth...

      const xhr = new XMLHttpRequest();
                xhr.open('POST', '/login', true);
                xhr.setRequestHeader('Content-Type', 'application/json');
                xhr.onload = function () {
                    if (xhr.status === 200) {
                        window.location.reload();
                    } else {
                        console.error('Failed to log in.');
                    }
                };
                xhr.send(JSON.stringify({ idToken: response.credential }));

Google Auth im Controller

Der Controller ist hier.'... es ist ziemlich einfach, es nimmt nur die geposteten JWT.MSC2 encodiert es dann verwendet, dass um sich bei der App zu anmelden.

    [Route("login")]
        [HttpPost]
        public async Task<IActionResult> HandleGoogleCallback([FromBody] GoogleLoginRequest request)
        {
            var handler = new JwtSecurityTokenHandler();
            var jsonToken = handler.ReadToken(request.IdToken) as JwtSecurityToken;

            if (jsonToken == null)
            {
                return BadRequest("Invalid token");
            }

            var claimsIdentity = new ClaimsIdentity(
                jsonToken.Claims,
                GoogleDefaults.AuthenticationScheme);

            var authProperties = new AuthenticationProperties
            {
                IsPersistent = true
            };

            await HttpContext.SignInAsync(
                CookieAuthenticationDefaults.AuthenticationScheme,
                new ClaimsPrincipal(claimsIdentity),
                authProperties);

            return Ok();
        }
    }

HINWEIS:-Dies ist der Präfekt von ', da er die Claimnamen auflöst. Sie sind alle unteren Fall, aber es funktioniert für jetzt.

Controller Basisklasse zum Extrahieren der Login-Eigenschaften

In meinem BaseController extrahiere ich die Eigenschaften, die ich benötige

      public record LoginData(bool loggedIn, string? name, string? avatarUrl, string? identifier);
    
    protected LoginData GetUserInfo()
    {
        var authenticateResult = HttpContext.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationScheme).Result;
        if (authenticateResult.Succeeded)
        {
            var principal = authenticateResult.Principal;
            if(principal == null)
            {
                return new LoginData(false, null, null, null);
            }
            var name = principal.FindFirst("name").Value;
            var avatarUrl =principal.FindFirst("picture").Value;
            var nameIdentifier = principal.FindFirst("sub");
            return new LoginData(true, name, avatarUrl, nameIdentifier?.Value);
        }
        return new LoginData(false,null,null,null);
    }

Und dies ermöglicht es, Gooogle-Authentifizierung zu verwenden, ohne die ASP.....NET Identity-Datenbank zu benutzen.

logo

© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.