Как интегрировать социальный логин с существующим.Серверная часть Net core Web API и интерфейс Angular SPA с работающим пользователем / паролем OpenIddict и токеном на предъявителя

#asp.net-core #oauth-2.0 #asp.net-identity #single-page-application #openiddict

#asp.net-core #oauth-2.0 #asp.net-identity #одностраничное приложение #openiddict

Вопрос:

TL; DR

Вопрос: как реализовать социальный вход в систему (поток авторизации OAuth2) с существующим приложением SPA / Web API, основанным на идентификации, пользователе / пароле, аутентификации токена на предъявителя?


У меня есть существующее приложение, которое имеет:

Серверная часть: веб-API .Net Core 2 с настроенными службами идентификации и OpenIddict, с рабочим процессом аутентификации на основе запроса пользователя / пароля для токена на предъявителя.

Пользователи хранятся с идентификатором (AspNetUsers).

Часть кода Startup.cs

 // Register the OpenIddict services.
services.AddOpenIddict()
    .AddCore(options =>
    {
        options.UseEntityFrameworkCore().UseDbContext<ApplicationDbContext>();
    })
    .AddServer(options =>
    {
        options.UseMvc();
        options.EnableTokenEndpoint("/connect/token");
        options.AllowPasswordFlow();
        options.AllowRefreshTokenFlow();
        options.AcceptAnonymousClients();
        options.RegisterScopes(
            OpenIdConnectConstants.Scopes.OpenId,
            OpenIdConnectConstants.Scopes.Email,
            OpenIdConnectConstants.Scopes.Phone,
            OpenIdConnectConstants.Scopes.Profile,
            OpenIdConnectConstants.Scopes.OfflineAccess,
            OpenIddictConstants.Scopes.Roles);
    })
    .AddValidation();
  

.

Интерфейс: приложение SPA Angular 7, которое использует этот серверный API и авторизацию токенов

Таким образом, в основном текущая настройка заключается в том, что пользователь вводит пользователя / пароль в SPA, который вызывает /connect/token конечную точку сервера, которая проверяет учетные данные и генерирует токен для клиента.

И теперь мне нужно интегрировать социальный вход в систему (поток авторизации OAuth2), чтобы

  1. пользователь выбирает вход с помощью провайдера,
  2. перенаправляется на страницу авторизации провайдеров,
  3. перенаправляется обратно в мое приложение, которое
  4. необходимо создать идентификатор пользователя и сохранить данные Identity UserLoginInfo и
  5. предоставьте мой токен приложения, чтобы пользователь мог войти в систему.

Я понимаю поток авторизации OAuth2, который должен запрашивать код авторизации, а затем обменивать код авторизации на токен доступа для этого провайдера. Я также знаю, что этот поток должен использовать серверную часть, поскольку он использует конфиденциальную информацию, такую как client_secret, которая не может быть сохранена на стороне клиента.

Но в какой-то момент пользователю необходимо взаимодействовать с интерфейсом, поэтому подключение этих частей кажется очень сложным, учитывая, что это широко используемые технологии. Все практические примеры, которые я нашел в Google, были с использованием приложения .Net Core MVC. Я также нашел эту статью ASP.NET Предварительный просмотр ядра 3.0 4 — Аутентификация и авторизация для СПА-центров, которые кажутся многообещающими, но все еще находятся в предварительном просмотре 4.

Я уже создал приложения для социальных сетей, и у меня есть client_id, client_secret. Также зарегистрировал мои перенаправляющие URL-адреса.

То, что я безуспешно пытался, было:

  1. Во внешнем интерфейсе пользователь выбирает вход в систему с помощью социального провайдера,
  2. Пользователь перенаправляется на страницу авторизации провайдера, аутентифицируется и
  3. перенаправляется от провайдера на мой URL-адрес интерфейса ( redirect_uri ) с кодом провайдера, а затем
  4. мой интерфейс вызывает мою серверную /connect/token существующую конечную точку, передавая выбранного поставщика и полученный код, конечная точка была запрограммирована на получение поставщика и кода также, затем
  5. мой сервер вызывает отправку URL-адреса get accessToken провайдером "grant_type", "authorization_code" "code", code "redirect_uri", "https://same_frontend_host/same/path" "client_id", providerClientId "client_secret", providerSecret и получает StatusCode: 401, ReasonPhrase: 'Unauthorized' ответ

Что я делаю не так? Это было действительно трудное время, чтобы заставить это работать.

Что сработало, но это не то, что мне нужно

Неявный 2-шаговый процесс авторизации с использованием интерфейса для вызовов аутентификации поставщика и внутреннего вызова для получения моего токена на предъявителя и создания идентификатора пользователя. С помощью этой настройки пользователь успешно вошел в систему с помощью социального провайдера, к сожалению, это не то, что мне нужно

Редактировать:

Сделал диаграмму того, что реализовано, на шаге 5/6 происходит сбой с StatusCode: 401, ReasonPhrase: 'Unauthorized' , и дальнейшие шаги не завершены.

введите описание изображения здесь

Комментарии:

1. смогли ли вы найти решение, я столкнулся с аналогичной проблемой

Ответ №1:

Описанный вами процесс в значительной степени соответствует «Междоменному коду авторизации», потоку подключения OpenID, который никогда не был стандартизирован.

Я бы не рекомендовал использовать такой нестандартный вариант. Вместо этого рассмотрите возможность настройки вашего потока, чтобы ваш JS-клиент взаимодействовал исключительно с вашим собственным сервером авторизации, вместо того, чтобы запускать поток, перенаправляя клиент агента пользователя внешнему поставщику.

Ключевая идея здесь заключается в том, что ваш собственный сервер авторизации должен инициировать первоначальную связь с внешним провайдером (т. Е. он должен сформировать запрос на авторизацию и перенаправить ваших пользователей на конечную точку авторизации внешнего провайдера) и обработать последнюю часть: ответ на авторизацию обратного вызова. Для этого я бы рекомендовал использовать обработчики OAuth2 / OIDC, поставляемые с ASP.NET Ядро (есть поставщики для Google, Facebook и многих других)

Конечно, это не означает, что ваш JS-клиент не может отправить подсказку о внешнем поставщике, которого пользователь должен использовать для аутентификации. Это то, с чем вы можете легко справиться в своем контроллере авторизации. Вот пример:

 public class AuthorizationController : Controller
{
    private readonly IAuthenticationSchemeProvider _authenticationSchemeProvider;
    private readonly SignInManager<ApplicationUser> _signInManager;

    public AuthorizationController(
        IAuthenticationSchemeProvider authenticationSchemeProvider,
        SignInManager<ApplicationUser> signInManager)
    {
        _authenticationSchemeProvider = authenticationSchemeProvider;
        _signInManager = signInManager;
    }

    [HttpGet("~/connect/authorize")]
    public async Task<IActionResult> Authorize(OpenIdConnectRequest request)
    {
        Debug.Assert(request.IsAuthorizationRequest(),
            "The OpenIddict binder for ASP.NET Core MVC is not registered. "  
            "Make sure services.AddOpenIddict().AddMvcBinders() is correctly called.");

        if (!User.Identity.IsAuthenticated)
        {
            // Resolve the optional provider name from the authorization request.
            // If no provider is specified, call Challenge() to redirect the user
            // to the login page defined in the ASP.NET Core Identity options.
            var provider = (string) request.GetParameter("identity_provider");
            if (string.IsNullOrEmpty(provider))
            {
                return Challenge();
            }

            // Ensure the specified provider is supported.
            var schemes = await _authenticationSchemeProvider.GetAllSchemesAsync();
            if (!schemes.Any(scheme => scheme.Name == provider))
            {
                return Challenge();
            }

            // When using ASP.NET Core Identity and its default AccountController,
            // the user must be redirected to the ExternalLoginCallback action
            // before being redirected back to the authorization endpoint.
            var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider,
                Url.Action("ExternalLoginCallback", "Account", new
                {
                    ReturnUrl = Request.PathBase   Request.Path   Request.QueryString
                }));

            return Challenge(properties, provider);
        }

        // ...
    }
}
  

Комментарии:

1. Может быть слишком поздно… но он использует WebApi который не может инициировать поток…