Asp.net Identity, Generate WebApi token OAuthGrant

2019-04-10 18:49发布

问题:

I am trying to setup a project structure so that I have a WebApi, WebUI and Domain layer. I have moved all the Asp.Net.Identity objects into the Domain layer and have also setup the ApplicationContext here too (inheriting from IdentityContext).

(I have used this tutorial and package as a base which is excellent. http://tech.trailmax.info/2014/09/aspnet-identity-and-ioc-container-registration/)

In the WebAPI layer I am able to use the Account controller correctly to login and register. However, I cannot generate an access token.

The OAuthGrantResourceOwnerCredentialsContext method internally uses

var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();

This works fine but doesnt give me the same context as my Account Controller as I am using Unity constructor injection in this to use my ApplicationUserManager from the domain.

I have tried injecting the OAuth class but I never seem to get the instance back.

Any advice?


Edit, this is what I have in Startup class in a default WebApi project.

// Configure the application for OAuth based flow
        PublicClientId = "self";
        OAuthOptions = new OAuthAuthorizationServerOptions
        {
            TokenEndpointPath = new PathString("/Token"),
            Provider = new ApplicationOAuthProvider(PublicClientId),
            AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
            AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
            AllowInsecureHttp = true
        };

So the ApplicationOAuthProvider seems to be used when getting an access token.

-- More info.

UnityConfig.cs

container.RegisterType<ApplicationDbContext>(); //this is referencing my domain layer

Startup.Auth.cs

app.CreatePerOwinContext(() => DependencyResolver.Current.GetService<ApplicationUserManager>());

// Configure the application for OAuth based flow
        PublicClientId = "self";
        OAuthOptions = new OAuthAuthorizationServerOptions
        {
            TokenEndpointPath = new PathString("/Token"),
            Provider = new ApplicationOAuthProvider(PublicClientId),
            AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
            AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
            AllowInsecureHttp = true
        };

ApplicationOAuthProvider.cs

Have injected constructor as below

public class ApplicationOAuthProvider : OAuthAuthorizationServerProvider
{
    private readonly string _publicClientId;

    private ApplicationUserManager userManager;

    public ApplicationOAuthProvider(ApplicationUserManager userManager)
    {
        this.userManager = userManager;
    }

    public ApplicationOAuthProvider(string publicClientId)
    {

        //this.userManager = userManager;

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

        _publicClientId = publicClientId;
    }

    public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
    {
        //var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>(); //PROBLEM LINE!!!

        ApplicationUser user = await userManager.FindAsync(context.UserName, context.Password);
    }
}

The problem line is shown above. This method gets called when requesting a token, and the userManager is always null.


Edit to show UnityWebApiActivator.cs

public static class UnityWebApiActivator
{
    /// <summary>Integrates Unity when the application starts.</summary>
    public static void Start() 
    {
        // Use UnityHierarchicalDependencyResolver if you want to use a new child container for each IHttpController resolution.
        // var resolver = new UnityHierarchicalDependencyResolver(UnityConfig.GetConfiguredContainer());
        var resolver = new UnityDependencyResolver(UnityConfig.GetConfiguredContainer());

        GlobalConfiguration.Configuration.DependencyResolver = resolver;
    }

    /// <summary>Disposes the Unity container when the application is shut down.</summary>
    public static void Shutdown()
    {
        var container = UnityConfig.GetConfiguredContainer();
        container.Dispose();
    }
}

回答1:

I have just create pure WebApi project with Identity, checked over the classes and not sure I understand your question correctly.

The standard VS2013 template contains this in Startup.Auth.cs:

public partial class Startup
{
    public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }

    public static string PublicClientId { get; private set; }

    // For more information on configuring authentication, please visit http://go.microsoft.com/fwlink/?LinkId=301864
    public void ConfigureAuth(IAppBuilder app)
    {
        // blah - other stuff

        PublicClientId = "self";
        OAuthOptions = new OAuthAuthorizationServerOptions
        {
            Provider = new ApplicationOAuthProvider(PublicClientId),
            // another blah
        };

        app.UseOAuthBearerTokens(OAuthOptions);

        //blah-blah-blah
    }
}

I have checked and ApplicationOAuthProvider is not used anywhere else. So no need to inject it.

Inside of this class, as you say, it calls for context.OwinContext.GetUserManager<ApplicationUserManager>() to get user manager. If you get an incorrect instance of ApplicationDbContext there, then you inject incorrect instance of ApplicationUserManager into Owin context. Do you still have a line with this:

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

Go replace it with this:

app.CreatePerOwinContext(() => DependencyResolver.Current.GetService<ApplicationUserManager>());

This should do the job - would be the best solution.

Alternatively in ApplicationOAuthProvider replace line where you get the ApplicationUserManager from OWIN context with this:

var userManager = DependencyResolver.Current.GetService<ApplicationUserManager>()

This should resolve your user manager from Unity, giving you correct DbContext.