Is there a builtin mechanism to authenticate /app/rest/** if the request is coming from another service ?
You can do this by spring security configuration. Try adding this configuration in SecurityConfiguration
.authorizeRequests()
.antMatchers("/app/rest/**").hasAuthority(AuthoritiesConstants.ADMIN)
Related
We have a SaaS web app and our clients are requiring SSO authentication for each of them. We are using AzureADB2C and it works great, but now are looking at adding SSO.
I put in the SSO setup into the B2C tenet and it works great, but really messed up our login screen with a "MyCompanySSO" button to log in with, on our customer-facing login screen.
So now my idea is to have a separate user flow that handles each SSO setup. Starting with us. We'd go to MyCompany.OurSaaSApp.us and that'd forward them directly to the user flow endpoint and prompt them to login with their SSO account (AzureAD).
This all seems to try to work, but I'm getting these errors within the AzureADB2C middleware:
Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler:Warning: .AspNetCore.Correlation. state property not found.
Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler:Information: Error from RemoteAuthentication: Correlation failed..
Then I get pumped out onto a error page and the login fails.
So 2 things...
1.) Am I going in the right direction knowing what we're wanting to accomplish
2.) What do we need to do to resolve this?
Thanks everyone for the help, it's been greatly appreciated.
(note:)
Just to reiterate. The SSO works properly when the custom identity provider is attached to the existing SignUpOrIn UserFlow I have configured in the app. I'm only getting this error when I try to use another UserFlow that I want to use specifically for this SSO.
I'm not sure about that specific error, although "state" parameter is a parameter that your app sends in the request that will be returned in the token for correlation purposes.
Using and different policy for each federation sounds like the right approach, but if you are doing from a single instance of your app, you'll need to modify the OIDC protocol message with the correct authority (ie policy) on redirect.
In your OIDC middleware configuration, set up a handler for the RedirectToIdentityProvider notification. Then handle it with something like:
private Task OnRedirectToIdentityProvider(RedirectToIdentityProviderNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)
{
//var policy = notification.OwinContext.Get<string>("Policy");
var tenantSegment = notification.Request.Path.Value.Split(new char [] { '/'}, StringSplitOptions.RemoveEmptyEntries)[0];
if (!string.IsNullOrEmpty(tenantSegment) && !tenantSegment.Equals(DefaultPolicy))
{
notification.ProtocolMessage.IssuerAddress = notification.ProtocolMessage.IssuerAddress.ToLower().Replace(DefaultPolicy.ToLower(), $"B2C_1A_{tenantSegment.ToLower()}_SignUpSignInPolicy");
}
return Task.FromResult(0);
}
If you need to inject anything else tenant-related, that would be the place to do it.
In my Spring Integration project (with Spring Security API using Basic Authorization), I am trying to access the Principal object in order to read the Username.
This is the structure of the inbound gateway:
<int-http:inbound-gateway request-channel="gatewayRequests"
reply-channel="gatewayResponses"
supported-methods="POST"
path="/api/v1/myservice"
request-payload-type="java.lang.String" reply-timeout="100">
<int-http:request-mapping consumes="application/xml" produces="application/xml"/>
<int-http:header name="principal" expression="T(org.springframework.security.core.context.SecurityContextHolder).context.authentication.principal"/>
</int-http:inbound-gateway>
I got the aforementioned expression from the reply in this:
Spring Integration and http inbound adapter, how do I extract a principal user?
Despite successful authentication, I don't see the principal - is my syntax correct in expecting the result of the expression to be mapped to a message header?
Instead of the mapping to the header, if I were to use the following, how do I access the Principal value in the code layer (assuming it gets added into the payload)?
<payload-
expression="T(org.springframework.security.core.context.SecurityContextHolder).
context.authentication.principal">
Can anyone kindly help me?
Sincerely,
Bharath
There is already a header like:
.setHeader(org.springframework.integration.http.HttpHeaders.USER_PRINCIPAL,
servletRequest.getUserPrincipal())
in the message sent to the gatewayRequests.
Why that doesn't work for you?
OTOH that expression must work too. If you don't have that one, then you can't assume that you are authenticated correctly...
I have a running java ee application and now i want to integrate keycloak as authentication server. The only thing i have troubles is the user storage. I want to have all the user data in my java application.
The problem now:
If the user registers on the keycloak frontend, my java application doesn´t know that the user has registered, so i cannot create a new entity. I found out that keycloak is able to load some custom modules (https://keycloak.github.io/docs/userguide/keycloak-server/html/providers.html) but i haven´t found any examples.
Is there any solution where keycloak notifys my java application when the user registered?
I have had the same problem and I have resolved it using a filter. I just check if the principal exist and if not I insert it into my application DB:
KeycloakSecurityContext ctx = (KeycloakSecurityContext)request.getAttribute(KeycloakSecurityContext.class.getName());
User userEntity = em.find(User.class, ctx.getToken().getSubject());
if (userEntity == null) {
....create user...
}
You can also use an event listener (keycloak events listener) as shown in Example Event Listener that prints events to System.out, but for this exact use case that solution was easier and faster.
You have to implement a custom Authentication SPI (inside success() method you create user on your app), deployed it , and add it to the registration flow
Keycloak documentation : Link
The Azure API app documentation briefly describe three methods of protecting the API app. One of them is the internal accessibility settings: “Internal - Only other API apps or web apps in the same resource group are allowed to call the API app.”
I have create another Azure API app in the same resource group and hosting plan. But a get a HTTP 403 authorization failure with the following error message when I try to connect to the interal API app from the Web App:
“Permissions for service are set to internal but this request was external.”
Has anyone been able to use the internal settings between API Apps in the same Resource Group?
We will be documenting this soon. in the meantime, what you need to do is the following. You need to install nuget package Microsoft.Azure.AppService.ApiApps.Service. Then, create a delegating handler as follows:
class InternalCredentialHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
Runtime.FromAppSettings(request).SignHttpRequest(request);
return base.SendAsync(request, cancellationToken);
}
}
Then when you use HttpClient or a generated client to connect to another internal API, simply pass in the delegating handler. For example:
MySampleClient client = new MySampleClient(new DelegatingHandler[] { new InternalCredentialHandler() });
Thanks,
Mohit
Edit: the documentation for this is now available at https://azure.microsoft.com/documentation/articles/app-service-api-dotnet-consume-internal/
I have an ASP.NET Web API service that runs on a web server with Windows Authentication enabled.
I have a client site built on MVC4 that runs in a different site on the same web server that uses the HttpClient to pull data from the service. This client site runs with identity impersonation enabled and also uses windows authentication.
The web server is Windows Server 2008 R2 with IIS 7.5.
The challenge I am having is getting the HttpClient to pass the current windows user as part of its authentication process. I have configured the HttpClient in this manner:
var clientHandler = new HttpClientHandler();
clientHandler.UseDefaultCredentials = true;
clientHandler.PreAuthenticate = true;
clientHandler.ClientCertificateOptions = ClientCertificateOption.Automatic;
var httpClient = new HttpClient(clientHandler);
My understanding is that running the site with identity impersonation enabled and then building the client in this manner should result in the client authenticating to the service using the impersonated identity of the currently logged in user.
This is not happening. In fact, the client doesn't seem to be authenticating at all.
The service is configured to use windows authentication and this seems to work perfectly. I can go to http://server/api/shippers in my web browser and be prompted for windows authentication, once entered I receive the data requested.
In the IIS logs I see the API requests being received with no authentication and receiving a 401 challenge response.
Documentation on this one seems to be sparse.
I need some insight into what could be wrong or another way to use windows authentication with this application.
Thank You,
Craig
I have investigated the source code of HttpClientHandler (the latest version I was able to get my hands on) and this is what can be found in SendAsync method:
// BeginGetResponse/BeginGetRequestStream have a lot of setup work to do before becoming async
// (proxy, dns, connection pooling, etc). Run these on a separate thread.
// Do not provide a cancellation token; if this helper task could be canceled before starting then
// nobody would complete the tcs.
Task.Factory.StartNew(startRequest, state);
Now if you check within your code the value of SecurityContext.IsWindowsIdentityFlowSuppressed() you will most probably get true. In result the StartRequest method is executed in new thread with the credentials of the asp.net process (not the credentials of the impersonated user).
There are two possible ways out of this. If you have access to yours server aspnet_config.config, you should set following settings (setting those in web.config seems to have no effect):
<legacyImpersonationPolicy enabled="false"/>
<alwaysFlowImpersonationPolicy enabled="true"/>
If you can't change the aspnet_config.config you will have to create your own HttpClientHandler to support this scenario.
UPDATE REGARDING THE USAGE OF FQDN
The issue you have hit here is a feature in Windows that is designed to protect against "reflection attacks". To work around this you need to whitelist the domain you are trying to access on the machine that is trying to access the server. Follow below steps:
Go to Start --> Run --> regedit
Locate HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa\MSV1_0 registry key.
Right-click on it, choose New and then Multi-String Value.
Type BackConnectionHostNames (ENTER).
Right-click just created value and choose Modify.
Put the host name(s) for the site(s) that are on the local computer in the value box and click OK (each host name/FQDN needs to be on it's own line, no wildcards, the name must be exact match).
Save everything and restart the machine
You can read full KB article regarding the issue here.
I was also having this same problem. Thanks to the research done by #tpeczek, I developed the following solution: instead of using the HttpClient (which creates threads and sends requests async,) I used the WebClient class which issues requests on the same thread. Doing so enables me to pass on the user's identity to WebAPI from another ASP.NET application.
The obvious downside is that this will not work async.
var wi = (WindowsIdentity)HttpContext.User.Identity;
var wic = wi.Impersonate();
try
{
var data = JsonConvert.SerializeObject(new
{
Property1 = 1,
Property2 = "blah"
});
using (var client = new WebClient { UseDefaultCredentials = true })
{
client.Headers.Add(HttpRequestHeader.ContentType, "application/json; charset=utf-8");
client.UploadData("http://url/api/controller", "POST", Encoding.UTF8.GetBytes(data));
}
}
catch (Exception exc)
{
// handle exception
}
finally
{
wic.Undo();
}
Note: Requires NuGet package: Newtonsoft.Json, which is the same JSON serializer WebAPI uses.
The reason why this is not working is because you need double hop authentication.
The first hop is the web server, getting impersonation with Windows authentication to work there is no problem. But when using HttpClient or WebClient to authenticate you to another server, the web server needs to run on an account that has permission to do the necessary delegation.
See the following for more details:
http://blogs.technet.com/b/askds/archive/2008/06/13/understanding-kerberos-double-hop.aspx
Fix using the "setspn" command:
http://www.phishthis.com/2009/10/24/how-to-configure-ad-sql-and-iis-for-two-hop-kerberos-authentication-2/
(You will need sufficient access rights to perform these operations.)
Just consider what would happen if any server was allowed to forward your credentials as it pleases... To avoid this security issue, the domain controller needs to know which accounts are allowed to perform the delegation.
To impersonate the original (authenticated) user, use the following configuration in the Web.config file:
<authentication mode="Windows" />
<identity impersonate="true" />
With this configuration, ASP.NET always impersonates the authenticated user, and all resource access is performed using the authenticated user's security context.