# Доdanie Google Auth без ASP.NET Identity Database

<!--category-- ASP.NET, Google Auth -->
<datetime class="hidden">2024-08-05T08:06</datetime>

## Введение

У цій програмі я хотів додати механізм зразка, що дозволяє при вході додавати коментарі ( і деякі адміністративні завдання ) до aplikacji МSK2 Я хотів використати Google Auth для цього celu \ . Я не хотів використовувати ASP \ МSK4 \ NET Identity базу даних для цього призначення \. \ Я хотіла залишити програму такою простою, як це можливо довгий час | . \

Базики даних - це потужна складова будь-якої додатки, але вони також додають і складності.

[TOC]

## Шаг

Спочатку треба встановити Google Auth в Google Developer Console. Ви можете зробити наступні кроки: [зв 'язок](https://developers.google.com/identity/gsi/web/guides/overview) щоб отримати ваші дані для вас Google Client ID та Secret.

Після того, як у вас є Google Client ID та Secret,, ви можете додати їх до ваших настроїв

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

Звісно, ви не маєте перевіряти їх на контролі над джерелом. Замість локального розвитку ви можете використовувати файл секретів:

![secrets.png](secrets.png)

Там ви можете додати вашу Google Client ID і секретну. ( зауважте, що ваші клієнтські ID не конфіденційні.

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

## Konfiguracja Google Auth з POCO

Зверніть увагу, я використовую модифіковану версію IConfigSection Стіва Сміта
Це для того, щоб уникнути IOptions речей, які я вважаю трохи незграбними ( і рідко потребують, тому що я майже ніколи не змінюю конфігурацію після застосування у моїх сценаріїв ).

У моїй я роблю це, що дозволяє мені отримати ім 'я секції з самого класу:

<details>
<summary>Click to expand</summary>

```csharp


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; }
}
```

</details>
Отже, моя Auth виглядає так

```csharp
public class Auth : IConfigSection
{
    public static string Section => "Auth";
    public string GoogleClientId { get; set; }
    public string GoogleClientSecret { get; set; }
    
    public string AdminUserGoogleId { get; set; }
    
}
```

Де я використовую статичний метод інтерфейсу для отримання назви секції.

Тоді я можу зробити це в своєму стартеті:

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

В будь-якому випадку повернімося до Google stuff!

## Установка програми.cs

Щоб додати це

```csharp
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;
    });
```

Ви' помічаєте там ' вхідні записи CORS тут МSK2 вам також потрібно встановити їх у консольі Google Identity

![googleidentity.png](googleidentity.png)

Це гарантує, що Auth Google можна використовувати лише з домен, які ви встановлюєте.

## Google Auth In Razor

У моєму _У мене є цей Javascript, тут я встановлюю свої кнопки Google і запускаю зворотний дзвінок, який записує ASPM SK2NET app.

# Google JS

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

Це падіння для коду нижче

```javascript


        
        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;

```

Тут ви бачите, що у мене є до двох div елементів на сторінці з id google_ кнопка і googleМSK1 кнопкиM SK2 Це елементи, які Google JS перетворить на кнопки в.

TIP: Якщо ви використовуєте Tailwind,, ви можете втиснути кнопку div, щоб працювати правильно у темному режимі M SK2 інакше вона відображає білий фон навколо кнопки )

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

У JavaScript я відтворюю це назад до дії Controller під назвою Login. Тут я обробляю Google Auth

```javascript
      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 у контроллері

Тут є контроллер. МСК0, МСК1 досить простий. Просто треба записати JWT. МSK2 розшифровує, а потім використовує його для входу в додаток.

```csharp
    [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();
        }
    }
```

NOTA: Це не МСК1 не префект, тому що він виділяє імена відповідальності. M SK2 всі вони нижчі, МSK3 але це працює на даний момент

### Базова клас контроллера для видобутку властивостей входу

У своєму контроллері я виділяю необхідні властивості;

```csharp
      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);
    }
```

І це ─ МСК0 ─ це ‒ МSK1 ─ Це дозволяє використовувати аутентифікацію Gooogle без використання ASP ─ . ─ NET Identity database ─.