2015-02-12 2 views
12

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

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

Я использую ASP.NET по умолчанию Web API в Visual Studio шаблон с внешней аутентификацией вместе с пакетом Nuget OWin.Security.Providers для функций входа в 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 где-то похоронен). Кроме того, я думаю, мне нужен способ отдельно запросить токен у третьей стороны на основе кода авторизации.

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

Registering Web API 2 external logins from multiple API clients with OWIN Identity

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

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

ответ

4

Чтобы быть ясным, образец, упомянутый в приведенной ссылке, можно использовать с любым клиентом 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) с принятым токеном, чтобы проверить его и создать соответствующий экземплярдля каждого запроса, полученного вашим 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 без необходимости запрашивать согласие пользователя.

Это, очевидно, серьезная проблема безопасности, и именно поэтому вы должны использовать. Вы можете больше узнать о путаных заместительных атаках по этой теме: https://stackoverflow.com/a/17439317/542757.

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

+0

Благодарим за помощь! Простите, я немного новичок в OAuth и до сих пор смущен. Как это разделяет части аутентификации и авторизации? То есть, мне нужны 2 части: 1) Учитывая, что имя поставщика (строка) передано от клиента, как мне вернуть URL-адрес аутентификации клиенту? ... и 2) Учитывая код авторизации, который клиент передает api, как я могу отправить его вместе со встроенным кодом OAuth, чтобы завершить авторизацию и вернуть токен? – mayabelle

+0

Я думаю, что ваш ответ охватывает способы обработки последующих вызовов после того, как клиент уже имеет токен и отправил токен в api для выполнения операции. Это очень полезно, так как это будет моим следующим шагом. Но прямо сейчас я застрял в предыдущей части, поток, чтобы получить маркер для клиента в первую очередь (клиент отправляет имя провайдера api, api возвращает URL-адрес Dropbox, клиент переходит к URL-адресу Dropbox, где пользователь вводит учетные данные и получает обратный код авторизации, клиент отправляет auth-код в api, api вызывает Dropbox для обмена кодом для токена, api возвращает токен клиенту). – mayabelle

+0

Хорошо, спасибо за разъяснение. Как бы это выглядело, если мне нужно было использовать поток кода? Будет ли это вариант 2?Если да, то как это выглядит? – mayabelle

Смежные вопросы