AuthorizationCodeProvider: Create никогда не вызывается, как сгенерировать код авторизации?

Я настраиваю свой собственный сервер OAuth2. До сих пор я успешно реализовал GrantResourceOwnerCredentials в своей реализации OAuthAuthorizationServerProvider. Теперь, поскольку я разрабатываю приложение для нашего бизнеса, я хочу реализовать грант кода авторизации OAuth2.

Я пытался следовать указаниям здесь https://docs.microsoft.com/en-us/aspnet/aspnet/overview/owin-and-katana/owin-oauth-20-authorization-server, но в моей реализации я не нашел как достучаться до Create звонка AuthorizationCodeProvider (которую поставил в OAuthAuthorizationServerOptions).

Я кратко проверил, работает ли доступ к TokenEndpointPath с (неправильным) параметром кода, и в отладчике я вижу, что вызов Receive моего AuthorizationCodeProvider срабатывает. Конечно, успеха нет, потому что код, который я отправляю, является «sometestcode», а не реальным, но код срабатывает, а это значит, что я на правильном пути.

Вот что у меня есть до сих пор:

    public override Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context)
    {
        if (OAuthRepository.GetClient(context.ClientId) != null)
        {
            var expectedRootUri = new Uri(context.Request.Uri, "/");

            if (context.RedirectUri.StartsWith(expectedRootUri.AbsoluteUri))
            {
                context.Validated();
                return Task.FromResult<object>(null);
            }
        }

        context.Rejected();
        return Task.FromResult<object>(null);
    }

    public override Task AuthorizeEndpoint(OAuthAuthorizeEndpointContext context)
    {
        // I know this is wrong but it's just a start and not the focus of this SO question.
        context.Response.Redirect(context.AuthorizeRequest.RedirectUri);
        context.RequestCompleted();
        return Task.FromResult<object>(null);
    }

    public override Task GrantAuthorizationCode(OAuthGrantAuthorizationCodeContext context)
    {
        // Needs additional checks, not the focus of my question either 
        var newTicket = new AuthenticationTicket(context.Ticket.Identity, context.Ticket.Properties);
        context.Validated(newTicket);
        return Task.FromResult<object>(null);
    }

Теперь, когда я звоню своему AuthorizeEndpointPath с помощью redirect_uri, меня немедленно отправляют к этому Ури. Я знаю, что это неправильно: меня должны отправить на отдельную страницу входа. Позже я исправлю свой веб-API, чтобы перенаправить на правильный Uri.

Суть моего вопроса заключается в следующем: сейчас я нахожусь в процессе реализации страницы входа, но я не знаю, как получить код авторизации из моего WebAPI после того, как пользователь вошел в систему. (Я пропускаю часть согласия для теперь и предположим, что если пользователь вошел в систему, с ним все в порядке, я добавлю согласие позже.)

Я основываю свой поток на схеме, опубликованной здесь https://docs.apigee.com/api-platform/security/oauth/oauth-v2-policy-authorization-code-grant-type

Я использую Thinktecture IdentityModel для создания страницы входа в контроллер MVC. Теперь мне нужно получить код авторизации из веб-API в моем контроллере MVC. И после этого я могу перенаправить пользователя обратно к исходному клиенту (приложению), которое запросило поток кода авторизации.

Чтобы получить код авторизации из моего веб-API, я вижу три метода в OAuth2Client Thinktecture:

  • CreateAuthorizeUrl
  • CreateCodeFlowUrl
  • реквестауториатионатионкодеасинк

Кажется, ни один из них не делает то, что я хочу. Как мне поступить, чтобы мой WebAPI вызывался для генерации кода?

    [HttpGet]
    [ImportModelStateFromTempData]
    public ActionResult Authorize(string clientId, string returnUrl, string responseType)
    {
        AuthorizeViewModel viewModel = new AuthorizeViewModel();
        ...
        ... 
        ...
        return View(viewModel);
    }

    [HttpPost]
    [ExportModelStateToTempData]
    public async Task<ActionResult> Authorize(AuthorizeViewModel viewModel)
    {
        // NOTE: This is in MVC and is postback from *.cshtml View.
        OAuth2Client.?????? // <=== How to obtain authorization code from WebAPI?

        ...
        return Redirect(returnUrl);
    }

Я думаю, что правильно настроил его на стороне веб-API. Я просто не знаю, как попасть в Create часть потока. Я надеюсь, что кто-то может помочь мне понять, чего я не вижу. У меня где-то слепое пятно, я думаю...

Как заставить OAuth2Client получить код авторизации из моего WebAPI?

Я также использую Postman для тестирования своего веб-API. Если кто-нибудь может помочь мне получить URL-адрес в Web API 2.0, который возвращает код авторизации, я также приму это как ответ. Тогда я смогу сам написать код в MVC.

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

Итак, я думаю, что нашел часть своего слепого пятна. Во-первых, я отметил «AuthorizeEndpoint» как «не в центре внимания этого вопроса SO», но это было большой ошибкой.

Когда я адаптирую AuthorizeEndpoint следующим образом:

    public override Task AuthorizeEndpoint(OAuthAuthorizeEndpointContext context)
    {
        System.Security.Claims.ClaimsIdentity ci = new System.Security.Claims.ClaimsIdentity("Bearer");
        context.OwinContext.Authentication.SignIn(ci);
        context.RequestCompleted();
        return Task.FromResult<object>(null);
    }

И если я адаптирую свою реализацию AuthorizationCodeProvider.Create так:

    public void Create(AuthenticationTokenCreateContext context)
    {
        context.Ticket.Properties.IssuedUtc = DateTime.UtcNow;
        context.Ticket.Properties.ExpiresUtc = DateTime.UtcNow.AddSeconds(60);

        // Some random Guid
        context.SetToken(Guid.NewGuid().ToString("n"));
    }

Любой вызов /authorize перенаправляется на redirect_uri с параметром запроса code=<THE_RANDOM_GUID>! :D

Очевидно, что эта реализация не там, где должна быть, поэтому мой вопрос еще не решен. Оставшиеся проблемы:

  • Прямо сейчас любой может запросить код авторизации, client_id игнорируется. ValidateClientAuthentication, по-видимому, не пострадал как часть AuthorizeEndpoint. Как получить ClientId в AuthorizeEndpoint?
  • Код авторизации не привязан к клиенту. Любой, кто перехватит код, сможет его использовать. Как мне получить ClientId в AuthorizationCodeProvider.Create, чтобы сохранить его вместе с кодом?
  • Код авторизации вообще не привязан к пользователю, это пустой ClaimsIdentity. Как поместить страницу входа пользователя между ними, а в AuthorizeEndpoint получить ClaimsIdentity для вошедшего в систему пользователя?

person Diana Koenraadt    schedule 17.04.2019    source источник
comment
Когда вы тестировали с Почтальоном, была ли вообще активирована авторизация? Я бы предположил, что нет, поскольку обычно для AuthorizeViewModel требуется атрибут [FromBody].   -  person roboto1986    schedule 18.04.2019
comment
@ roboto1986 Авторизация в MVC. У меня есть View Authorize.cshtml, который отлично отображается. Я просто не знаю, какой код написать в обратной передаче, чтобы получить код авторизации из Web API. Спасибо за ответ, я рад, что вопрос привлекает внимание, так как я застрял...   -  person Diana Koenraadt    schedule 18.04.2019
comment
Вы видели некоторые из этих примеров?: csharp.hotexamples.com/examples/   -  person roboto1986    schedule 18.04.2019
comment
Спасибо. Примеры не решили, увы..   -  person Diana Koenraadt    schedule 20.04.2019


Ответы (1)


Итак, после некоторого поиска в Интернете я добился некоторого успеха, выполнив поиск на github. По-видимому, OAuthAuthorizationServerProvider предлагает AuthorizeEndpoint, и этот метод следует использовать для обоих "Эй, вы не авторизованы, идите авторизоваться!" а также для "Ааа, ладно ты крут, вот код авторизации.". Я ожидал, что OAuthAuthorizationServerProvider будет иметь для этого два отдельных метода, но это не так. Это объясняет, почему на github я нахожу несколько проектов, реализующих AuthorizeEndpoint довольно своеобразным образом. Я принял это. Вот пример:

public override async Task AuthorizeEndpoint(OAuthAuthorizeEndpointContext context)
{
    if (context.Request.User != null && context.Request.User.Identity.IsAuthenticated)
    {
        var redirectUri = context.Request.Query["redirect_uri"];
        var clientId = context.Request.Query["client_id"];

        var authorizeCodeContext = new AuthenticationTokenCreateContext(
            context.OwinContext, 
            context.Options.AuthorizationCodeFormat,
            new AuthenticationTicket(
                (ClaimsIdentity)context.Request.User.Identity,
                new AuthenticationProperties(new Dictionary<string, string>
                {
                    {"client_id", clientId},
                    {"redirect_uri", redirectUri}
                })
            {
                IssuedUtc = DateTimeOffset.UtcNow,
                ExpiresUtc = DateTimeOffset.UtcNow.Add(context.Options.AuthorizationCodeExpireTimeSpan)
            }));

        await context.Options.AuthorizationCodeProvider.CreateAsync(authorizeCodeContext);

        context.Response.Redirect(redirectUri + "?code=" + Uri.EscapeDataString(authorizeCodeContext.Token));
    }
    else
    {
        context.Response.Redirect("/account/login?returnUrl=" + Uri.EscapeDataString(context.Request.Uri.ToString()));
    }
    context.RequestCompleted();
}

Источник: https://github.com/wj60387/WebApiOAUthBase/blob/master/OwinWebApiBase/WebApiOwinBase/Providers/OAuthServerProvider.cs

Что касается моих оставшихся трех вопросов:

  1. Прямо сейчас любой может запросить код авторизации, client_id игнорируется. ValidateClientAuthentication, по-видимому, не попадает в состав AuthorizeEndpoint. Как получить ClientId в AuthorizeEndpoint?

Ответ: Вы должны реализовать `ValidateClientAuthentication'.

  1. Код авторизации не привязан к клиенту. Любой, кто перехватит код, сможет его использовать. Как получить ClientId в AuthorizationCodeProvider.Create, чтобы сохранить его вместе с кодом?

Ответ: OAuthAuthorizationServerProvider позаботится об этом. Пока вы устанавливаете «client_id» в билете, он будет проверять, что клиент, который запрашивает токен доступа для кода авторизации, один и тот же.

  1. Код авторизации вообще не привязан к пользователю, это пустой ClaimsIdentity. Как поместить страницу входа пользователя между ними, а в AuthorizeEndpoint получить ClaimsIdentity для вошедшего в систему пользователя?

Ответ: Вы создаете отдельную страницу входа. Это означает, что пользователь входит в систему. Если ваш WebAPI использует аутентификацию на основе файлов cookie, вы можете просто перенаправить пользователя на AuthorizeEndpoint еще раз. Если вы используете токены доступа, ваша страница входа должна сделать запрос к `AuthorizeEndpoint' с токеном доступа для получения кода авторизации. (Не передавайте токен доступа третьим лицам. Ваша страница входа запрашивает код авторизации и отправляет его обратно.) Другими словами, если вы используете токены доступа, то в этом потоке участвуют два клиента.

person Diana Koenraadt    schedule 01.05.2019