Как интегрировать Social Login с существующим базовым Web-интерфейсом .Net API и внешним интерфейсом Angular SPA с рабочими пользователем / паролем OpenIddict и токеном на предъявителя. - PullRequest
0 голосов
/ 23 апреля 2019

TL; DR

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


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

Серверная часть: .Net Core 2 Web API с настроенными службами Identity и 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, которая проверяет учетные данные и генерирует токен для клиента.

А теперь мне нужно интегрировать Social Login (поток авторизации OAuth2), чтобы

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

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

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

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

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

  1. В интерфейсе пользователь выбирает логин с социальным провайдером,
  2. Пользователь перенаправляется на страницу авторизации провайдера, аутентифицируется и
  3. перенаправляется от провайдера на мой внешний URL-адрес (redirect_uri) с кодом провайдера, тогда
  4. мой веб-интерфейс вызывает мой бэкэнд /connect/token существующей конечной точки, передающей выбранного провайдера и полученный код, конечная точка была запрограммирована на получение провайдера и кода, затем
  5. мой провайдер внутренних вызовов получает URL-адрес 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' ответ

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

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

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

EDIT:

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

enter image description here

1 Ответ

0 голосов
/ 08 мая 2019

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

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

Ключевая идея заключается в том, что ваш собственныйСервер авторизации должен инициировать первоначальное взаимодействие с внешним поставщиком (т. е. он должен создать запрос на авторизацию и перенаправить ваших пользователей на конечную точку авторизации внешнего поставщика) и обработать последнюю часть: ответ авторизации обратного вызова.Для этого я бы рекомендовал использовать обработчики OAuth2 / OIDC, поставляемые с ASP.NET Core (есть провайдеры для 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);
        }

        // ...
    }
}
...