Setting Cookie in Response from WEB API of a different domain - c#

I have a Web API project and a separate web app. they have two different port numbers for their local host address. I am trying to set a cookie on the web app passed from the WEB API response. I can see it the cookie in the header, however the browser does not save the cookie. Also, if I set a cookie on the web app and try read it in the API request, it can not be seen.
task.Result.Headers.AddCookies(new CookieHeaderValue[] {
new CookieHeaderValue(MemberToken, Guid.NewGuid().ToString()){
Domain = request.RequestUri.Host == "localhost" ? null : request.RequestUri.Host,
Expires = DateTime.Now.AddDays(30),
Path = "/"
}
});

Related

Accessing HttpOnly cookies across multiple domain with URL redirection

I have an ASP.Net MCV website (.net 4.6) (https://site.aa.main.com) and we have built a redirection to an Angular SAP (https://spa.aa.new.main.com). We also have a standalone API (.net core 3.1)(https://api.aa.new.main.com:5001) to serve requests from the SPA.
Here I need to set a cookie in site 1 before the redirection, then I can use that cookie in the API.
I have the below code in site 1 to set this cookie,
HttpCookie payidCookie = new HttpCookie("myKey", "myValue")
{
Secure = true,
HttpOnly = true,
Domain = ".new.main.com",
};
this.Response.Cookies.Add(payidCookie);
Then I have the below code to consume the cookie in the API,
if (Request.Cookies["myKey"] != null)
{
var value = Request.Cookies["myKey"];
}
But the cookie is not available in the API. Request.Cookies["myKey"] return null.
Does anyone know why I cannot see the cookie in the API and how to fix this issue?
Thanks.
This case is explicitly covered in specification, site.aa.main.com cannot set cookies for new.main.com:
The user agent will reject cookies unless the Domain attribute
specifies a scope for the cookie that would include the origin
server. For example, the user agent will accept a cookie with a
Domain attribute of "example.com" or of "foo.example.com" from
foo.example.com, but the user agent will not accept a cookie with a
Domain attribute of "bar.example.com" or of "baz.foo.example.com".
You would either have to set cookie for main.com or figure out other naming setup that would have common subdomain for all involved hosts.

Antiforgery throws bad request in asp.net core web api angular

I am trying to add anti-forgery to my asp.net core 3.1 web API by adding a filter in the startup file.
options => options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute())
This web API is consumed by my angular app which is hosted in a different port. I have done all the configuration which is specified in Microsoft docs as below.
app.Use(next => context =>
{
string path = context.Request.Path.Value;
if (path != null)
{
var tokens = antiforgery.GetAndStoreTokens(context);
context.Response.Cookies.Append("XSRF-TOKEN",
tokens.RequestToken, new CookieOptions
{
HttpOnly = false,
Path = "/",
}
);
}
return next(context);
});
In services
services.AddAntiforgery(options =>
{
options.HeaderName = "X-XSRF-TOKEN";
});
It generates two tokens one is.Asp.NetCore.AntiForgery and XSRF-TOKEN. I am getting this token in my client app and sending it to API as request header as x-xsrf-token but it fails every time. I have set up my cors to allow any origin. I am getting token as below in my angular app.
let xsrfToken = this.xsrfTokenExtractor.getToken() as string;
if(xsrfToken){
request = request.clone({headers: request.headers.set("X-XSRF-TOKEN", xsrfToken)});
}
Let me explain to you my flow. I have an identity server, web API, and angular app all of which are hosted in different ports. The angular app redirects to the identity server for authentication once it's done it will be redirected back to my client app. I have set up this csrf in web API. so basically, the authentication happens using a bearer token. I know that we don't need csrf protection because we already use a bearer token as my authentication mechanism. But I need it to work for csrf as well. Is there any way to achieve this?
I have a similar flow and I encountered a similar problem. In my case, I did not use the Configure method but instead I created a separate Endpoint that allows me to request the cookies.
var aft = _antiForgery.GetAndStoreTokens(HttpContext);
Response.Cookies.Append("XSRF-TOKEN", aft.RequestToken, new CookieOptions
{
HttpOnly = false,
Secure = true,
Domain = config.Domain
});
Initially I was doing the logging in part the cookie part in the same request and that lead to every request being rejected with a 400 Bad Request token but once I separated the login part and the cookies part in two separated endpoints and made two requests, everything worked as expected.

How to serve multiple web sites by the one ADFS server?

I have two servers: one of them serves UI (it is called webUI) and another works with data (it is called webAPI).
I try to implement an authentication across the ADFS server. It has Relying Party Trusts for both servers: [urn=webui,identifier=address/webui],[urn=webapi,identifier=address/webapi].
I adjused the HttpConfiguration for webUI and user can be authenticated and use website, which the webUI serves (it's good).
var wsFedMetAdd = ConfigurationManager.AppSettings["wsFedMetAdd"];
if (string.IsNullOrWhiteSpace(wsFedMetAdd))
throw new ConfigurationErrorsException(Properties.Resources.InvalidMetadataAddress);
var wsFedWtrealm = ConfigurationManager.AppSettings["wsFedWtrealm"];
if (string.IsNullOrWhiteSpace(wsFedWtrealm))
throw new ConfigurationErrorsException(Properties.Resources.InvalidWtrealm);
appBuilder.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = WsFederationAuthenticationDefaults.AuthenticationType
});
var options = new WsFederationAuthenticationOptions
{
MetadataAddress = wsFedMetAdd,
Wtrealm = wsFedWtrealm,
SignInAsAuthenticationType = "Federation"
};
appBuilder.UseWsFederationAuthentication(options);
config.Filters.Add(new AuthorizeAttribute() { Roles = "Admin" });
Once client gets RequestSecurityTokenResponse (SAML Token). Also responses from ADFS set cookies for further requests (MSISAuth, MSISAuthenticated and so on).
The webAPI has the same implemention of HttpConfiguration (only one difference - wsFedWtrealm is urn:webapi instead urn:webui). Then I try send a request to the webAPI from client and the ADFS Server asks to authenticate one more.
I can't understand what should I do to use the same credentials for webAPI which I entered for webUI. Or maybe I should use SAML Token?
UPDATE
Wow. It is worked without SAML token, just using cookies.
When the user tries to be authenticated for webUI, diverse cookies are set on client (.AspNet.Federation, MSISAuth, MSISAuthenticated...). Then I substitute the webUI link with the webAPI link in the address bar and then webAPI doesn't ask to enter login and password. Hence data is displayed in browser. Authentication is picked up for webUI and for webAPI too.
But now problem is I get the error when javascript tries to send a request to webAPI:
XMLHttpRequest cannot load
https://my_address/adfs/ls/?wtrealm=urn%3awebapi&wctx=_ No 'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'https://my_address:9001' is therefore not allowed
access.
What version of ADFS?
You are mixing two protocols - Web API generally uses OAuth.
Use OpenID Connect for the UI and then that will naturally flow into the WebAPI as per this : Securing a Web API with ADFS on WS2012 R2 Got Even Easier.
Or for a somewhat more convoluted approach - what protocol to use with ADFS when security webapi for non-browser clients
This post help me to solve my problem.
I added to code of index.html new element iframe. Attribute src is the link to my webAPI.

How to add subdomain to Azure WebSite via API

My website is hosted as Azure WebSite and I need to allow users create their own subdomains.
I already implemented this on local machine using DNS and CNAME option, but I can't do it in Azure. When I try to add Azure host name for the site I receive error that hostname must ends with '.azurewebsites.net'.
Here is my code:
var client = new WebSiteManagementClient(new CertificateCloudCredentials(ConfigurationManager.AppSettings["AzureSubscriptionId"],
new X509Certificate2(Path.Combine(AppDomain.CurrentDomain.RelativeSearchPath, "{certificate}"), "{password}")));
var configuration = client.WebSites.Get(webSpaceName, webSiteName, new WebSiteGetParameters());
if (configuration.StatusCode != HttpStatusCode.OK)
throw new Exception("AzureGet: " + subdomain);
configuration.WebSite.HostNames.Add(hostName);
var response = client.WebSites.Update(webSpaceName, webSiteName, new WebSiteUpdateParameters());
Is there any other methods to add custom HostName like 'sub.mydomain.net'?
You could use a wildcard custom domain name for your website, and then check the hostname in your application code by inspecting the headers (host or X-Forwarded-Host) of the incoming request.
i.e. your domain name would point *.mydomain.net to your web app, and then your DB keeps track of the user registered domains, and then your web app does the routing.

Asp.net forms authenticated webservice

I have a small conundrum where I have an asp.net application that's using forms based authentication. Inside of the application, I have a webservice that checks User.IsInRole("somerole") which works fine with ajax calls from the application since the user is logged in, and the ajax calls come from his logged in browser.
Now, I want to make a fat client call the webservices (c# console client for starters), but cant figure out how to pass the credential information to it.
I've looked at doing something like the following to no avail:
SomeWebService svc = new SomeWebService();
svc.Credentials = new NetworkCredential("formsusername","formspassword","");
String returnValue = svc.CallMyWebMethod();
Can anyone out there show me the trick to this? :-)
Thanks!
Forms Authentication works by having the client send a Cookie along each request. This cookie is emitted by the server when the client successfully authenticates by sending the correct credentials.
So here are the steps that you need to do in your console application in order to authenticate the user using forms authentication:
Send an HTTP POST request to some web page passing the username and password. In response the web server will give you the authentication cookie (Set-Cookie HTTP response header) that you need to capture. That's usually your Log page.
When calling your web service you need to pass this cookie (Cookie HTTP request header). In order to set a cookie along with the request, you will have to override the GetWebRequest method on the client proxy class that was generated for you:
protected override WebRequest GetWebRequest(Uri uri)
{
var request = (HttpWebRequest)base.GetWebRequest(uri);
request.CookieContainer.Add(
new Cookie(
".ASPXAUTH",
"THE VALUE YOU HAVE RETRIEVED WHEN YOU SEND YOUR FIRST LOGON REQUEST"
)
);
return request;
}

Categories