Создание провайдера Autin Auth, который обменивает пользовательский токен на .Net Auth Cookie

Я пытаюсь создать SSO-подобное решение между 2 приложениями .Net. В приложении 1 есть собственный генератор токенов и конечные точки для проверки токенов, которые возвращают информацию о пользователе.

Приложение .Net 2 защищено с использованием Owin и является типичным автономным приложением, и пользователь может напрямую войти в систему, используя пароль и имя пользователя.

Я создал (на основеСтрасть к кодированию блога а такжеGithub) пользовательский поставщик Owin, который будет искать токен либо в заголовке авторизации, либо в качестве параметра запроса по ссылке, по которой пользователь будет щелкать ссылку в .Net App 1 и отправлять в .Net App 2 токен в строке запроса. как в GET (я знаю, что это небезопасно, мы в конечном итоге собираемся использовать OpenID для того, что стоит, нам просто нужно это для демонстрации). Я могу получить токен, проверить его, создать удостоверение и аутентификацию. Я просто не могу заставить провайдера создать .Net Auth Cookie, чтобы последующие запросы аутентифицировались и не выдавали ошибку 401.

Файл обработчика:

using Microsoft.Owin.Infrastructure;
using Microsoft.Owin.Logging;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Infrastructure;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;

namespace SomeOAuth
{
// Created by the factory in the someAuthenticationMiddleware class.
class SomeAuthenticationHandler : AuthenticationHandler<SomeAuthenticationOptions>
{
    private const string HandledResponse = "HandledResponse";

    private readonly ILogger _logger;
    private readonly string _challenge;

    /// <summary>
    /// Creates a new OpenIdConnectAuthenticationHandler
    /// </summary>
    /// <param name="logger"></param>
    public SomeAuthenticationHandler(ILogger logger, string challenge)
    {
        _logger = logger;
        _challenge = challenge;
    }


    protected override async Task<AuthenticationTicket> AuthenticateCoreAsync()
    {
        // ASP.Net Identity requires the NameIdentitifer field to be set or it won't  
        // accept the external login (AuthenticationManagerExtensions.GetExternalLoginInfo)
        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();
            }
        }

        if (string.IsNullOrEmpty(requestToken))
        {
            string accessTokenParam = Request.Query.Get("access_token");
            if (!string.IsNullOrEmpty(accessTokenParam))
            {
                requestToken = accessTokenParam;
            }
        }

        if (!string.IsNullOrEmpty(requestToken))
        {
            using (var client = new HttpClient())
            {
                try
                {
                    var request = new HttpRequestMessage(HttpMethod.Post, "https://testserver/API/Auth/Authenticate");
                    var s = new StringContent("{\"oauthtoken\":\"" + requestToken + "\"}", Encoding.UTF8, "application/json");
                    // var ts = s.ToString();
                    request.Content = new StringContent("{\"oauthtoken\":\"" + requestToken + "\"}", Encoding.UTF8, "application/json");

                    System.Diagnostics.Debug.WriteLine("Request:");
                    System.Diagnostics.Debug.WriteLine(request.ToString());
                    if (request.Content != null)
                    {
                        System.Diagnostics.Debug.WriteLine(await request.Content.ReadAsStringAsync());
                    }
                    System.Diagnostics.Debug.WriteLine("");

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

                    var payload = JObject.Parse(await response.Content.ReadAsStringAsync());
                    var userId = payload.Value<string>("username");

                    //need to get the useid of the user as well as the name and role

                    var identity = new ClaimsIdentity("Some");
                    identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, "fakeuser", null, "Some"));
                    /*
                    identity.AddClaim(new Claim(ClaimTypes.GivenName, user.FirstName + " " + user.LastName));
                    identity.AddClaim(new Claim(ClaimTypes.Email, user.ContactInfo.Email));
                    identity.AddClaim(new Claim(ClaimTypes.Sid, user.Guid.ToString()));
                    */
                    identity.AddClaim(new Claim("http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider", "Some"));
                    AuthenticationProperties properties = CreateProperties("fakeusername", "");
                    var ticket = new AuthenticationTicket(identity, new AuthenticationProperties());
                    return ticket;
                }
                catch (Exception e)
                {
                    Console.WriteLine("asdf e = " + e.Message);
                }
                return null;
            }
        }
        else
        {
            return null;
        }
    }

    /// <summary>
    /// Handles SignIn
    /// </summary>
    /// <returns></returns>
    protected override Task ApplyResponseChallengeAsync()
    {

        if (Response.StatusCode == 401)
        {
            AuthenticationResponseChallenge challenge = Helper.LookupChallenge(Options.AuthenticationType, Options.AuthenticationMode);
            if (challenge == null)
            {
                return null;
            }
        }

        return Task.FromResult<object>(null);
    }




    public override Task<bool> InvokeAsync()
    {
        return InvokeReplyPathAsync();
    }

    private async Task<bool> InvokeReplyPathAsync()
    {
        AuthenticationTicket ticket = await AuthenticateAsync();

        if (ticket != null)
        {
            string value;
            if (ticket.Properties.Dictionary.TryGetValue(HandledResponse, out value) && value == "true")
            {
                return true;
            }
            if (ticket.Identity != null)
            {
                Request.Context.Authentication.SignIn(ticket.Properties, ticket.Identity);
            }
            // Redirect back to the original secured resource, if any.
            if (!string.IsNullOrWhiteSpace(ticket.Properties.RedirectUri))
            {
                Response.Redirect(ticket.Properties.RedirectUri);
                return true;
            }
        }

        return false;
    }

    private static AuthenticationTicket GetHandledResponseTicket()
    {
        return new AuthenticationTicket(null, new AuthenticationProperties(new Dictionary<string, string>() { { HandledResponse, "true" } }));
    }

    public AuthenticationProperties CreateProperties(string userName, string Roles)
    {
        IDictionary<string, string> data = new Dictionary<string, string>
    {
        { "userName", userName },
        {"roles",Roles}
    };
        return new AuthenticationProperties(data);
    }

  }
}

Файл промежуточного программного обеспечения:

using Microsoft.Owin;
using Microsoft.Owin.Security.Infrastructure;
using Owin;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.DataProtection;
using Microsoft.Owin.Security.DataHandler;
using Microsoft.Owin.Logging;

namespace SomeOAuth
{
    // One instance is created when the application starts.
    public class SomeeAuthenticationMiddleware : AuthenticationMiddleware<SomeAuthenticationOptions>
    {
        private readonly ILogger _logger;
        private readonly string _challenge = "Bearer";

        public SomeAuthenticationMiddleware(OwinMiddleware next, IAppBuilder app, SomeAuthenticationOptions options)
            : base(next, options)
        {

            _logger = app.CreateLogger<SomeAuthenticationMiddleware>();


        }

        // Called for each request, to create a handler for each request.
        protected override AuthenticationHandler<SomeAuthenticationOptions> CreateHandler()
        {
            return new SomeAuthenticationHandler(_logger, _challenge);
        }
    }
}

Файл опций:

using Microsoft.Owin;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.OAuth;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SomeOAuth
{
    public class SomeAuthenticationOptions : AuthenticationOptions
    {
        public SomeAuthenticationOptions(string userName, string userId)
            : base(OAuthDefaults.AuthenticationType)
        {
            UserName = userName;
            UserId = userId;
        }

        public string Challenge { get; set; }

        public string UserName { get; set; }

        public string UserId { get; set; }

    }
}

Файл расширений:

using Microsoft.Owin.Extensions;
using Owin;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SomeOAuth
{

    public static class SomeAuthenticationExtensions
    {
        public static IAppBuilder UseSomeeAuthentication(this IAppBuilder app, SomeAuthenticationOptions options)
        {
            if (app == null)
            {
                throw new ArgumentNullException("app");
            }

            app.Use(typeof(SomeAuthenticationMiddleware), app, options);
            app.UseStageMarker(PipelineStage.Authenticate);

            return app;
        }
    }
}

Файл запуска

using System;
using CoreLX.Palms.VS.Web.Services;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin;
using Microsoft.Owin.Security.Cookies;
using Owin.Security.Providers.OpenID;
using Microsoft.Owin.Security.OAuth;
using Owin;
using SomeOAuth;
using CoreLX.Palms.LS.Web.Common.Models.User;

namespace CoreLX.Palms.VS.Web
{
    public partial class Startup
    {

        public void ConfigureAuth(IAppBuilder app)
        {

            app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
        app.CreatePerOwinContext<ApplicationSignInManager>    (ApplicationSignInManager.Create);

            //app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions
            //{
            //    AccessTokenProvider = new SomeTokenProvider(),
            //    Provider = new SomeOAuthBearerAuthenticationProvider("access_token")
            //});

            app.UseSomeAuthentication(new SomeAuthenticationOptions("testuser", "9"));

            // Use a cookie to temp store information about a user     logging in with a third party login provider
            app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);

            // Enable the application to use a cookie to store information for the signed in user
            app.UseCookieAuthentication(
                new CookieAuthenticationOptions
                {
                    AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
                    LoginPath = new PathString("/Account/Login"),
                    ExpireTimeSpan = new TimeSpan(0, 3, 0, 0),
                    Provider = new CookieAuthenticationProvider
                    {
                        OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
                        validateInterval: TimeSpan.FromMinutes(30),
                        regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager)),
                        OnApplyRedirect = ctx =>
                                {
                                // don't redirect to login page for webapi/ajax requests
                                // http://brockallen.com/2013/10/27/using-cookie-authentication-middleware-with-web-api-and-401-response-codes/
                                if (!IsWebApiRequest(ctx.Request))
                                {
                                    ctx.Response.Redirect(ctx.RedirectUri);
                                }
                            }
                    }
                });



            app.UseOpenIDAuthentication("http://me.yahoo.com/", "Yahoo");



        }


        private static bool IsWebApiRequest(IOwinRequest request)
        {
            // hack for check if it's webapi requesr
            if (request.Path.StartsWithSegments(new PathString("/api")))
            {
            return true;
            }

            // checks if it's ajax request
            IReadableStringCollection query = request.Query;
            if ((query != null) && (query["X-Requested-With"] == "XMLHttpRequest"))
            {
                return true;
            }
            IHeaderDictionary headers = request.Headers;
            return ((headers != null) && (headers["X-Requested-With"] ==     "XMLHttpRequest"));
    }
    }
}

Я также пытался просто использовать пользовательских провайдеров для предоставленного стандарта

OAuthBearerAuthenticationProvider

Вот код для плагинов / провайдеров, которые я попробовал, у меня нет предпочтений, если нет ошибок 401:

поставщик

using Microsoft.Owin.Security.OAuth;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SomeOAuth
{
    public class SomeOAuthBearerAuthenticationProvider : IOAuthBearerAuthenticationProvider
    {
        readonly string _parameterName;
        public SomeOAuthBearerAuthenticationProvider(string parameterName)
        {
            _parameterName = parameterName;
        }
        public Task ApplyChallenge(OAuthChallengeContext context)
        {
            return Task.FromResult<object>(null);
        }

        public Task RequestToken(OAuthRequestTokenContext context)
        {
            string token = context.Token;
            if(string.IsNullOrEmpty(token) &&     !string.IsNullOrEmpty(_parameterName))
            {
                token = context.Request.Query.Get(_parameterName);
            }

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

            return Task.FromResult<object>(null);
        }

        public Task ValidateIdentity(OAuthValidateIdentityContext context)
        {
            context.Validated();
            return Task.FromResult<object>(null);
        }
    }
}

И AccessTokenProvider

using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Infrastructure;
using Newtonsoft.Json.Linq;
using System;
//using Newtonsoft.Json.Linq;
using System.Net;
using System.Net.Http;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;


namespace SomeOAuth
{
    public sealed class SomeTokenProvider : AuthenticationTokenProvider
    {
        public override async Task ReceiveAsync(AuthenticationTokenReceiveContext context)
        {
            using (var client = new HttpClient())
            {
                try
                {
                    var request = new HttpRequestMessage(HttpMethod.Post, "https://someserver/API/Auth/Authenticate");
                    var s = new StringContent("{\"oauthtoken\":\"" + context.Token + "\"}", Encoding.UTF8, "application/json");
                    // var ts = s.ToString();
                    request.Content = new StringContent("{\"oauthtoken\":\"" + context.Token + "\"}", Encoding.UTF8, "application/json");

                    System.Diagnostics.Debug.WriteLine("Request:");
                    System.Diagnostics.Debug.WriteLine(request.ToString());
                    if (request.Content != null)
                    {
                        System.Diagnostics.Debug.WriteLine(await request.Content.ReadAsStringAsync());
                    }
                    System.Diagnostics.Debug.WriteLine("");

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

                    var payload = JObject.Parse(await response.Content.ReadAsStringAsync());
                    var userId = payload.Value<string>("username");

                    //need to get the useid of the user as well as the name and role

                    var identity = new ClaimsIdentity("Some");
                    identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, "someuser", null, "Some"));
                /*
                    identity.AddClaim(new Claim(ClaimTypes.GivenName, user.FirstName + " " + user.LastName));
                    identity.AddClaim(new Claim(ClaimTypes.Email, user.ContactInfo.Email));
                    identity.AddClaim(new Claim(ClaimTypes.Sid, user.Guid.ToString()));
                    */
                    identity.AddClaim(new Claim("http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider", "Some"));
                    context.SetTicket(new AuthenticationTicket(identity, new AuthenticationProperties()));
                }
                catch (Exception e)
                {
                    Console.WriteLine("asdf e = " + e.Message);
                }
            }
        }
    }
}

Ответы на вопрос(1)

Ваш ответ на вопрос