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

Приложение MVC 5 - реализует поток кода авторизации OAuth

На основе этого урока http://www.asp.net/aspnet/overview/owin-and-katana/owin-oauth-20-authorization-server я создал сервер авторизации, сервер ресурсов и клиент MVC. Клиент MVC имеет контроллер, который получает некоторые данные с сервера ресурсов. Сервер ресурсов требует аутентификации. Клиенты MVC получают код авторизации с сервера авторизации и перенаправляют пользователя на сервер авторизации для аутентификации. Наконец, клиенты MVC обменивают код авторизации для токена доступа для доступа к серверу ресурсов. Это поток кода авторизации, как описано в протоколе OAuth 2. Это прекрасно работает.

Теперь у меня есть требование, чтобы сам контроллер MVC-клиента требовал аутентификации. Я не могу найти учебник для этого.

Я добавил

app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());

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

app.UseOAuthBearerAuthentication(new Microsoft.Owin.Security.OAuth.OAuthBearerAuthenticationOptions()
{
    Provider = new OAuthBearerAuthenticationProvider()
});

Но я также настаиваю на реализации событий Провайдера. Может ли кто-нибудь вести меня в правильном направлении? Или есть какие-то учебники, которые могут мне помочь?

4b9b3361

Ответ 1

В итоге у меня появилось решение, основанное на этих двух статьях из Brock Allen:

Основная идея состоит в том, чтобы зарегистрировать две аутентификации Middlewares. Активная аутентификация Cookie и пассивная аутентификация OAuthBearer. В Startup.Auth.cs они добавляются следующим образом:

app.UseCookieAuthentication(new CookieAuthenticationOptions()
{
    AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
    LoginPath = new PathString("/ExternalLogin/Login"),
});
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions()
{
    AuthenticationType = DefaultAuthenticationTypes.ExternalBearer,
    AuthenticationMode = Microsoft.Owin.Security.AuthenticationMode.Passive,
});

Вы также добавляете ExternalLogin-Controller. Его метод входа в систему должен перенаправить пользователя на страницу авторизации вашего авторизационного сервера, чтобы получить код авторизации. Вы должны предоставить функцию обратного вызова, в которой вы будете обрабатывать код авторизации.

public async Task<ActionResult> Login(string returnUrl)
{
    if (string.IsNullOrEmpty(returnUrl) && Request.UrlReferrer != null)
        returnUrl = Server.UrlEncode(Request.UrlReferrer.PathAndQuery);

    if (Url.IsLocalUrl(returnUrl) && !string.IsNullOrEmpty(returnUrl))
        _returnUrl = returnUrl;

    //callback function
    _redirectUrl = Url.Action("AuthorizationCodeCallback", "ExternalLogin", null, Request.Url.Scheme);

    Dictionary<string, string> authorizeArgs = null;
    authorizeArgs = new Dictionary<string, string>
    {
        {"client_id", "0123456789"}
        ,{"response_type", "code"}
        ,{"scope", "read"}
        ,{"redirect_uri", _redirectUrl}
        // optional: state
    };

    var content = new FormUrlEncodedContent(authorizeArgs);
    var contentAsString = await content.ReadAsStringAsync();
    return Redirect("http://localhost:64426/oauth/authorize?" + contentAsString);
}

В вашей функции обратного вызова вы обмениваете код авторизации для токена доступа (плюс токен обновления), чтобы заразить ваше пассивное промежуточное ПО аутентификации OAuthBearer и подписаться с токеном доступа в качестве вашего Cookie.

public async Task<ActionResult> AuthorizationCodeCallback()
{
    // received authorization code from authorization server
    string[] codes = Request.Params.GetValues("code");
    var authorizationCode = "";
    if (codes.Length > 0)
        authorizationCode = codes[0];

    // exchange authorization code at authorization server for an access and refresh token
    Dictionary<string, string> post = null;
    post = new Dictionary<string, string>
    {
        {"client_id", "0123456789"}
        ,{"client_secret", "ClientSecret"}
        ,{"grant_type", "authorization_code"}
        ,{"code", authorizationCode}
        ,{"redirect_uri", _redirectUrl}
    };

    var client = new HttpClient();
    var postContent = new FormUrlEncodedContent(post);
    var response = await client.PostAsync("http://localhost:64426/token", postContent);
    var content = await response.Content.ReadAsStringAsync();

    // received tokens from authorization server
    var json = JObject.Parse(content);
    _accessToken = json["access_token"].ToString();
    _authorizationScheme = json["token_type"].ToString();
    _expiresIn = json["expires_in"].ToString();
    if (json["refresh_token"] != null)
        _refreshToken = json["refresh_token"].ToString();

    //SignIn with Token, SignOut and create new identity for SignIn
    Request.Headers.Add("Authorization", _authorizationScheme + " " + _accessToken);
    var ctx = Request.GetOwinContext();
    var authenticateResult = await ctx.Authentication.AuthenticateAsync(DefaultAuthenticationTypes.ExternalBearer);
    ctx.Authentication.SignOut(DefaultAuthenticationTypes.ExternalBearer);
    var applicationCookieIdentity = new ClaimsIdentity(authenticateResult.Identity.Claims, DefaultAuthenticationTypes.ApplicationCookie);
    ctx.Authentication.SignIn(applicationCookieIdentity);

    var ctxUser = ctx.Authentication.User;
    var user = Request.RequestContext.HttpContext.User;

    //redirect back to the view which required authentication
    string decodedUrl = "";
    if (!string.IsNullOrEmpty(_returnUrl))
        decodedUrl = Server.UrlDecode(_returnUrl);

    if (Url.IsLocalUrl(decodedUrl))
        return Redirect(decodedUrl);
    else
        return RedirectToAction("Index", "Home");
}

Я надеюсь, что это полезно для тех, кто внедряет поток кода авторизации OAuth в своем приложении MVC 5.

Ответ 2

Я использовал официальный образец MVC Implicit Client, который, я считаю, является правильным потоком аутентификации для приложения MVC.

Для авторизации я использовал этот начало работы, особенно часть о бесконечном цикле, когда указаны роли [Authorize(Roles = "Foo,Bar")], и пользователь аутентифицирован, но doesn ' т любой из них.