I have an aspnetcore Web API and the host is configured for Windows Auth with the following code.
var host = new WebHostBuilder()
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseUrls("http://TestServer:5000")
.UseStartup<Startup>()
.UseWebListener(options =>
{
options.Listener.AuthenticationManager.AuthenticationSchemes = Microsoft.Net.Http.Server.AuthenticationSchemes.NTLM;
//NOTE: Multiple auth assignments don't work in 1.0.0 - Wait for 1.1.* for the line below to start working
//See: https://github.com/aspnet/Announcements/issues/198
//options.Listener.AuthenticationManager.AllowAnonymous = true;
})
.Build();
host.Run();
The calling Client is configured with the following
HttpClientHandler handler = new HttpClientHandler()
{
UseDefaultCredentials = true,
PreAuthenticate = true
};
HttpClient client = new HttpClient(handler);
client.BaseAddress = new Uri("http://TestServer:5000/");
client.DefaultRequestHeaders
.Accept
.Add(new MediaTypeWithQualityHeaderValue("application/json"));
In the service I'm calling I can access the calling user's ClaimsPrincipal Identity.
My Question is how do I call this service from an integration test using the TestServer Client initialised via the CreateClient method. What is the best way to ensure the Identity is set when calling from an integration test?
I'm also using XUnit in case you would like to know.
Any help or advice would be greatly appreciated.
Related
I would like to know how to use WebProxy with RestSharp. I am using version 108.0.1 and the code given below returns 407 when running locally from my workstation, which should use my credentials.
var client = new RestClient("https://www.google.com");
var proxy = new System.Net.WebProxy("http://mycorpproxy.com");
proxy.UseDefaultCredentials = true;
client.Options.Proxy = proxy;
var request = new RestRequest();
request.Method = Method.Get;
var response = client.Execute(request);
You need to specify the proxy in the options when you create the client, not after. In v107, the options object properties were init-only, but it fails on legacy SDKs, so we had to revert it to setters, but setting the options that are used to create an HttpClient instance after the client is created has no effect.
var proxy = new WebProxy("http://mycorpproxy.com") {
UseDefaultCredentials = true
};
var options = new RestClientOptions("https://www.google.com") {
Proxy = proxy
};
var client = new RestClient(options);
I would like to create a simple TCP Server / TCP Client and use a Controller to interface with that classes, to then host the controller in a kestrel webserver.
I wanted to use SimpleSockets to create TCP Clients and the Server.
The creator of that library describes two ways of instantiating a TCP-Server either by providing an SSL-Certificate or by just creating a TcpListener without the need of an certificate.
It is described here
I want to use the option by providing an ssl-certificate but I cannot figure out how to provide the constructor with that certficate that is managed by the kestrel webserver, is there any way of injecting it (if that is the right way to do it) into the constructor of the TCP-Server?
If yes how would I inject the certificate?
In the code of the webserver I add the certificate like that:
builder.Services.AddAuthentication(CertificateAuthenticationDefaults.AuthenticationScheme)
.AddCertificate(options =>
{
options.AllowedCertificateTypes = builder.Configuration.GetSection("Authentication").GetSection("CertificateAuthentication").GetValue<CertificateTypes>("AllowedCertificateTypes");
options.Events = new CertificateAuthenticationEvents
{
OnCertificateValidated = context =>
{
var validationService = context.HttpContext.RequestServices.GetService<CertificateValidationService>();
if (validationService is not null)
{
if (validationService.ValidateCertificate(context.ClientCertificate))
{
var claims = new[]
{
new Claim(ClaimTypes.NameIdentifier, context.ClientCertificate.Subject, ClaimValueTypes.String, context.Options.ClaimsIssuer),
new Claim(ClaimTypes.Name, context.ClientCertificate.Subject, ClaimValueTypes.String, context.Options.ClaimsIssuer)
};
context.Principal = new ClaimsPrincipal(new ClaimsIdentity(claims, context.Scheme.Name));
context.Success();
return Task.CompletedTask;
}
}
context.Fail("Invalid client certificate");
return Task.CompletedTask;
}
};
})
.AddCertificateCache();
And that is the code of the constructor of my TCP-Server class where I want to inject the certificate into:
public TcpServer(X509Certificate2 certificate)
{
Listener = new SimpleSocketTcpSslListener(certificate);
}
Thanks in advance.
I have completed the guide here to add Azure AD authentication to my application:
https://azure.microsoft.com/en-gb/resources/samples/active-directory-dotnet-webapp-openidconnect-aspnetcore/
and can log in successfully, have a service principal and everything works as expected.
I now want to make web requests as the user, but can't see how to get the authentication details to send in the request, I've tried looking through the ClaimsPrincipal.Current object, but there is nothing i can pass to a HTTP client to make the request.
The sample web app you refered to only signs the user in, but you need to get the access token on behalf of that user to access the api.
You can refer to this sample. This sample calls another webapi, you can ignore that part, just change the resource to https://management.core.windows.net/
public void Configure(string name, OpenIdConnectOptions options)
{
options.ClientId = _azureOptions.ClientId;
options.Authority = _azureOptions.Authority;
options.UseTokenLifetime = true;
options.CallbackPath = _azureOptions.CallbackPath;
options.RequireHttpsMetadata = false;
options.ClientSecret = _azureOptions.ClientSecret;
options.Resource = "https://management.core.windows.net/"; // management api
options.ResponseType = "id_token code";
// Subscribing to the OIDC events
options.Events.OnAuthorizationCodeReceived = OnAuthorizationCodeReceived;
options.Events.OnAuthenticationFailed = OnAuthenticationFailed;
}
private async Task OnAuthorizationCodeReceived(AuthorizationCodeReceivedContext context)
{
// Acquire a Token for the management API
string userObjectId = (context.Principal.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier"))?.Value;
var authContext = new AuthenticationContext(context.Options.Authority, new NaiveSessionCache(userObjectId, context.HttpContext.Session));
var credential = new ClientCredential(context.Options.ClientId, context.Options.ClientSecret);
var authResult = await authContext.AcquireTokenAsync(context.Options.Resource,credential);
// Notify the OIDC middleware that we already took care of code redemption.
context.HandleCodeRedemption(authResult.AccessToken, context.ProtocolMessage.IdToken);
}
I'm looking at Microsoft.AspNetCore.TestHost.TestServer in the Microsoft source code. There I see the CreateClient() property that is HttpClient objects.
So how do I attached the client certificate for Digitial Signature in xUnit Test?
HttpClient example would be this.
var x509Certificate2 = new X509Certificate(); // Pretend it contains certificate data already.
var httpClientHandler = new HttpClientHandler() {
ClientCertificateOptions = ClientCertificateOption.Manual
};
httpClientHandler.ClientCertificates.Add(x509Certificate2);
using (var httpClient = new HttpClient(httpClientHandler))
{
}
Now using the TestServer
var testServer = new TestServer();
testServer.CreateClient(); // How do I attached client certificate here?
So, how do we attach the client certificate here? For CreateClient()
Also, I can try to make do with implementing the HttpRequestMessage but it doesn't support that certificate option either.
You may try to use the testServer.SendAsync(...) method and just construct your HttpContext from there.
var result = await server.SendAsync(context =>
{
context.Connection.ClientCertificate = myClientCertificate;
context.Request.Method = "POST";
});
Just make sure to specify the Path and the Body as needed.
I have created a ServiceStack service on top of Asp.Net that implements Basic authentication. Everything is working fine on the service routes. I am able to login and I get the session cookies which are validated on subsequent calls. I'm using an HttpClient for those requests.
I also have a SignalR Hub that runs on the same Asp.Net service, but the Principal is not authenticated on my Hub methods.
Basically what I need is for ServiceStack to intercept calls into my Hub and validate the session cookie and populate the Context.User.Identity and mark it as authenticated. If I can get that set up, a simple [Authorize] attribute on my hub will do the rest.
Here is a sample of my code:
// set up a HttpClient with a cookie container to hold the session cookie
var cookieJar = new CookieContainer();
var handler = new HttpClientHandler { CookieContainer = cookieJar, UseCookies = true, UseDefaultCredentials = false };
var client = new HttpClient(handler) { BaseAddress = _baseUri };
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Basic",
Convert.ToBase64String(Encoding.ASCII.GetBytes(string.Format("{0}:{1}", userName, password))));
// do client login and get response with session cookie...
var response = client.PostAsync(...);
// add the cookies to the SignalR hub connection
var responseCookies = cookieJar.GetCookies(_baseUri);
var cookieContainer = new CookieContainer();
foreach (Cookie cookie in responseCookies)
{
cookieContainer.Add(cookie);
}
_hubConnection = new HubConnection(_baseUri.ToString()) { CookieContainer = cookieContainer };
After this setup, my session cookies are sent to the Hub on each invocation. Somehow I need for ServiceStack to intercept those requests and set the authenticated user.
Let ServiceStack do the authenication and persisting the user session. Then in the SignalR hub endpoints that need authentication put this code:
var cache = AppHostBase.Resolve<ICacheClient>();
var sess = cache.SessionAs<AuthUserSession>();
if (!sess.IsAuthenticated)
throw new AuthenticationException();
Johan's answer works but it is not very convenient to put this code to every method and if you put it in the hub constructor, it will fail in the web on page refresh with "Only ASP.NET Requests accessible via Singletons are supported" exception.
Instead, I have chosen to create a custom attribute that gives you more control on the hub and method call authorization.
Simplest attribute would look like this:
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public class AuthorizeServiceStack : AuthorizeAttribute
{
public override bool AuthorizeHubConnection(HubDescriptor hubDescriptor, IRequest request)
{
return CheckAuthorization();
}
public override bool AuthorizeHubMethodInvocation(IHubIncomingInvokerContext hubIncomingInvokerContext, bool appliesToMethod)
{
return CheckAuthorization();
}
private static bool CheckAuthorization()
{
var cache = AppHostBase.Resolve<ICacheClient>();
var sess = cache.SessionAs<AuthUserSession>();
return sess.IsAuthenticated;
}
}
As you can see, the code is the same as in Johan's answer but it will work in the web as well, ensuring that HttpContext.Current is not null when you are calling cache.SessionAs