2016-05-16 7 views
3

Ich versuche eine SSO-ähnliche Lösung zwischen 2 .Net-Anwendungen zu erstellen. Net App 1 verfügt über einen benutzerdefinierten Token-Generator und Endpunkte, um Token zu validieren, die Benutzer zurückgeben Information.Owin-Auth-Provider erstellen, der ein benutzerdefiniertes Token für ein .Net-Auth-Cookie austauscht

.Net Anwendung 2 ist mit Owin geschützt und war eine typische Standalone-App und ein Benutzer würde sich direkt mit einem Passwort und Benutzernamen anmelden.

Ich erstellte (basierend auf Passion for Coding Blog und Github) einen benutzerdefinierten Owin-Anbieter, der nach einem Token entweder in einem Authorization-Header oder als Abfrageparameter von einem Link suchen würde, die ein Benutzer auf einen Link von .Net App 1 klicken und senden würde an die .Net App 2 das Token in der Abfrage-String wie bei GET (Ich weiß, das ist nicht sicher, wir werden schließlich OpenID für das verwenden, was es wert ist, brauchen wir nur das für eine Demo). Ich bin in der Lage, das Token zu validieren und eine Identität zu erstellen und zu authentifizieren. Ich kann den Provider einfach nicht dazu bringen, ein .Net Auth Cookie zu erstellen, so dass nachfolgende Anfragen authentifiziert werden und keinen 401 Fehler erhalten.

Handler File:

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); 
    } 

    } 
} 

Middleware-Datei:

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); 
     } 
    } 
} 

Optionsdatei:

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; } 

    } 
} 

Extensions Datei:

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; 
     } 
    } 
} 

Startup File

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")); 
    } 
    } 
} 

Ich habe auch nur zu verwenden, um die benutzerdefinierten Anbieter für den Standard ist vorgesehen versucht

OAuthBearerAuthenticationProvider 

Hier ist der Code für die Plugins/Provider, die ich versucht habe ich nicht ein Präferenz, solange es keine 401-Fehler:

Provider

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); 
     } 
    } 
} 

Und die 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); 
       } 
      } 
     } 
    } 
} 

Antwort

1

Sie die Middleware in der falschen Reihenfolge zu registrieren. Das owin-Middleware-Modell arbeitet durch die Auth-Middleware, indem es eine Anweisung (AuthenticationResponseGrant) in das owin-Wörterbuch einfügt, bevor es zur vorherigen Middleware zurückkehrt. Wenn diese vorherige Middleware die externe Cookie-Middleware ist, wird ein Cookie ausgegeben. Es gibt mehr Details in my blog post. Also schalte diese zwei Zeilen:

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

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

Es gibt auch ein anderes Problem. Die AuthenticationType der Identität muss die eine der Cookie-Middleware machen. Die Methode UseExternalSignInCookie ruft intern app.SetDefaultSignInAsAuthenticationType auf. Wenn Sie also die neue ClaimsIdentity erstellen, sollten Sie nicht Some als Authentifizierungstyp verwenden, sondern das Ergebnis app.GetDefaultSignInAsAuthenticationType() über die SignInAsAuthenticationType für die Optionsklasse übermitteln. Der Aufruf an app.GetDefaultSignInAsAuthenticationType() wird normalerweise in dem Middleware-Konstruktor durchgeführt.

Verwandte Themen