Подтвердить что ты не робот

OWIN/OAuth2 Сторонний логин: аутентификация из клиентского приложения, авторизация из веб-интерфейса API

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

Вот чего я пытаюсь достичь:

Я использую стандартный шаблон ASP.NET Web API Visual Studio с внешней аутентификацией, а также OWin.Security.Providers Пакет Nuget для Dropbox функции входа в систему и существующие встроенные функции входа для Google (Диск) и Microsoft (OneDrive).

Проблема, с которой я сталкиваюсь, заключается в том, что встроенная функциональность, похоже, выполняет аутентификацию и авторизацию как часть одного потока. Например, если я установил следующее в Startup.Auth.cs:

DropboxAuthenticationOptions dropboxAuthOptions = new DropboxAuthenticationOptions
                                                    {
                                                        AppKey = _dropboxAppKey,
                                                        AppSecret = _dropboxAppSecret
                                                    };
app.UseDropboxAuthentication(dropboxAuthOptions);

... и перейдите к этому URL из моего веб-браузера:

http://<api_base_url>/api/Account/ExternalLogin?provider=Dropbox&response_type=token&client_id=self&redirect_uri=<api_base_url>

Я успешно перенаправлен на Dropbox для входа в систему:

https://www.dropbox.com/1/oauth2/authorize?response_type=code&client_id=<id>&redirect_uri=<redirect_uri>

... а затем, после предоставления доступа, перенаправляется обратно на:

http://<api_base_url>/Help#access_token=<access_token>&token_type=bearer&expires_in=1209600

... поскольку вы можете видеть, что токен является частью этого, поэтому его можно извлечь. Проблема заключается в том, что клиент должен быть одним из тех, кто перемещается в Dropbox и возвращает код авторизации обратно в веб-API, и веб-API отправит код авторизации обратно третьему лицу, чтобы получить токен, который затем будет возвращен клиент... как показано на диаграмме выше. Мне нужно действие ExternalLogin в AccountController, чтобы каким-то образом получить URL-адрес Dropbox и вернуть его клиенту (это будет просто ответ json), но я не вижу способа получить это (он просто возвращает ChallengeResult, а фактический код Dropbox где-то похоронен). Кроме того, я думаю, мне нужен способ отдельно запросить токен у третьей стороны на основе кода авторизации.

Этот пост кажется немного похожим на то, что я пытаюсь сделать:

Регистрация внешних интерфейсов Web API 2 от нескольких клиентов API с идентификатором OWIN

... но решение, похоже, требует, чтобы клиент был MVC-приложением, что не обязательно для меня. Я хочу, чтобы это было максимально просто на стороне клиента, следуйте потоку с моей диаграммы выше, но также не изобретайте колесо (как можно больше используйте то, что уже существует в реализации OWIN/OAuth2). В идеале я не хочу, чтобы клиент должен был ссылаться на любую из OWIN/OAuth-библиотек, поскольку все, что мне действительно нужно, это доступ к внешнему url, предоставленному API (Dropbox в моем примере), пользователь вводит свои учетные данные и дать разрешение, и отправить полученный код авторизации обратно на api.

Концептуально это звучит не так сложно, но я не знаю, как его реализовать и по-прежнему использовать как можно больше существующего кода OAuth. Пожалуйста, помогите!

4b9b3361

Ответ 1

Чтобы быть ясным, образец, который я упомянул в указанной вами ссылке, может использоваться с любым клиентом OAuth2, используя любой поддерживаемый поток (неявный, код или пользовательский). При общении с вашим собственным сервером авторизации вы, конечно, можете использовать неявный поток, если хотите использовать JS или мобильные приложения: вам просто нужно создать запрос авторизации с помощью response_type=token и извлечь токен доступа из фрагмента URI в JS сторона.

http://localhost:55985/connect/authorize?client_id=myClient&redirect_uri=http%3a%2f%2flocalhost%3a56854%2f&response_type=token

Для справки, здесь образец: https://github.com/aspnet-security/AspNet.Security.OpenIdConnect.Server/tree/dev/samples/Mvc/Mvc.Server


В случае, если вы предпочитаете более простой подход (в котором не будет никакого настраиваемого сервера авторизации OAuth2), здесь еще один вариант с использованием промежуточного программного обеспечения для идентификации на предъявителя OAuth2 и реализация пользовательского IAuthenticationTokenProvider для ручной проверки непрозрачного токена, выпущенного Dropbox. В отличие от упомянутого примера (который действует как прокси-сервер авторизации между Dropbox и клиентским приложением MVC), приложение JS напрямую зарегистрировано в Dropbox.

Вам нужно будет сделать запрос против конечной точки профиля Dropbox (https://api.dropbox.com/1/account/info) с принятым токеном, чтобы проверить его и построить адекватный экземпляр ClaimsIdentity для каждого запроса, полученного вашим API. Здесь образец (но, пожалуйста, не используйте его как есть, он не был протестирован):

public sealed class DropboxAccessTokenProvider : AuthenticationTokenProvider {
    public override async Task ReceiveAsync(AuthenticationTokenReceiveContext context) {
        using (var client = new HttpClient()) {
            var request = new HttpRequestMessage(HttpMethod.Get, "https://api.dropbox.com/1/account/info");
            request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", context.Token);

            var response = await client.SendAsync(request);
            if (response.StatusCode != HttpStatusCode.OK) {
                return;
            }

            var payload = JObject.Parse(await response.Content.ReadAsStringAsync());

            var identity = new ClaimsIdentity("Dropbox");
            identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, payload.Value<string>("uid")));

            context.SetTicket(new AuthenticationTicket(identity, new AuthenticationProperties()));
        }
    }
}

Вы можете легко подключить его через свойство AccessTokenProvider:

app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions {
    AccessTokenProvider = new DropboxAccessTokenProvider()
});

У этого есть свои собственные недостатки: он требует кэширования, чтобы избежать наводнения конечной точки Dropbox и не является правильным способом, если вы хотите принимать токены, выпущенные разными провайдерами (например, Dropbox, Microsoft, Google, Facebook).

Не говоря уже о том, что если вы предлагаете очень низкий уровень безопасности: поскольку вы не можете проверить аудиторию токена доступа (то есть участника, на который был выдан токен), вы не можете обеспечить что токен доступа был выдан клиентскому приложению, которому вы полностью доверяете, что позволяет любому стороннему разработчику использовать свои собственные токены Dropbox с вашим API без запроса согласия пользователя.

Это, очевидно, серьезная проблема безопасности, и почему вы СЛЕДУЕТ предпочитаете подход, используемый в связанном образце. Вы можете больше узнать о путаных замещениях в этой теме: fooobar.com/info/83060/....

Удачи, и не стесняйтесь, если вам все еще нужна помощь.