OWIN Cookie Authentication and Cross Domain AngularJS Client - angularjs

Application Setup
Client - AngularJS (hosted at client.domain.com)
API + SignalR - Hosted at api.domain.com
SignalR setup class
public void Configuration(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie
});
app.Map("/signalr", map =>
{
map.UseCors(CorsOptions.AllowAll);
var hubConfiguration = new HubConfiguration
{
EnableJSONP = true
};
map.RunSignalR(hubConfiguration);
});
}
And I do following from Authentication service
AuthenticationManager.SignOut();
List<Claim> claims = new List<Claim>();
claims.Add(new Claim(ClaimTypes.Name, myuserid));
ClaimsIdentity id = new ClaimsIdentity(claims, DefaultAuthenticationTypes.ApplicationCookie);
HttpContext.Current.GetOwinContext().Authentication.SignIn(new AuthenticationProperties() { IsPersistent = false }, id);
Authentication response looks like
HTTP/1.1 200 OK
Cache-Control: private
Pragma: no-cache
Content-Length: 556
Content-Type: application/json; charset=utf-8
Expires: -1
Server: Microsoft-IIS/7.5
Access-Control-Allow-Origin: *
Set-Cookie: ASP.NET_SessionId=pvasgf4q2klfbmk4oojqkxm5; path=/; HttpOnly
X-AspNet-Version: 4.0.30319
Set-Cookie: .AspNet.ApplicationCookie=1lBbz_FRNvxt1AhwkzZHFoiyhyZgsasNXStV3SbjQUHt0hqxTG--yXtjYNquB4zmQn-6M99pqHRPHaT4uf8g1JAnqg5WldD65IJ_FxYoEucf_WvV1fY4pwf774Cyo6fhI5nVk9Z6fdpjY422eo0GIibIMhaV9QvHNaiSsbFO0koT-CnhQuD5DED_418nP7m4FBbXdwIbFM_squ0bVYKXuB35jm57zVk9hUVWdlPmWxc; path=/; HttpOnly
X-Powered-By: ASP.NET
Date: Tue, 30 Dec 2014 00:20:29 GMT
When I the app makes request to signalr/negotiate, it receives 401 (unauthorized) because the cookie received in authentication response is not being sent with signalr request.
Is there anything I am missing?
UPDATE 1
All of above works fine when client and api are hosted in the same domain like this.
Client - www.domain.com/client
API - www.domain.com/api
UPDATE 2
Upon digging a little further, I realized that the issue is not related to signalr at all. I wrote a light client just to see if I can authenticate and follow that up with a simple get request. I hosted the client at api.domain.com/lightclient and made api requests to api.domain.com/api everything works. The following get request has the authentication cookie. However, when I moved the client to client.domain.com, browser removes the cookie.

Related

Problem with cookies ASP.NET / React.js - not saving

I've problem with setting token with react app. Im getting "refresh-token" from API response
private void SetTokenCookie(string token)
{
var cookieOptions = new Microsoft.AspNetCore.Http.CookieOptions
{
HttpOnly = true,
Expires = DateTime.UtcNow.AddHours(8),
Path = "/auth/refresh-token"
};
Response.Cookies.Append("refreshToken", token, cookieOptions);
}
There is no problem in Postman. I received from response in Headers -> Set-Cookie - refreshToken=3CTqZ6GQNT%2FxsmbUjFJQnkdkSd1utwtPtmxBtpawbZXCXwLR1HqvM3u3ZeJ6rRoidOSUTDJ17B0tku2PD7qeVw%3D%3D; expires=Tue, 14 Dec 2021 23:57:32 GMT; path=/auth/refresh-token; secure
But when im using browser, this cookie is not saving.

Unable to set httpOnly cookie between Heroku and Netlify + cloudflare

I have the following problem. I am trying to set httpOnly cookie and nothing happens. I spent a few hours trying to solve this issue and I have no idea what is going on... My architecture is the following:
Backend: Python fast-api hosted on Heroku, available at https://api.mysuperdomain.com.
Frontend: GatsbyJs hosted on Netlify, available at https://mysuperdomain.com
When I call login request from React component:
const handleSubmit = async (e) => {
e.preventDefault()
const config = {
headers: {
crossDomain: true,
withCredentials: true,
'Content-Type': 'application/x-www-form-urlencoded'
}
}
const requestBody = {
username: emailRef.current.value,
password: passwordRef.current.value
}
try {
const data = await axios.post('https://api.mysuperdomain.com/login', qs.stringify(requestBody), config)
I get response from my backend with headers, set-cookie:
set-cookie: Authorization="Bearer somethinghere"; Domain=.mysuperdomain.com; expires=Tue, 28 Jul 2020 20:40:32 GMT; Max-Age=1800; Path=/; SameSite=lax
unfortunately in browser storage I cannot see this cookie.
My backend(API) sets the cookie in the following way:
#app.post("/login")
async def login(form_data: OAuth2PasswordRequestForm = Depends()):
user = authenticate_user(fake_users_db, form_data.username, form_data.password)
if not user:
raise HTTPException(status_code=400, detail="Incorrect username or password")
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
access_token = create_access_token(
data={"sub": form_data.username}, expires_delta=access_token_expires
)
token = jsonable_encoder(access_token)
response = JSONResponse({'status': 'authenticated'})
response.set_cookie(
key="Authorization",
value=f"Bearer {token}",
domain=".mysuperdomain.com",
httponly=True,
max_age=1800,
expires=1800,
)
return response
My DNS records are within the Cloudflare, and CNAME record for backend is proxied:
Typ Name Content TTL Proxy status
CNAME api limitless-starfish-something.herokudns.com Auto Proxied
SSL/TLS encryption mode is Flexible (Encrypts traffic between the browser and Cloudflare). Backend at Heroku has no SSL Certificate therefore I set flexible SSL/TLS encryption mode.
Maybe it is somehow related to above config?
I think this happens because you didn't add a CORS middleware to your app, in FastAPI, allow_credentials is set to bool = False in default. But you can change that easily.
First you need to import CORSMiddleware from fastapi.middlewares
from fastapi.middleware.cors import CORSMiddleware
Then we can add a middleware to our app
app.add_middleware(
CORSMiddleware,
allow_credentials=True,
)
Also you can add origins and all other stuff with CORSMiddleware, for more related info check FastAPI-CORS out.

Why Set-Cookie header not added?

I have a question, I do not understand why the cookie is not added. I have a frontend and backend on separate servers and at various ports, I hit the backend for a token. In response, he gets it, however, it is not set. Could someone look? Secure and httpOnly is deactivated. On the frontend side I have set withCredentials: true.
On the frontend side:
axios.defaults.withCredentials = true;
axios.post('http://backendhost:8584/login', user).then(response => {
if(response){
this.props.history.push('/home');
}
});
Cors On the api side:
#Bean
CorsConfigurationSource corsConfiguration() {
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.setAllowedOrigins(Collections.singletonList("*"));
config.setAllowedMethods(Collections.singletonList("*"));
config.setAllowedHeaders(Collections.singletonList("*"));
UrlBasedCorsConfigurationSource source =
new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return source;
}
Set cookie on the auth server side:
response.addCookie(generateCookie("session-token", customer.getToken()));
public Cookie generateCookie(String key, String value) {
Cookie cookie = new Cookie(key,value);
cookie.setMaxAge(60 * 60 * 24 * 365);
cookie.setSecure(false);
cookie.setHttpOnly(false);
cookie.setPath("/");
return cookie;
}
And I get it in chrome:
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: http://frontendhost:8080
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Content-Type: application/json
Date: Tue, 16 Jun 2020 16:30:11 GMT
Expires: 0
Pragma: no-cache
Referrer-Policy: no-referrer
Set-Cookie: session-token=eyJhbGciOiJSUz; Max-Age=31536000; Expires=Wed, 16-Jun-2021 16:30:11 GMT; Path=/
transfer-encoding: chunked
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block

400 Error while trying to call a PATCH API call in React even when the OPTIONS has passed OK

I have been trying to use React to make API calls, I am able to successfully run GET and POST requests but when i ran PATCH, it return 400 error which is bad request. But i am able to run the same request successfully on Postman.
I have a feeling I am not passing the data in the right format, but the body and the headers are being passed in the same form as in the POST method which is working fine.
Also, the preflight request is being passed as OK (200) but the connection closes at PATCH request
This is my request, where the Id is being received from another component which is then passed to the API.
class AddPatients extends React.Component {
constructor(props)
{
super(props);
this.state = {message :''};
console.log(this.props.list_id);
}
addPatients(url)
{
let xhr = new XMLHttpRequest();
xhr.open('PATCH',url, true);
xhr.setRequestHeader("Authorization", "Auth_key");
xhr.setRequestHeader("X-OHP-Developer-API-Key", "API_Key ");
xhr.setRequestHeader("Content-Type", "application/json");
xhr.setRequestHeader("Accept", "application/json");
let body = {
data:
[
{
//record 1
},
{
//record 2
}
]
}
const res = xhr.send(JSON.stringify(body));
console.log('the response is', res);
}
onButtonClick()
{
var url = 'the url link/' + this.props.list_id;
this.addPatients(url);
}
render() {
return(
<div>
<button onClick={this.onButtonClick.bind(this)}> Add patients to the list </button>
{this.props.list_id}
</div>
);
}
}
export default AddPatients;
It has nothing to do with the OPTIONS request i think because it returns OPTIONS IN Allow-Methods options like:
Request Method: OPTIONS
Status Code: 200 OK
Remote Address: 18.236.241.179:443
Referrer Policy: no-referrer-when-downgrade
HTTP/1.1 200 OK
Date: Tue, 31 Dec 2019 01:03:22 GMT
Server: Apache
Strict-Transport-Security: max-age=63072000; includeSubDomains
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET,POST,HEAD,DELETE,PUT,OPTIONS,PATCH
Access-Control-Allow-Headers: authorization,x-requested-with,x-ohp-developer-api-key,content-type
Vary: Accept-Encoding
Content-Encoding: gzip
Content-Length: 336
Keep-Alive: timeout=15, max=98
Connection: Keep-Alive
Content-Type: text/html; charset=iso-8859-1
Sorry, I resolved it. Was passing wring syntax in the JSON body, stupid mistake.

Sending Zendesk ticket to salesforce

I want to send Zendesk ticket to Salesforce.
I have used http target option in extension in zendesk and set the url of my visualforce page (Url: https://c.ap4.visual.force.com/apex/restOutput) and also enabled the Basic Authentication.
When i send the test data from zendesk no logs are generated in salesforce developer console.
The response sent by zendesk after sending the test data.
`
HTTP/1.1 200 OK
Date: Tue, 31 Jul 2018 04:53:09 GMT
Public-Key-Pins-Report-Only: pin-sha256="9n0izTnSRF+W4W4JTq51avSXkWhQB8duS2bxVLfzXsY="; pin-sha256="5kJvNEMw0KjrCAu7eXY5HZdvyCS13BbA0VJG1RSP91w="; pin-sha256="njN4rRG+22dNXAi+yb8e3UMypgzPUPHlv4+foULwl1g="; max-age=86400; includeSubDomains; report-uri="https://calm-dawn-26291.herokuapp.com/hpkp-report/nullm";
Expect-CT: max-age=0; report-uri="https://calm-dawn-26291.herokuapp.com/Expect-CT-report/nullm";
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Content-Security-Policy: upgrade-insecure-requests
X-Robots-Tag: none
Cache-Control: no-cache,must-revalidate,max-age=0,no-store,private
Set-Cookie: BrowserId=EKk-yAZfRfOSD0AHf2etLA;Path=/;Domain=.force.com;Expires=Sat, 29-Sep-2018 04:53:09 GMT;Max-Age=5184000
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Content-Type: text/html; charset=UTF-8
Vary: Accept-Encoding
Content-Encoding: gzip
Transfer-Encoding: chunked
Connection: close
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head></head><body onload="document.forms['workerForm'].submit()">
<form accept-charset="UTF-8" action="https://ap4.salesforce.com/visualforce/session" enctype="application/x-www-form-urlencoded" id="workerForm" method="post" name="workerForm" ><input type="hidden" name="url" id="url" value="https://c.ap4.visual.force.com/apex/restOutput" />
</form>
</body>
</html>
`
My visual page code is as follows:
`
<apex:page controller="myController">
</apex:page>
`
My controller is defined as
`
#RestResource(urlMapping='/restOutput')
global class myController {
public myController(){
System.debug('constructor');
callBack();
}
#HttpPost
global static void callBack()
{
RestRequest req = RestContext.request;
RestResponse res = RestContext.response;
System.debug(req.requestBody);
//System.debug(ApexPages.currentPage().getParameters());
}
}
`
Finally i got the solution,
I was making mistake in consuming the api.
To consume the salesforce api, send a post request with authorization header(containing access token),post data. The Request seems similar to.
curl --request POST \
--url https://ap4.lightning.force.com/services/apexrest/restOutput \
--header '<access_token or sessionID' \
--data '{\n"ticket_title":"Workbench Api Request Test",\n"ticket_description":"This is just a test Description",\n"ticket_status":"New",\n"requester_firstname":"Ebizon",\n"requester_lastname":"NetInfo",\n"requester_email":"contact#ebizontek.com"\n}\n'

Resources