wake-up-neo.com

Web API 2 OWIN-Bearer-Token-Authentifizierung - AccessTokenFormat null?

Ich habe ein vorhandenes ASP.NET MVC 5-Projekt und füge ihm ein Web API 2-Projekt hinzu. Ich möchte die Bearer-Token-Authentifizierung verwenden und habe das Tutorial von Hongye Sun "OWIN-Bearer-Token-Authentifizierung mit Web-API-Beispiel" und diese Frage ebenfalls befolgt.

In meiner Login -Methode ist für die Zeile Startup.OAuthBearerOptions.AccessTokenFormat.Protect(ticket); das AccessTokenFormat null. Irgendeine Idee warum?

Mein AccountController :

[RoutePrefix("api")]
public class AccountController : ApiController
{        
    public AccountController() {}

    // POST api/login
    [HttpPost]
    [Route("login")]
    public HttpResponseMessage Login(int id, string pwd)
    {
        if (id > 0) // testing - not authenticating right now
        {
            var identity = new ClaimsIdentity(Startup.OAuthBearerOptions.AuthenticationType);
            identity.AddClaim(new Claim(ClaimTypes.Name, id.ToString()));
            AuthenticationTicket ticket = new AuthenticationTicket(identity, new AuthenticationProperties());
            var currentUtc = new SystemClock().UtcNow;
            ticket.Properties.IssuedUtc = currentUtc;
            ticket.Properties.ExpiresUtc = currentUtc.Add(TimeSpan.FromMinutes(30));
            var token = Startup.OAuthBearerOptions.AccessTokenFormat.Protect(ticket);
            return new HttpResponseMessage(HttpStatusCode.OK)
            {
                Content = new ObjectContent<object>(new
                {
                    UserName = id.ToString(),
                    AccessToken = token
                }, Configuration.Formatters.JsonFormatter)
            };
        }

        return new HttpResponseMessage(HttpStatusCode.BadRequest);
    }

    // POST api/token
    [Route("token")]
    [HttpPost]
    public HttpResponseMessage Token(int id, string pwd)
    {
        // Never reaches here. Do I need this method?
        return new HttpResponseMessage(HttpStatusCode.OK);
    }
}

Startup Klasse:

public class Startup
{
    private static readonly ILog _log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
    public static OAuthBearerAuthenticationOptions OAuthBearerOptions { get; private set; }
    public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }
    public static Func<MyUserManager> UserManagerFactory { get; set; }
    public static string PublicClientId { get; private set; }

    static Startup()
    {
        PublicClientId = "MyWeb";

        UserManagerFactory = () => new MyUserManager(new UserStore<MyIdentityUser>());

        OAuthBearerOptions = new OAuthBearerAuthenticationOptions();

        OAuthOptions = new OAuthAuthorizationServerOptions
        {
            TokenEndpointPath = new PathString("/api/token"),
            Provider = new MyWebOAuthProvider(PublicClientId, UserManagerFactory),
            AuthorizeEndpointPath = new PathString("/api/login"),
            AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
            AllowInsecureHttp = true
        };
    }

    public void Configuration(IAppBuilder app)
    {         
        // Enable the application to use bearer tokens to authenticate users
        app.UseOAuthBearerTokens(OAuthOptions);
        app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
            LoginPath = new PathString("/api/login")
        });

        // Configure Web API to use only bearer token authentication.
        var config = GlobalConfiguration.Configuration;            
        config.SuppressDefaultHostAuthentication();
        config.Filters.Add(new HostAuthenticationFilter(OAuthBearerOptions.AuthenticationType));

        app.UseWebApi(config);                          
    }
}

MyIdentityUser fügt nur eine zusätzliche Eigenschaft hinzu:

public class MyIdentityUser : IdentityUser
{
    public int SecurityLevel { get; set; }
}

MyUserManager ruft meine benutzerdefinierte Benutzerauthentifizierungsmethode auf einem internen Server auf:

public class MyUserManager : UserManager<MyIdentityUser>
{
    public MyUserManager(IUserStore<MyIdentityUser> store) : base(store) { }

    public MyIdentityUser ValidateUser(int id, string pwd)
    {
        LoginIdentityUser user = null;

        if (MyApplication.ValidateUser(id, pwd))
        {
            // user = ??? - not yet implemented
        }

        return user;
    }
}   

MyWebOAuthProvider (Ich habe dies der SPA-Vorlage entnommen. Nur GrantResourceOwnerCredentials wurde geändert):

public class MyWebOAuthProvider : OAuthAuthorizationServerProvider
{
    private readonly string _publicClientId;
    private readonly Func<MyUserManager> _userManagerFactory;

    public MyWebOAuthProvider(string publicClientId, Func<MyUserManager> userManagerFactory)
    {
        if (publicClientId == null)
        {
            throw new ArgumentNullException("publicClientId");
        }

        if (userManagerFactory == null)
        {
            throw new ArgumentNullException("userManagerFactory");
        }

        _publicClientId = publicClientId;
        _userManagerFactory = userManagerFactory;
    }

    public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
    {
        using (MyUserManager userManager = _userManagerFactory())
        {
            MyIdentityUser user = null;
            var ctx = context as MyWebOAuthGrantResourceOwnerCredentialsContext;

            if (ctx != null)
            {
                user = userManager.ValidateUser(ctx.Id, ctx.Pwd);
            }                

            if (user == null)
            {
                context.SetError("invalid_grant", "The user name or password is incorrect.");
                return;
            }

            ClaimsIdentity oAuthIdentity = await userManager.CreateIdentityAsync(user,
                context.Options.AuthenticationType);
            ClaimsIdentity cookiesIdentity = await userManager.CreateIdentityAsync(user,
                CookieAuthenticationDefaults.AuthenticationType);
            AuthenticationProperties properties = CreateProperties(user.UserName);
            AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);
            context.Validated(ticket);
            context.Request.Context.Authentication.SignIn(cookiesIdentity);
        }
    }

    public override Task TokenEndpoint(OAuthTokenEndpointContext context)
    {
        ...  // unchanged from SPA template
    }

    public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
    {
        ...  // unchanged from SPA template
    }

    public override Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context)
    {
        ...  // unchanged from SPA template
    }

    public static AuthenticationProperties CreateProperties(string userName)
    {
        ...  // unchanged from SPA template
    }
}

MyWebOAuthGrantResourceOwnerCredientialsContext :

public class MyWebOAuthGrantResourceOwnerCredentialsContext : OAuthGrantResourceOwnerCredentialsContext
{
    public MyWebOAuthGrantResourceOwnerCredentialsContext (IOwinContext context, OAuthAuthorizationServerOptions options, string clientId, string userName, string password, IList<string> scope)
        : base(context, options, clientId, userName, password, scope)
    { }

    public int Id { get; set; }        
    public string Pwd { get; set; }
}

Wie wird AccessTokenFormat festgelegt? Ist das, was ich eingerichtet habe, korrekt? Ich authentifiziere mich nicht bei externen Diensten, sondern nur bei einem älteren internen Server. Vielen Dank.

18
Ionian316

Ich hatte das gleiche Problem - es lag an meiner Initialisierung in Startup ().

Wie Sie habe ich die OAuthBearerOptions in einem statischen Feld gespeichert:

OAuthBearerOptions = new OAuthBearerAuthenticationOptions();

Aber dann habe ich später fälschlicherweise eine neue Instanz derselben Klasse verwendet:

app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());  // wrong!

Offensichtlich bestand die Korrektur darin, stattdessen das statische Feld zu verwenden:

app.UseOAuthBearerAuthentication(OAuthBearerOptions);

Tatsächlich sieht es so aus, als würden Sie UseOAuthBearerAuthentication () überhaupt nicht aufrufen. Ich folgte diesem ausgezeichnete Reihe von Beiträgen von Taiseer Joudeh.

Vollständige Startup.cs:

public class Startup
{
    public static OAuthBearerAuthenticationOptions OAuthBearerOptions { get; private set; }

    public void Configuration(IAppBuilder app)
    {
        HttpConfiguration config = new HttpConfiguration();

        ConfigureOAuth(app);

        WebApiConfig.Register(config);
        app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
        app.UseWebApi(config);
    }

    public void ConfigureOAuth(IAppBuilder app)
    {
        //use a cookie to temporarily store information about a user logging in with a third party login provider
        app.UseExternalSignInCookie(Microsoft.AspNet.Identity.DefaultAuthenticationTypes.ExternalCookie);
        OAuthBearerOptions = new OAuthBearerAuthenticationOptions();

        OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions() {

            AllowInsecureHttp = true,
            TokenEndpointPath = new PathString("/token"),
            AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
            Provider = new SimpleAuthorizationServerProvider()  // see post
        };

        // Token Generation
        app.UseOAuthAuthorizationServer(OAuthServerOptions);
        app.UseOAuthBearerAuthentication(OAuthBearerOptions);

        //[Configure External Logins...]
    }
}
23
Dunc

Ich bin nicht sicher, ob Sie noch nach der Antwort auf diese Frage suchen - aber hier ist ein bisschen Code, den ich in meiner AngularJS-App verwende, um das Sicherheitstoken von meinem WebAPI2-Endpunkt abzurufen.

    $http({
        method: 'POST', url: '/token', data: { username: uName, password: uPassword, grant_type: 'password' },
        transformRequest: function (obj) {
            var str = [];
            for (var p in obj)
                str.Push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
            return str.join("&");
        }
    }).success(function (data, status, headers, config) {
        console.log("http success", data);
        accessToken.value = data.access_token;
        console.log("access token = ", accessToken.value);
    }).error(function (data, status, headers, config) {
        console.log("http error", data);
    });

Ich kann dann das accessToken im Header anderer Anforderungen übergeben, um die Authentifizierungsüberprüfung zu erhalten.

6
Paul Mouchet

Ich habe den Beispielcode entfernt, da er bei Verwendung mit der Web-API und der SPA-Vorlage zu Verwirrung führen kann. Sie sollten den Vorlagencode beibehalten, um das Token mithilfe des OAuth= Autorisierungsservers zu generieren. In Ihrem Szenario sollten Sie die Kennwortgewährung für Ressourcenbesitzer verwenden, um den Benutzer zu authentifizieren. Überprüfen Sie mein Blog auf der SPA-Vorlage Einzelheiten zum Kennwortfluss finden Sie unter http://blogs.msdn.com/b/webdev/archive/2013/09/20/understanding-security-features-in-spa-template.aspx

Anstatt eine eigene Web-API für die Anmeldung zu schreiben, müssen Sie für die Kennwortanmeldung den Endpunkt/token des OWIN OAuth Server verwenden.

0
Hongye Sun