IdentityServer4 Resource owner password and Win auth: unauthorized

£可爱£侵袭症+ 提交于 2019-12-13 02:53:57

问题


in the last few days I've been reading IdentityServer4 docs and putting together my sample server + sample client using Resource owner password. Now I'd like to add Windows authentication (will be done via Active Directory) in parallel, so the client app (not a web app but a desktop app) could either prompt the user for credentials or login using Windows authentication via Active Directory.

The documentation about Windows Authentication explains how to configure IIS or HTTP.Sys, but what I want is to:

  1. user opens the app
  2. the app use single sign on to post a request to the web api to request token and refresh token
  3. the web api uses windows authentication to validate the identity of the user and returns the token

I've tried to follow this answer, but it doesn't work (It returns unauthorized).

web api: Startup.cs

    public class Startup
    {

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvcCore()
                .AddAuthorization()
                .AddJsonFormatters();


            services.AddAuthentication("Bearer")    
            .AddJwtBearer(options =>
            {
                // base-address of your identityserver
                options.Authority = "http://localhost:5000";
                options.RequireHttpsMetadata = false;
                // name of the API resource
                options.Audience = "api/user";
            });

            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

            services.AddIdentityServer(options => { options.PublicOrigin = "http://localhost:5000"; options.MutualTls.Enabled = false; })                
                .AddExtensionGrantValidator<WinAuthGrantValidator>()
                .AddDeveloperSigningCredential()
                .AddTestUsers(Config.GetUsers())
                .AddInMemoryApiResources(Config.GetApiResources())
                .AddInMemoryClients(Config.GetClients())
                .AddInMemoryIdentityResources(Config.GetIdentityResources());

        }

        public void Configure(IApplicationBuilder app, IHostingEnvironment env,
            ILogger<Startup> logger, IServer server)
        {
            app.Use(async (context, next) =>
            {
                context.Features.Get<IHttpMaxRequestBodySizeFeature>()
                    .MaxRequestBodySize = 10 * 1024;

                var serverAddressesFeature =
                    app.ServerFeatures.Get<IServerAddressesFeature>();
                var addresses = string.Join(", ", serverAddressesFeature?.Addresses);

                logger.LogInformation($"Addresses: {addresses}");

                await next.Invoke();

            });
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Error");

                app.UseHsts();
            }

            // Enable HTTPS Redirection Middleware when hosting the app securely.
            //app.UseHttpsRedirection();
            app.UseStaticFiles();
            app.UseCookiePolicy();
            app.UseMvc();
            app.UseIdentityServer();
            app.UseAuthentication();
        }
    }

    internal static class Config
    {
        public static List<TestUser> GetUsers()
        {
            return new List<TestUser>
            {
                new TestUser
                {
                    SubjectId = "1",
                    Username = "alice",
                    Password = "password"
                },
                new TestUser
                {
                    SubjectId = "2",
                    Username = "bob",
                    Password = "password"
                }
            };
        }

        public static IEnumerable<Client> GetClients()
        {
            return new List<Client>
            {
                // other clients omitted...

                // resource owner password grant client
                new Client
                {
                    ClientId = "ro.client",
                    AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,

                    ClientSecrets =
                    {
                        new Secret("secret".Sha256())
                    },
                   // AllowedScopes = { "api1" }
                   AllowedScopes = { "api/user" }
                },
            new Client
            {
                ClientId = "winauth",
                AllowedGrantTypes =  new List<string>{ "windows_auth" },

                ClientSecrets =
                {
                    new Secret("secret".Sha256())
                },
               // AllowedScopes = { "api1" }
               AllowedScopes = { "api/user" }
             }
            };
        }

        internal static IEnumerable<ApiResource> GetApiResources()
        {
            return new List<ApiResource>
            {
                new ApiResource { Name = "api1",Scopes = new List<Scope> { new Scope {  Name = "api1",
                    DisplayName = "Full access to API 2"}  }, Enabled = true,  ApiSecrets = new List<Secret>
                    {
                        new Secret("secret".Sha256())
                    }
                },
                new ApiResource { Name = "api/user",Scopes = new List<Scope> { new Scope {  Name = "api/user",
                    DisplayName = "Full access to API 2"}  }, Enabled = true,  ApiSecrets = new List<Secret>
                    {
                        new Secret("secret".Sha256())
                    }
                }};
        }


        public static List<IdentityResource> GetIdentityResources()
        {
            return new List<IdentityResource>
            {
                new IdentityResources.OpenId(),
                new IdentityResources.Profile()
            };
        }
    }



public class WinAuthGrantValidator : IExtensionGrantValidator
    {
    private readonly HttpContext httpContext;

    public string GrantType => "windows_auth";

    public WinAuthGrantValidator(IHttpContextAccessor httpContextAccessor)
    {
        httpContext = httpContextAccessor.HttpContext;
    }

    public async Task ValidateAsync(ExtensionGrantValidationContext context)
    {
        // see if windows auth has already been requested and succeeded
        var result = await httpContext.AuthenticateAsync("Windows");
        if (result?.Principal is WindowsPrincipal wp)
        {
            context.Result = new GrantValidationResult(wp.Identity.Name, GrantType, wp.Claims);
        }
        else
        {
            // trigger windows auth
            await httpContext.ChallengeAsync("Windows");
            context.Result = new GrantValidationResult { IsError = false, Error = null, Subject = null };
        }
    }
}
}

web API: Program.cs

public class Program
{
    public static void Main(string[] args)
    {
        var isService = !(Debugger.IsAttached || args.Contains("--console"));

        if (isService)
        {
            var pathToExe = Process.GetCurrentProcess().MainModule.FileName;
            var pathToContentRoot = Path.GetDirectoryName(pathToExe);
            Directory.SetCurrentDirectory(pathToContentRoot);
        }

        var builder = CreateWebHostBuilder(
            args.Where(arg => arg != "--console").ToArray());

        var host = builder.Build();

        if (isService)
        {
            // To run the app without the CustomWebHostService change the
            // next line to host.RunAsService();
            host.RunAsCustomService();
        }
        else
        {
            host.Run();
        }
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .ConfigureLogging((hostingContext, logging) =>
            {
                logging.AddEventLog();
            })
            .ConfigureAppConfiguration((context, config) =>
            {
                // Configure the app here.
            })
            .UseStartup<Startup>()
            .UseHttpSys(options =>
            {
                options.AllowSynchronousIO = true;
                options.Authentication.Schemes = Microsoft.AspNetCore.Server.HttpSys.AuthenticationSchemes.Kerberos | Microsoft.AspNetCore.Server.HttpSys.AuthenticationSchemes.NTLM;
                options.Authentication.AllowAnonymous = true;
                options.MaxConnections = null;
                options.MaxRequestBodySize = 30000000;
                options.UrlPrefixes.Add("http://localhost:5000");
            });
}

web API UserController.cs

[Route("api/[controller]")]
[Authorize(AuthenticationSchemes = "Bearer")]
[ApiController]
public class UserController : ControllerBase
{
    // GET api/user
    [HttpGet]
    public ActionResult<IEnumerable<string>> Get()
    {
        return new string[] { User.Identity.Name, User.Identity.AuthenticationType };
    }
}

client code:

using (var client = new HttpClient())
{
    disco = await client.GetDiscoveryDocumentAsync(new   DiscoveryDocumentRequest
    {
        Address = baseUrl,
        Policy = { RequireHttps = false }
    });
    if (disco.IsError)
    {
        Console.WriteLine(disco.Error);
        Console.ReadLine();
        return;
     }
     var httpHandler = new HttpClientHandler
     {
         UseDefaultCredentials = true,
     };

     using (var client = new HttpClient())
     {
         // request token
         TokenResponse tokenResponse = await client.RequestTokenAsync(new TokenRequest
         {
             GrantType = "windows_auth",
             Address = disco.TokenEndpoint,
             ClientId = "winauth",
             ClientSecret = "secret"
         });

         if (tokenResponse.IsError)
         {
             Console.WriteLine(tokenResponse.Error);
             Console.ReadLine();
            return;
         }
     }

It returns unauthorized.


回答1:


I found a solution to this: I need to configure a TestUser with SubjectId = MYDOMAIN\myusername then it worked.

The error was getting was overly confusing.



来源:https://stackoverflow.com/questions/57273372/identityserver4-resource-owner-password-and-win-auth-unauthorized

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!