Using Session with WCF WS2007FederationHttpBinding (WSFederationHttpBinding) - asp.net

I can't get my values stored in the WCF session when use WS2007FederationHttpBinding. I've seen this topic Session in WCF not consistent, but it didn't help. Moreover, I don't care about reliable session, I just want to store some info in the session and read it at the second request. This scheme works for me with basicHttpsBinding.
Here is my binding. After channel creation I store it to the client Session
var binding = new WS2007FederationHttpBinding(WSFederationHttpSecurityMode.TransportWithMessageCredential);
binding.Security.Message.EstablishSecurityContext = true;
binding.Security.Message.IssuedKeyType = SecurityKeyType.BearerKey;
binding.MaxReceivedMessageSize = 4000000;
var serviceFactory = new ChannelFactory<T>(binding, new EndpointAddress(serviceUrl));
serviceFactory.Credentials.SupportInteractive = false;
var channel = serviceFactory.CreateChannelWithIssuedToken(token);
Service configuration:
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
Session access in the service:
HttpContext.Current.Session["serviceSession"] = "123";
Session retrieval in the service (is always null on the second request but SessionId is the same):
if (HttpContext.Current.Session["serviceSession"] == null) ...
I keep the channel itself in the client session between two requests and reuse it taking from the session for the second request.
Session["clientSession"] = channel;

I beleive HttpContext.Current.Session will only work if you configre asp comptability mode on the service. But even then it will not be correlated with your federation setting. What worked for me is to define the service instance mode as per session:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
public class Service : IService
and then you can use local data members of the service class to persist data between different calls of the same session.

Related

Net Core 6: Session ID Changes After Redirect to AuthenticationHandler.AuthenticateAsync()

:: Using A Custom DistributedCache (Redis Implementation) ::
On all Redirect(url) calls, the session changes. I know the official MS Docs says that IAuthenticationHandler "Created per request to handle authentication for a particular scheme". Meaning even redirects cause a new session
public async Task<AuthenticateResult> AuthenticateAsync() { }
The issue I am having is that during the new session ALL CONTEXT is lost. Everything I saved in the Context (including the ClaimsPrincipal of the user) gets lost. I cannot even fetch transients or singletons attached to my service through:
var SessionProvider = Context.RequestServices.GetService(typeof(ISessionProvider)) as SessionProvider;
Because there is no context. Here is the implementation I followed::
ASP.NET Core 2.0 authentication middleware
Please guide me on any assistance on how to persist Session during AuthenticationAysnc() calls.
Edit :: Here is my service code
service.AddAuthentication(options => {
options.DefaultAuthenticateScheme = "CoreAuthScheme";
options.DefaultChallengeScheme = "CoreAuthScheme";
options.DefaultScheme = "CoreAuthScheme";
}).AddCustomAuth(o => {})
And With Web App
//Register Session Security (Goes before Routing)
app.UseSession();
app.UseAuthentication();
app.UseAuthorization();
Thank you
Solution ::
Proper Implementation of these two library srcs are needed if you are using a custom IDistributedCache. Obviously The IDistributedCache here is Redis Implementation, but the same goes for SQL, and other DB caching (get the proper src implementation of IDistributedCache)
Session Management
https://github.com/dotnet/aspnetcore/tree/c85baf8db0c72ae8e68643029d514b2e737c9fae/src/Middleware/Session/src
IDistributedCache (Redis)
https://github.com/dotnet/aspnetcore/tree/c85baf8db0c72ae8e68643029d514b2e737c9fae/src/Caching/StackExchangeRedis/src
The reason why I was losing session is because the default implementation of DistributedSession uses Offsets and special encodings to process values returned from the IDistributedCache Redis I implemented. See below:
DistributedSession.Deserialize(...)
var expectedEntries = DeserializeNumFrom3Bytes(content);
_sessionIdBytes = ReadBytes(content, IdByteCount);
for (var i = 0; i < expectedEntries; i++)
{
var keyLength = DeserializeNumFrom2Bytes(content);
var key = new EncodedKey(ReadBytes(content, keyLength));
var dataLength = DeserializeNumFrom4Bytes(content);
_store.SetValue(key, ReadBytes(content, dataLength));
}
Once you have those two libraries properly sourced and implemented. Session Management Works :). AuthenticateAsync calls the Distributed Session which calls DistributedCache and data is properly serialized and deserialized
----------------------------------------------------------- alimaslax

SQL Server Report Server (SSRS) via WCF: HTTP request is unauthorized with client authentication scheme 'Ntlm'

I have a .Net Core 3.1 application which is trying to connect to a SQL Server Report Server via WCF, in order to programmatically generate reports on demand.
But the program is not able to authenticate against the Report Server.
Here is the relevant program code:
var binding = new BasicHttpBinding(BasicHttpSecurityMode.TransportCredentialOnly);
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Ntlm;
binding.MaxReceivedMessageSize = 10485760; //10MB limit
// Create the execution service SOAP Client
var rsExec = new ReportExecutionServiceSoapClient(
binding,
new EndpointAddress("http://my-ssrs/ReportServer")
);
// Setup access credentials.
var clientCredentials = new NetworkCredential(
"MyReportServerUserName",
"MyReportServerPassword",
"."
);
if (rsExec.ClientCredentials != null)
{
rsExec.ClientCredentials.Windows.AllowedImpersonationLevel =
System.Security.Principal.TokenImpersonationLevel.Impersonation;
rsExec.ClientCredentials.Windows.ClientCredential = clientCredentials;
}
// ************************************************
// Get following Exception when next line executes.
// ************************************************
await rsExec.LoadReportAsync(null, "/path-to/my-report", null);
When the last line ("rsExec.LoadReportAsync") is executed, I get the following exception:
The HTTP request is unauthorized with client authentication scheme 'Ntlm'. The authentication header received from the server was 'NTLM'.
The Report Server is on the same Windows Domain.
After some research, I've tried changing the ClientCredentialType = HttpClientCredentialType.Windows but this generated a different exception, as follows:
The HTTP request is unauthorized with client authentication scheme 'Negotiate'. The authentication header received from the server was 'NTLM'.
Does anyone have any suggestions about what I might try?
Had the same problem. Solved it by additionally setting the proxy credential type:
binding.Security.Transport.ProxyCredentialType = System.ServiceModel.HttpProxyCredentialType.Ntlm;

when I use asmx service and SSRS report service I am getting "The request failed with http status 401: unauthorised"

I was trying to call report related service (asmx) from my asp.net web application by running locally.
Then an exception happened saying. The request failed with http status401:unauthorised.
In my analysis I understood the issue caused due to below code
SSRSWebService.ReportingService2005 rs = new SSRSWebService.ReportingService2005();
rs.Credentials = new MyReportServerCredentials().NetworkCredentials;
and
Uri reportUri = new Uri(ConfigurationManager.AppSettings["ReportServerManagement.ReportingService2005"]);
this.rptViewer.ServerReport.ReportServerCredentials = new MyReportServerCredentials();
In my detailed analysis I understood that the issue was because of the credential set up in serviceObject.credential OR ServerReport.ReportServerCredentials was wrong. This can be rectified in two different way either by setting credential to default with below code
rs.Credentials = System.Net.CredentialCache.DefaultCredentials;//"rs" is report object
Or locate below code and set up proper authenticated user credential in the code
public WindowsIdentity ImpersonationUser
{
get
{
// Use the default Windows user. Credentials will be
// provided by the NetworkCredentials property.
return null;
}
}
public ICredentials NetworkCredentials
{
get
{
// Read the user information from the Web.config file.
// By reading the information on demand instead of
// storing it, the credentials will not be stored in
// session, reducing the vulnerable surface area to the
// Web.config file, which can be secured with an ACL.
// User name
string userName =
<<AccurateUserName;>>
// Password
string password =
<<AccuratePassword;>>
// Domain
string domain = <<AccurateDomainName;>>
return new NetworkCredential(userName, password, domain);
}
}
In order to check whether which user has the access, we need to type service url ending with asmx (http:/MyServiceHostedServer/MyService.asmx) in a web browser. It will prompt a user name and password . Give our username as :Domain\Username and password.If we are able to see wsdl xml file then that user has the access.

Apache HTTP client 4.3 credentials per request

I have been having a look to a digest authentication example at:
http://hc.apache.org/httpcomponents-client-4.3.x/examples.html
In my scenario the there are several threads issuing HTTP requests and each of them has to be authenticated with their own set of credentials. Additionally, please consider this question is probably very specific for the Apache HTTP client 4.3 onwards, 4.2 handles authentication probably in a different way, although I didn't check it myself. That said, there goes the actual question.
I want to use just one client instance (static member of the class, that is threadsafe) and give it a connection manager to support several concurrent requests. The point is that each request will provide different credentials and I am not seeing the way to assign credentials per request as the credentials provider is set when building the http client. From the link above:
[...]
HttpHost targetHost = new HttpHost("localhost", 80, "http");
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(
new AuthScope(targetHost.getHostName(), targetHost.getPort()),
new UsernamePasswordCredentials("username", "password"));
CloseableHttpClient httpclient = HttpClients.custom()
.setDefaultCredentialsProvider(credsProvider).build();
[...]
Checking:
http://hc.apache.org/httpcomponents-client-ga/tutorial/html/authentication.html#d5e600
The code sample in point 4.4 (seek 4.4. HTTP authentication and execution context), seems to say that the HttpClientContext is given the auth cache and the credentials provider and then is passed to the HTTP request. Next to it the request is executed and it seems that the client will get credentials filtering by the host in the HTTP request. In other words: if the context (or the cache) has valid credentials for the target host of the current HTTP request, he will use them. The problem for me is that different threads will perform different requests to the same host.
Is there any way to provide custom credentials per HTTP request?
Thanks in advance for your time! :)
The problem for me is that different threads will perform different requests to the same host.
Why should this be a problem? As long as you use a different HttpContext instance per thread, execution contexts of those threads are going to be completely indepenent
CloseableHttpClient httpclient = HttpClients.createDefault();
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials("user:pass"));
HttpClientContext localContext = HttpClientContext.create();
localContext.setCredentialsProvider(credentialsProvider);
HttpGet httpget = new HttpGet("http://localhost/");
CloseableHttpResponse response = httpclient.execute(httpget, localContext);
try {
EntityUtils.consume(response.getEntity());
} finally {
response.close();
}
I have a similar issue.
I must call n-times a service with a single system user, authenticated with NTLM. I want to do this using multiple threads.
What I came up with is creating a single HTTPClient with no default credential provider. When a request needs to be performed I use an injected CredentialProviderFactory into the method performing the request (in a specific thread). Using this I get a brand new CredentialsProvider and I put this into a Context (created in the thread).
Then I call the execute method on the client using the overload execute(method, context).
class MilestoneBarClient implements IMilestoneBarClient {
private static final Logger log = LoggerFactory.getLogger(MilestoneBarClient.class);
private MilestoneBarBuilder builder;
private CloseableHttpClient httpclient;
private MilestoneBarUriBuilder uriBuilder;
private ICredentialsProviderFactory credsProviderFactory;
MilestoneBarClient(CloseableHttpClient client, ICredentialsProviderFactory credsProviderFactory, MilestoneBarUriBuilder uriBuilder) {
this(client, credsProviderFactory, uriBuilder, new MilestoneBarBuilder());
}
MilestoneBarClient(CloseableHttpClient client, ICredentialsProviderFactory credsProviderFactory, MilestoneBarUriBuilder uriBuilder, MilestoneBarBuilder milestoneBarBuilder) {
this.credsProviderFactory = credsProviderFactory;
this.uriBuilder = uriBuilder;
this.builder = milestoneBarBuilder;
this.httpclient = client;
}
// This method is called by multiple threads
#Override
public MilestoneBar get(String npdNumber) {
log.debug("Asking milestone bar info for {}", npdNumber);
try {
String url = uriBuilder.getPathFor(npdNumber);
log.debug("Building request for URL {}", url);
HttpClientContext localContext = HttpClientContext.create();
localContext.setCredentialsProvider(credsProviderFactory.create());
HttpGet httpGet = new HttpGet(url);
long start = System.currentTimeMillis();
try(CloseableHttpResponse resp = httpclient.execute(httpGet, localContext)){
[...]
For some reasons I sometimes get an error, but I guess it's an NTLMCredentials issue (not being thread-safe...).
In your case, you could probably pass the factory to the get methods instead of passing in creation.

Impersonation WCF

I have a WCF service, hosted in IIS, which I require to impersonate the annon account.
in my Webconfig
<authentication mode="Windows"/>
<identity impersonate ="true"/>
Testing the following, with vs2008
public void ByRuleId(int ruleId)
{
try
{
string user = WindowsIdentity.GetCurrent().Name;
string name = Thread.CurrentPrincipal.Identity.Name;
........
//get the data as a string.
using (FileStream fs = File.Open(location, FileMode.Open))
using (StreamReader reader = new StreamReader(fs))
{
rawData = reader.ReadToEnd();
}
}
catch.....
}
this works. however if I add impersonation attribute
[OperationBehavior(Impersonation=ImpersonationOption.Required)]
public void ByRuleId(int ruleId)
this does not work with the error message
"Either a required impersonation level was not provided, or the provided impersonation level is invalid."
a little poking around I noticed the first way was authenticated by Kerboros and the second way just failed on authentication type
I am using the WCF client tool, to pass my credentials. this seems to be working.
Check the 'TokenImpersonationLevel' of identity of the current thread; you'll need it to be at least 'Impersonation' to perform operations on the machine that the service is running on.
Typically, if you are using a proxy client, you'll need to set the 'TokenImpersonationLevel' of the client:
http://www.devx.com/codemag/Article/33342/1763/page/4
the main goal of this was to get anon access, even tho MattK answer was a great help.
here is what i did to do so.
on the implementation of the WCF contract I added the
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
public class TransferFile : ITransferFile
and in the web.config
<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled ="true" />
after this i was able to impersonate the anon account

Resources