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

Мне нужна следующая архитектура (я составил название продукта для этого примера):

Приложение Web API 2, работающее на одном сервере http://api.prettypictures.com

Клиентское приложение MVC 5 работает на другом сервере http://www.webpics.com

Я хотел бы, чтобы www.webpics.com клиентское приложение использовало API Pretty Pictures для:

  • Зарегистрируйте новые учетные записи с именем пользователя и паролем.
  • Зарегистрируйте новые учетные записи в Facebook/Google/Twitter/Microsoft.
  • Войти
  • Извлечение изображений

Все вышеперечисленные работы за исключением регистрации внешних учетных записей в Facebook, Google и т.д.

Я не могу обработать правильный поток для создания внешней учетной записи от отдельного клиента API.

Я изучил большинство документов, доступных в потоке аутентификации, например: enter image description here

Я прочитал почти все, что могу, о новой модели Identity в OWIN.

Я рассмотрел шаблон SPA в Visual Studio 2013. Он демонстрирует, как делать большую часть того, что мне нужно, но только тогда, когда клиент и API находятся на одном хосте; если я хочу, чтобы несколько клиентов обращались к моему API и могли разрешить пользователям регистрироваться через Google и т.д., это не работает, и насколько я могу сказать, что поток аутентификации OWIN ломается.

Вот поток до сих пор:

  • Пользователь просматривает www.webpics.com/Login
  • www.webpics.com вызывает api.prettypictures.com/Account/ExternalLogins returnUrl, чтобы вернуться к обратному вызову на www.webpics.com) и отображает результирующие ссылки на пользователя.
  • Пользователь нажимает кнопку "Google"
  • Браузер перенаправляет на api.prettypictures.com/Account/ExternalLogin имя поставщика и т.д.
  • Действие API ExternalLogin вызывает вызов google.com
  • Браузер перенаправляется на google.com
  • Пользователь вводит свое имя пользователя и пароль (если они еще не вошли в систему google.com)
  • google.com теперь представляет собой разрешение на безопасность: "api.prettypictures.com" хотел бы получить доступ к вашему адресу электронной почты, имени, жене, детям и т.д. Это нормально?
  • Пользователь нажимает "Да" и возвращается к api.prettypictures.com/Account/ExternalLogin с файлом cookie, установленным Google.

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

Я не знаю, как это сделать.

Фактически в этот момент браузер заканчивается на конечной точке api.prettypictures.com/Account/ExternalLogin после обратного вызова от Google. API подписан для Google, но клиент не знает, как с этим бороться. Должен ли я передать этот файл cookie на www.webpics.com?

В приложении SPA это делается через AJAX, а google.com возвращает токен в виде фрагмента URL-адреса, и все работает хорошо, потому что все это находится на одном домене. Но это бросает вызов большей части того, что "API" может использовать несколько клиентов.

Help!

Ответ 1

Обновление: все изменилось с тех пор, как я написал это сообщение в январе: MSFT выпустила свое официальное промежуточное программное обеспечение OpenID для подключения клиента, и я много работал с @manfredsteyer, чтобы адаптировать сервер авторизации OAuth2, встроенный в Katana, в OpenID connect, Эта комбинация дает гораздо более легкое и гораздо более мощное решение, которое не требует какого-либо пользовательского клиентского кода и на 100% совместимо со стандартными клиентами OAuth2/OpenID. Различные шаги, о которых я упоминал в январе, теперь можно заменить несколькими строками:

Сервер:

app.UseOpenIdConnectServer(options =>
{
    options.TokenEndpointPath = new PathString("/connect/token");
    options.SigningCredentials.AddCertificate(certificate);

    options.Provider = new CustomOpenIdConnectServerProvider();
});

Клиент:

app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
    Authority = "http://localhost:55985/",

    ClientId = "myClient",
    ClientSecret = "secret_secret_secret",
    RedirectUri = "http://localhost:56854/oidc"
});

Вы можете найти все детали (и разные образцы) в репозитории GitHub:

https://github.com/aspnet-contrib/AspNet.Security.OpenIdConnect.Server

https://github.com/aspnet-contrib/AspNet.Security.OpenIdConnect.Server/tree/dev/samples/Nancy


Джош, вы определенно на правильном пути, и ваша реализация делегированная/федеративная аутентификация кажется довольно хорошей (я полагаю, что вы использовали предопределенное промежуточное ПО OWIN от Microsoft.Owin.Security.Facebook/Google/Twitter).

Что вам нужно сделать, это создать свой собственный сервер авторизации OAuth2. У вас есть много вариантов для достижения этого, но самый простой из них - это, вероятно, подключить OAuthAuthorizationServerMiddleware к вашему классу запуска OWIN. Вы найдете его в пакете Microsoft.Owin.Security.OAuth Nuget.

Хотя лучшей практикой было бы создание отдельного проекта (часто называемого "AuthorizationServer" ), я лично предпочитаю добавлять его в свой "проект API", когда он не предназначен для использования в нескольких API (здесь вы бы вставить его в проект хостинга "api.prettypictures.com" ).

В репозитории Katana вы найдете отличный образец:

https://katanaproject.codeplex.com/SourceControl/latest#tests/Katana.Sandbox.WebServer/Startup.cs

app.UseOAuthAuthorizationServer(new OAuthAuthorizationServerOptions
{
    AuthorizeEndpointPath = new PathString("/oauth2/authorize"),
    TokenEndpointPath = new PathString("/oauth2/token"),
    ApplicationCanDisplayErrors = true,

    AllowInsecureHttp = true,

    Provider = new OAuthAuthorizationServerProvider
    {
        OnValidateClientRedirectUri = ValidateClientRedirectUri,
        OnValidateClientAuthentication = ValidateClientAuthentication,
        OnGrantResourceOwnerCredentials = GrantResourceOwnerCredentials,
    },
    AuthorizationCodeProvider = new AuthenticationTokenProvider
    {
        OnCreate = CreateAuthenticationCode,
        OnReceive = ReceiveAuthenticationCode,
    },
    RefreshTokenProvider = new AuthenticationTokenProvider
    {
        OnCreate = CreateRefreshToken,
        OnReceive = ReceiveRefreshToken,
    }
});

Не стесняйтесь просматривать весь проект, чтобы увидеть, как форма согласия авторизации была реализована с использованием простых файлов Razor. Если вы предпочитаете структуру более высокого уровня, такую ​​как ASP.NET MVC или NancyFX, создайте свой собственный контроллер AuthorizationController и Authorize (убедитесь, что вы принимаете GET и POST), и используйте Маршрутизацию атрибутов в соответствии с параметром AuthorizeEndpointPath, определенным в вашем OAuth2 (т.е. [Route("oauth2/authorize")] в моем примере, где я изменил AuthorizeEndpointPath, чтобы использовать oauth2/ в качестве базы пути).

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

Скопируйте соответствующие файлы из находящегося там репозитория Microsoft.Owin.Security.Google: https://katanaproject.codeplex.com/SourceControl/latest#src/Microsoft.Owin.Security.Google/GoogleOAuth2AuthenticationHandler.cs

Вам понадобятся GoogleOAuth2AuthenticationHandler, GoogleOAuth2AuthenticationMiddleware, GoogleOAuth2AuthenticationOptions, GoogleAuthenticationExtensions (вам нужно будет удалить первые 2 метода, соответствующие реализации Google OpenID), IGoogleOAuth2AuthenticationProvider, GoogleOAuth2ReturnEndpointContext, GoogleOAuth2AuthenticationProvider, GoogleOAuth2AuthenticatedContext и GoogleOAuth2ApplyRedirectContext. После того, как вы вставили эти файлы в свой проект, разместив "webpics.com", переименуйте их соответствующим образом и измените URL-адрес конечных точек авторизации и доступа к токенам в GoogleOAuth2AuthenticationHandler, чтобы они соответствовали тем, которые вы определили на сервере авторизации OAuth2.

Затем добавьте метод Use из вашего переименованного/настраиваемого GoogleAuthenticationExtensions в ваш класс запуска OWIN. Я предлагаю использовать AuthenticationMode.Active, чтобы ваши пользователи были напрямую перенаправлены на конечную точку авторизации API OAuth2. Таким образом, вы должны подавить "api.prettypictures.com/Account/ExternalLogins" roundtrip и позволить промежуточному программному обеспечению OAuth2 изменить 401 ответ для перенаправления клиентов на ваш API.

Удачи. И не стесняйтесь, если вам нужна дополнительная информация;)