Внешняя аутентификация Owin без файлов cookie или локальных учетных данных

Я работаю над кросс-платформенным веб-приложением, используя angular и webapi. Проблема в том, что приложение angular работает в контейнере Cordova. Чтобы хорошо работать с остальными приложениями на устройстве, мне необходимо использовать плагин для SSO. Этот плагин вызывает у меня проблемы, потому что он делает несколько вещей. Он перехватывает все HTTP-запросы и добавляет в заголовок токен-носитель, который генерируется сторонним поставщиком токенов, поэтому я не могу его декодировать и перезаписывает любой токен-носитель, который я установил в заголовке. печенье..

Поэтому это немного усложняет ситуацию, когда вы не можете отправить свои собственные локальные учетные данные.

Итак, я начал с https://coding.abel.nu/2014/06/writing-an-owin-authentication-middleware/ и http://katanaproject.codeplex.com/SourceControl/latest#src/Microsoft.Owin.Security.OAuth/OAuthBearerAuthenticationHandler.cs

Поэтому я решил, что должен написать собственное промежуточное ПО, чтобы позаботиться об этом; Я подумал, что, поскольку стандартное промежуточное ПО oauth может работать без файлов cookie, мне не нужно слишком много времени, чтобы заставить свое немного отличающееся промежуточное ПО токена носителя сделать это. Но это не так... Написание собственного промежуточного ПО... поэтому я Я могу получить заголовок, проверить с помощью внешнего поставщика токенов, но я не могу войти в систему.

   protected override async Task<AuthenticationTicket> AuthenticateCoreAsync()
        {
            try
            {
                // Find token in default location
                string requestToken = null;
                string authorization = Request.Headers.Get("Authorization");
                if (!string.IsNullOrEmpty(authorization))
                {
                    if (authorization.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
                    {
                        requestToken = authorization.Substring("Bearer ".Length).Trim();
                    }
                }
.... Take the Request token call other Server, verify token...

Также

    public override async Task<bool> InvokeAsync()
    {
         var ticket = await this.AuthenticateAsync();
         if(ticket != null)
         {
           this.Context.Authentication.SignIn(new AuthenticationProperties(), grantIdentity);
           return false;
         }
    }

Так что, в конце концов, SignIn не вызывает ошибки или чего-то еще, но фактически не входит в систему. Как только я перехожу к действию контроллера с атрибутом [Authorize], я получаю ошибку 401. У меня не включены внешние файлы cookie. Есть большая вероятность, что я на неправильном пути или слишком усложняю задачу.


person Jon Z    schedule 27.08.2015    source источник
comment
Я обнаружил, что проще просто переопределить атрибут авторизации. Я задал аналогичный вопрос, и этот парень дал мне следующую ссылку (BitOfTech.net). Пожалуйста, напишите, если вы разобрались с этим (stackoverflow.com/questions/32099027 /)   -  person Mr. B    schedule 27.08.2015
comment
@Mr.B - Эй, посмотри мой ответ .. Наконец-то я смог это сделать.   -  person Jon Z    schedule 09.09.2015


Ответы (2)


Ты слишком усердно это делаешь.

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

Вот пример отправки токена в строке запроса.

//in Startup class
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions
{
    Provider = new QueryStringOAuthBearerProvider(),
    //your settings
});

//implementation
public class QueryStringOAuthBearerProvider : OAuthBearerAuthenticationProvider
{
    private const string AccessTokenQueryKey = "access_token";

    public override Task RequestToken(OAuthRequestTokenContext context)
    {
        //check if token found in the default location - "Authorization: Bearer <token>" header
        if (string.IsNullOrEmpty(context.Token))
        {
            var token = context.Request.Query.Get(AccessTokenQueryKey);

            if (!string.IsNullOrEmpty(token))
            {
                context.Token = token;
            }
        }

        return Task.FromResult<object>(null);
    }
}
person Angel Yordanov    schedule 07.09.2015

Итак… Я собирался ответить на него раньше, но смог разобраться, не переопределяя атрибут авторизации. В итоге я посмотрел на источник кода безопасности OWIN. Хитрость в том, что вам действительно нужны 2 компонента промежуточного программного обеспечения OWIN. Один из них — это то, что я называю (и я украл это из источника owin) промежуточным программным обеспечением сервера. Промежуточное программное обеспечение сервера отвечает на вызов и / или, если вы чувствуете себя сумасшедшим, генерирует для вас локальные учетные данные. Это промежуточное ПО также является ПАССИВНЫМ компонентом промежуточного ПО. Я не буду генерировать локальные учетные данные, если кто-то не спросит, потому что это немного не по делу, но если кто-то считает, что это будет полезно, я могу обновить.

public class LowCalorieAuthenticationServerHandler : AuthenticationHandler<LowCalorieAuthenticationServerOptions>
{
    //Important this needs to be overriden, but just calls the base. 
    protected override Task<AuthenticationTicket> AuthenticateCoreAsync()
    {
        return Task.FromResult<AuthenticationTicket>(null);
    }

    /// <summary>The apply response challenge async.</summary>
    /// <returns>The <see cref="Task"/>.</returns>
    protected override async Task ApplyResponseChallengeAsync()
    {
        if (this.Response.StatusCode != 401)
        {
            Task.FromResult<object>(null);
            return;
        }

        var challenge = this.Helper.LookupChallenge(
            this.Options.AuthenticationType,
            this.Options.AuthenticationMode);
        if (challenge != null)
        {
            //OK in here you call the rediret to the 3rd party 
            //return a redirect to some endpoint
        }
        Task.FromResult<object>(null);
        return;
    }
}

В любом случае обратите внимание, как переопределение AuthenticateCoreAsync() просто возвращает return Task.FromResult(null); Это потому, что мы не хотим, чтобы это промежуточное ПО модифицировало запрос. ApplyResponseChallengeAsync дождется вызова и перенаправит вас на сторонний вход. ЕСЛИ вы хотите создать какой-то локальный токен, вы должны переопределить метод InvokeAsync.

Второе промежуточное ПО, которое вам нужно, — это валидатор токенов/внешних учетных данных. Затем это каким-то образом аутентифицирует пользователя. В случае токена локального носителя, встроенного в систему безопасности OWIN, он просто десериализует токен и, если это возможно, и срок действия токена не истек, он аутентифицирует пользователя. Итак, в случае, если вы хотите проверить токен с помощью 3-й части SSO, такой как Google или что-то еще, вы вставляете здесь свою логику. В моем случае я не только хотел позвонить стороннему провайдеру, чтобы получить информацию о пользователе, но и проверить, действителен ли их токен для единого выхода, и предотвратить несколько сеансов.

public class LowCalorieAuthenticationHandler : AuthenticationHandler<LowCalorieAuthenticationOptions>
{

    //Going to give you the user for the request.. You Need to do 3 things here
    //1. Get the user claim from teh request somehow, either froma header, request string, or cookie what ever you want
    //2. validate the user with whatever user store or 3rd party SSO you want
    //3. Generate a AuthenticationTicket to send to on to the request, you can use that to see if the user is valid in any Identity collection you want.  
    protected override async Task<AuthenticationTicket> AuthenticateCoreAsync()
    {




        //Good to throw in a point of override here.. but to keep it simple-ish
        string requestToken = null;
        string authorization = Request.Headers.Get("Authorization");

        //TOTAL FAKEOUT.. I am going to add a bearer token just so the simple sample works, but your client would have to provide this
        authorization = "Bearer  1234567869";

        //STEP 1 
        if (!string.IsNullOrEmpty(authorization) && authorization.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
        {
            requestToken = authorization.Substring("Bearer ".Length).Trim();
            return await FakeExternalBearer(requestToken);
        }

        return null;
    }

    private async Task<AuthenticationTicket> FakeExternalBearer(string token)
    {
        var authenticationType = Options.AuthenticationType;
        //pretend to call extenal Resource server to get user //STEP 2
        //CallExternal(token)

        //Create the AuthTicket from the return.. I will fake it out
        var identity = new ClaimsIdentity(
                            authenticationType,
                            ClaimsIdentity.DefaultNameClaimType,
                            ClaimsIdentity.DefaultRoleClaimType);

        identity.AddClaim(new Claim(ClaimTypes.NameIdentifier,"user1", null, authenticationType));
        identity.AddClaim(new Claim(ClaimTypes.Name, "Jon",null, authenticationType));

        var properties = new AuthenticationProperties();
        properties.ExpiresUtc = DateTime.UtcNow.AddMinutes(1);
        properties.IssuedUtc = DateTime.UtcNow;

        var ticket =  new AuthenticationTicket(identity, properties);
        return ticket;
    }
}

Хорошо, здесь мы переопределяем AuthenticateCoreAsync, но на самом деле мы сейчас что-то делаем. Это была ваша аутентификация пользователя. Это АКТИВНАЯ часть промежуточного программного обеспечения. Обратите внимание, что он должен вернуть действительный AuthenticationTicket. Это будет выполняться при каждом запросе, поэтому будьте осторожны с тем, что вы звоните и как часто. Итак, у меня есть очень простой пример здесь https://github.com/jzoss/LowCalorieOwin Если кому-то интересно подробнее спрашивайте. Я могу добавить больше. Я сделал это слишком сложно, потому что теперь, когда я это понимаю, это довольно просто, но на самом деле нет хороших примеров того, как это сделать.

person Jon Z    schedule 09.09.2015