Windows authentication returns 403 - Forbidden - kerberos

I have 2 systems
web which runs as windows service on kestrel
web api which also runs as windows service on kestrel, but runs on another machine
In web aplication i have windows authentication which works fine, but when i call web api (from web app) which also uses windows authentication, call ends in 403 error. This happens only when web and web api are on different machines, when they are on same machines call succeeds.
Call on web app is impersonated to logged user.
controller in web app:
[Authorize(AuthenticationSchemes = NegotiateDefaults.AuthenticationScheme)]
public async Task<IActionResult> Index()
{
var data = await testService.GetData();
return View("Index",data);
}
Call from web app (TestService.GetData):
var identity = (WindowsIdentity)httpContextAccessor.HttpContext.User.Identity;
return await WindowsIdentity.RunImpersonatedAsync(identity.AccessToken, async () =>
{
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(NegotiateDefaults.AuthenticationScheme);
var result = await httpClient.GetAsync("api/auth");
return await result.Content.ReadAsStringAsync();
});
Web api controller:
[Authorize(AuthenticationSchemes = NegotiateDefaults.AuthenticationScheme)]
public string Get()
{
...
}
When call is not impersonated or both apps are on same machine, everything is correct. But i need to make call in context of signed user and apps needs to be on different machines.
I think the problem is ntlm is used onstead of kerberos and something like this is not permitted in ntlm. But how to force use kerberos, or what is the reason kerberos is not used by default?
spns are set on both machines:
setspn -S HTTPS/{servername on which app is running} {service user under which app is running}
Any idea what i should else set or what can be problem?

Related

OpenIddict in abp framework - deploy in containerized environment (k8s)

abp version 6.0, tiered (.Web, .HttpApi.Host, .AuthServer), MVC.
The following messages appear in .AuthServer.
Client validation failed because 'https://webpage_url/signin-oidc' was not a valid redirect_uri for AppName_Web.
The authorization request was rejected because the redirect_uri was invalid: 'https://webpage_url/signin-oidc'.
How to properly set appsettings.json in .Web, .HttpApi.Host, .Web and .DbMigrator projects for deployment into containerized environments?
Where should be set internal (k8s) url address for auth server and when outer url (which is accessible via internet).
Url https://webpage_url/signin-oidc in the log is outer address (which is accessible via internet).
changing appsettings.json
Client validation failed because 'https://webpage_url/signin-oidc' was
not a valid redirect_uri for AppName_Web. The authorization request
was rejected because the redirect_uri was invalid:
'https://webpage_url/signin-oidc'.
Probably your redirect uri is not seeded, You can check your database if the redirect uri is added correctly for that client (application).
How to properly set appsettings.json in .Web, .HttpApi.Host, .Web and
.DbMigrator projects for deployment into containerized environments?
Where should be set internal (k8s) url address for auth server and
when outer url (which is accessible via internet).
Url https://webpage_url/signin-oidc in the log is outer address (which
is accessible via internet).
You don't change the redirect uri based on your deployment environment. It should point to a valid reachable endpoint that the openid-provider redirects to after signin.
As far as I understand, apart from normal login flow, you are having problems when interacting to openid-provider in isolated network (k8s, docker).
Since you have auth-server on real domain endpoint (like https://my-authserver.com), you are getting error from containers that tries to reach to domain (https://my-authserver.com/.well-known/openid-configuration) and you receive SSL error or not found error.
So you want internal requests done to the internal container (like http://my-auth-container/.well-known/openid-configuration) while user interacted login/logout should be done using the public domain name (https://my-authserver.com).
Instead of changing the public issuer, you can add OpenIdConnectOptions based on your deployment to configure MetadataAddress as:
context.Services.Configure<OpenIdConnectOptions>("oidc", options =>
{
options.MetadataAddress = configuration["AuthServer:MetaAddress"].EnsureEndsWith('/') +
".well-known/openid-configuration";
var previousOnRedirectToIdentityProvider = options.Events.OnRedirectToIdentityProvider;
options.Events.OnRedirectToIdentityProvider = async ctx =>
{
// Intercept the redirection so the browser navigates to the right URL in your host
ctx.ProtocolMessage.IssuerAddress = configuration["AuthServer:Authority"].EnsureEndsWith('/') +
"connect/authorize";
if (previousOnRedirectToIdentityProvider != null)
{
await previousOnRedirectToIdentityProvider(ctx);
}
};
var previousOnRedirectToIdentityProviderForSignOut =
options.Events.OnRedirectToIdentityProviderForSignOut;
options.Events.OnRedirectToIdentityProviderForSignOut = async ctx =>
{
// Intercept the redirection for signout so the browser navigates to the right URL in your host
ctx.ProtocolMessage.IssuerAddress = configuration["AuthServer:Authority"].EnsureEndsWith('/') +
"connect/endsession";
if (previousOnRedirectToIdentityProviderForSignOut != null)
{
await previousOnRedirectToIdentityProviderForSignOut(ctx);
}
};
This way, login/logout requests will be redirected to the configuration["AuthServer:Authority"] which should be a public domain (like https://my-authserver.com) and the internal requests will be redirected to the configuration["AuthServer:MetaAddress"] which should be an internal service (like http://my-auth-container)
For more details, check out:
eShopOnAbp Public-Web application configuration
eShopOnAbp Azure deployment configuration

Blazor WASM IHttpClientFactory.CreateClient(..) returns client without JWT token in headers

I have a Blazor client app that needs to communicate to multiple API services. In order to simplify things, lets say I have two API servers:
one API server that handles login and register services (https://localhost:7031/api/...)
one server that serves Blazor ASP.NET Core hosted app (https://localhost:7030).
Below I show how I configured identity services on 7031 (I am not using Identity Server):
services.AddIdentity<AppUser, IdentityRole<Guid>>(...)
.AddEntityFrameworkStores<AuthDbContext>()
.AddDefaultTokenProviders();
services.AddAuthenticationWithJwt(configuration); // extension method that configures JWT (not shown here)
services.AddAuthorization(options =>
{
options.AddIdentityPolicies();
options.FallbackPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
.Build();
});
To cut things short, login / register works. Web app is served on port 7030, and authenticates using API's hosted on port 7031. I have also created a test API endpoint with [Authorize] on 7031, to see if I token will be attached to this HTTPS request - all is well. For this to work, I have the following configuration for HttpClient:
services.AddHttpClient(
"IdentityApi",
client => client.BaseAddress = new Uri("https://localhost:7031"));
services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>().CreateClient("IdentityApi"));
So, when I inject HttpClient in some component, it successfully connects to authorized identity APIs. The problem starts when I need to connect to authorized base server APIs (port 7030). In one component I need to access both identity API and server API, so I tried by injecting IHttpClientFactory in it. For this purpose I also added another named HTTPClient:
services.AddHttpClient(
"BaseApi",
client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)); //"https://localhost:7030"
/* razor component */
var identityClient = HttpClientFactory.CreateClient("IdentityApi);
var baseClient = HttpClientFactory.CreateClient("BaseApi);
Both clients have NULL value for DefaultRequestHeaders property. Only if HttpClient is injected directly, then it has DefaultRequestHeaders set, along with Authorization token.
So, somehow this line makes sure that when HttpClient is injected, it has token assigned:
services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>().CreateClient("IdentityApi"));
I tried to use CustomAuthorizationMessageHandler, as suggested here, but I receive exception:
System.InvalidOperationException: Unable to resolve service for type 'Microsoft.AspNetCore.Components.WebAssembly.Authentication.IAccessTokenProvider' while attempting to activate 'x.y.z.CustomAuthorizationMessageHandler'.
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateArgumentCallSites(Type implementationType, CallSiteChain callSiteChain, ParameterInfo[] parameters, Boolean throwIfCallSiteNotFound) ... etc
Anyone knows how can I generate named HttpClients with token assigned?

Identity Server Windows and Username Password

I have identity server 3 setup and working with Windows Authentication, I have the Identity Server instance and the Windows Auth in 2 separate projects, I store custom claims in the database so I add these claims to the token in order to not have to make Web Api use windows and have to check user claims on every request since I have a separate Javascript client that uses the service. Great all works.
How do I add other authencation options to the identity server instance other type of clients? For Windows users in the domain I want to use Windows Auth and for users outside the domain I want to be able to show the login with username password but I don't want the windows users inside the domain to see the Identity Server page with the windows button.
I'm a little confused on how to setup this line:
factory.UserService = new Registration<IUserService>(typeof(ExternalRegistrationUserService));
var options = new IdentityServerOptions
{
SigningCertificate = Certificate.Load(),
Factory = factory,
AuthenticationOptions = new AuthenticationOptions
{
EnableLocalLogin = false,
IdentityProviders = ConfigureIdentityProviders,
}
};
Do I need to seutp multiple Identity Servers or can one Instance Support Muliple AuthenticationOptions?
You don't need multiple identity servers for that. Are you using identityserver 3 or 4? IdentityServer4 has a page in the documentation explaining how to do it with WebListener or Kestrel: http://docs.identityserver.io/en/release/topics/windows.html

azure sdk for net login is takes too long

I am using Fluent Library to develop a web app which can create a sql server on azure. The console app works great but when I implement the code to a web api it stuck in authentication step. I'm sure about the credentials which are true and I have a Service Principal.
// Authenticate
var credentials = new AzureCredentials(new ServicePrincipalLoginInformation { ClientId = ClientId, ClientSecret = Password }, tenantId, AzureEnvironment.AzureGlobalCloud);
var azure = Azure.Configure().Authenticate(credentials).WithDefaultSubscription();
I also can repro it on my side. I try to debug it with following code and add quick watch for azureauth.WithDefaultSubscription(), then get value The function evaluation requires all threads to run. So I guess that it may run some threads that WebAPI can't handle.
var azureauth = Azure.Configure().Authenticate(credentials);
azureauth.WithDefaultSubscription()
Please have a try to use following code to use specified subscriptionId as workaround. It works correctly on my side.
var azure = Azure.Configure().Authenticate(credentials).WithSubscription("subscriptionId");

exchange web services x509 Certivicate

I am trying to connect to Exchange Web Services to send an email on behalf of a user through my own Web Service (ASP/WCF). My code works fine when running on a desktop PC able to connect to the exchange server but when operating over the internet the exchange server can not be accessed, thus I am trying to connect through my web server instead.
I am looking for ways to login as another user without using Exchange Web Services impersonation (as I have been told to not use that unless there is absolutely no other way) and without the user providing their password.
I have the following code:
Dim service As New Microsoft.Exchange.WebServices.Data.ExchangeService(Microsoft.Exchange.WebServices.Data.ExchangeVersion.Exchange2007_SP1)
Dim emailAddress As String = "example#example.com"
System.Net.ServicePointManager.ServerCertificateValidationCallback = AddressOf CertificateValidationCallBack
Dim cert As New System.Security.Cryptography.X509Certificates.X509Certificate2(HttpContext.Current.Request.ClientCertificate.Certificate)
service.Credentials = New Microsoft.Exchange.WebServices.Data.WebCredentials(New System.Security.Cryptography.X509Certificates.X509Certificate())
service.UseDefaultCredentials = False
But this does not work because of an Unable to cast object of type 'System.Security.Cryptography.X509Certificates.X509Certificate' to type 'System.Net.ICredentials'. exception.
Can anyone direct me on how I might use an X509 certificate to authenticate against Exchange Web Services rather than using network credentials or username/password
I have been using this without error
ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true;