Facing issue with setting up http only cookie while using react js for frontend and django rest framework for backend - reactjs

I started building a basic Authentication system with JWT token authentication using rest API and react js. But, I was facing an issue while setting my cookie from the Django views sent using rest_framework.response.Response object. Now, the problem is that in the Django server the cookie is set, but in this case, while integrated with react js it fails. Django server is running on port 8000 and react js on 3000.
#api_view(['POST'])
def login(request):
try:
username = request.data['username']
password = request.data['password']
user = authenticate(request=request, username=username, password=password)
if user:
refresh = RefreshToken.for_user(user)
response = Response()
response.data = {
"status": True,
"Access": str(refresh.access_token)
}
response.set_cookie(key='refreshtoken', value=(refresh), httponly=True, samesite=None)
return response
else:
return Response(FALSE_RESPONSE)
except Exception as e:
print(e)
return Response(FALSE_RESPONSE)
This is the axios request, I was making from the frontend side.
axios({
method: "POST",
url: "http://localhost:8000/user-api/login/",
data: {
username:username,
password:password
},
credentials: 'include',
withCredentials: true
})
.then(response => {
console.log(response)
if(response.data['status']) {
setAccessToken(response.data['Access'])
setIsAuthenticated(true)
setLoginModal(false)
} else {
alert("Error! Credentials doesn't match.")
}
})
.catch(error => {
console.log(error)
})
This axios request generates no errors and I was successfully getting the token, but the refresh token was not getting saved in the cookies.
# settings.py
CORS_ALLOWED_ORIGINS = [
"http://localhost:3000",
"http://127.0.0.1:3000"
]
CORS_ALLOW_CREDENTIALS = True
Help me with this issue!!

Related

Redirect from React ASP.NET application to SSO login page blocked by CORS Policy

My application is set up as of follows:
Frontend is React (SPA),
Backend is ASP.NET Core 6, and
User will be authenticated via SSO using SAML2 protocol (the SAML code is implemented on the ASP.NET side, not React)
When the React page is loaded, it will send a POST request via fetch API to the ASP.NET server which then will trigger to load the SSO page (I'm actually confused by this as I'm unsure how this would work with React since React handles all the routing piece here). However, I keep getting an error that is saying:
"Access to fetch at 'https://sso.example.com/saml/idp/profile/redirectorpost/sso?SAMLRequest=xxxx&RelayState=ReturnUrl%3D%252Fexample%252Fexampleapp%252FGetLoggedInUser' (redirected from 'https://forms.test.com/test/testapp/GetLoggedInUser') from origin 'https://forms.test.com' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: Redirect is not allowed for a preflight request."
I attempted to fix this by changing the attributes in the fetch headers by all resulted in the same CORS error.
Here's my fetch api code on the frontend:
fetch("testapp/GetLoggedInUser", {
method: "POST",
headers: {
'Accept': 'application/json',
'Access-Control-Allow-Origin': '*',
},
body: data
}).then((response) => {
const contentType = response.headers.get("content-type");
if (contentType && contentType.indexOf("application/json") !== -1) {
return response.json().then((data) => {
return data;
}).catch((err) => {
console.log(err);
})
}
else {
return response.text().then((data) => {
return data;
}).catch((err) => {
console.log(err);
})
}
});
My backend (ASP.NET Core 6)
//[Authorize] is an attribute from Microsoft.AspNetCore.Authorization.AuthorizeAttribute
[Authorize]
[HttpPost("GetLoggedInUser")]
public string GetLoggedInUserData()
{
this.CheckSession();
Person Person = new ActiveUser(this.ID, GlobalVariables.ProcessCode);
dynamic P = Person.GetSimple();
User User = new User(this.WhitworthID);
P.LastAccess = DateTime.Now;
User.SetLastAccess();
return JsonConvert.SerializeObject(P);
}
Can anyone advise me on how to get past the CORS error when redirecting from React to the SSO page?
Modify your code to check if the response is redirected,if so redirect the browser to the new url.
fetch(url, { method: 'POST', redirect: 'manual'})
.then(response => {
// HTTP 301 response
// HOW CAN I FOLLOW THE HTTP REDIRECT RESPONSE?
if (response.redirected) {
window.location.href = response.url;
}
})
.catch(function(err) {
console.info(err + " url: " + url);
});

Cross-site cookie not set in split-stack Rails 7/React 18 app

I have a Rails 7 API and React 18 front end, both deployed to Heroku on separate subdomains. Locally, the Rails app runs on localhost:3000 and the React app on localhost:3001. I need to set a CSRF cookie on the front end from the back end. I'm using the rack-cors gem on the Rails side to handle cross-origin requests. The CSRF cookie is set on the server side when the server gets a request from the front end to create an authenticated session. When the server verifies the authentication token (received by the front end from Google, it is supposed to set a CSRF-TOKEN cookie that will then be set in the browser and included by the front end in subsequent authenticated requests. I can see that the Set-Cookie header is being sent with the following value, but the cookie is not being set in Chrome in my dev environment (the site won't load in Firefox or Safari for other reasons, so I'm unable to test cookie settings in other browsers right now):
Set-Cookie: CSRF-TOKEN=c2Cn8OMs4IhgI5A4g1GC1XjG5hEc6RRW7dSPynxNbgsb0vsoWCr07yulWVzUwFYNP7dD8ARMps3pz5MMngKdog; path=/; secure; SameSite=None
My CORS initializer (/config/initializers/cors.rb) - configatron.client_origin is set to http://localhost:3001 in dev environments or https://sim.danascheider.com in prod):
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
origins configatron.client_origin
resource '*',
headers: :any,
methods: %i[get post put patch delete options head],
credentials: true
end
end
Rails.application.config.action_controller.forgery_protection_origin_check = false
The code that sets the cookie (this works based on inspecting the response in the dev tools):
cookies['CSRF-TOKEN'] = {
value: form_authenticity_token,
domain: :all,
same_site: :none,
secure: true
}
And the code that makes the request from the front end:
export const logInUser = token => {
const uri = `${backendBaseUri}/sessions`
const body = JSON.stringify({ token })
return(
fetch(uri, { method: 'POST', crossDomain: true, body, headers: { 'Content-Type': 'application/json' } })
.then(resp => {
if (resp.status === 401) throw new AuthorizationError()
return resp
})
)
}
I've looked in various other sources and all say to do what I'm already doing.
The cookie ended up being set when I added the credentials: 'include' option to my fetch request. I don't fully understand why this worked or was required on this request, but it did:
export const logInUser = token => {
const uri = `${backendBaseUri}/sessions`
const body = JSON.stringify({ token })
return(
// Replace crossDomain: true (not a real option for 'fetch') with credentials: 'include'
fetch(uri, { method: 'POST', credentials: 'include', body, headers: { 'Content-Type': 'application/json' } })
.then(resp => {
if (resp.status === 401) throw new AuthorizationError()
return resp
})
)
}

How to fetch data from a REST API by using an API-Token

I'm trying to fetch data from the Jira Rest API in my React application by using the Axios library for http requests. An API token is necessary, in order to access data via the Jira API. I generated an API token in my Jira account settings, but I can't figure out, how to include it in my http request to gain access.
This is the endpoint provided by the Jira documentation for getting an issue from the Jira board:
curl -u admin:admin http://localhost:8080/jira/rest/api/2/issue/TEST-10 | python -mjson.tool
This is the React state hook for setting the data to the fetched data:
const [jiraTicket, setJiraTicket] = useState([]);
This is the fetch function for the API request (${} will be filled with user input):
function getJiraTicket() {
axios.get(`${username}:${apiToken}#Content-Type:application/json/https:/${jiraSiteName}.atlassian.net/rest/api/2/issue/${projectKey}-${ticketId}`)
.then((res) => {
const data = res.data;
setJiraTicket(data);
})
}
The button inside the react component return should invoke the fetch function:
return(
<Container>
<Button onClick{getJiraTicket()}>Fetch Jira Ticket</Button>
</Container>
);
This is the error I'm currently getting, because the authorization is not working the way I did it
(I replaced the provided username, API token etc. for this example):
GET http://localhost:3000/username:apitoken#https:/sitename.atlassian.net/rest/api/2/issue/projectkey-ticketid 404 (not found)
Edit:
My current approach:
function getJiraTicket() {
axios.get(`${userName}:${apiToken}#https://${siteName}.atlassian.net/rest/api/2/issue/${projectId}-${ticketId}`,{
auth: {
username: userName,
password: apiToken,
},
withCredentials: true
})
.then((res) => {
const data = res.data;
console.log(data);
setJiraTicket(data);
})
.catch(err => {
// This error means: The request was made and the server responded with a status code
if(err.res) {
console.log(err.res.data);
console.log(err.res.status);
console.log(err.res.headers);
console.log("request was made and server responded with status");
// The request was made but no response was received
} else if (err.request) {
console.log(err.request);
console.log("request was made, but no response was received");
// Something happened in setting up the request that triggered an error
} else {
console.log("Error", err.message);
console.log("request is note set up correctly");
}
console.log(err.config);
})
Current error, which I defined accordingly to the axios doc: "request was made, but no response was received"
Endpoint that works well in Postman (Basic auth is provided in Postman):
https://sitename.atlassian.net/rest/api/2/issue/projectid-ticketid
Update: CORS access isn't allowed, when an application tries to access the Jira API endpoints directly. This restriction takes place in order to prevent random authenticated requests to the specific Jira site, because the access is based on session based authentication. However the API endpoints can be accessed, if OAuth 2.0 is used instead of Basic auth, because the application will redirect the user to the Jira auth itself via this link:
https://auth.atlassian.com/authorize? audience=api.atlassian.com&
client_id=YOUR_CLIENT_ID&
scope=REQUESTED_SCOPE_ONE%20REQUESTED_SCOPE_TWO&
redirect_uri=https://YOUR_APP_CALLBACK_URL&
state=YOUR_USER_BOUND_VALUE& response_type=code& prompt=consent
Source: https://developer.atlassian.com/cloud/jira/platform/oauth-2-3lo-apps/#known-issues
Axios uses a headers config for get/post so you should not include them in your URL. Here is a general example of how you should construct the URL and apply headers:
let axiosUrl = `https://${jiraSiteName}.atlassian.net/rest/api/2/issue/${projectKey}-${ticketId}`
axios({
baseURL: axiosUrl,
method: 'get',
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Origin", "*"
},
//timeout: 2000,
auth: {
username: userName,
password: apiToken,
}
})
.then((res) => {
setJiraTicket(res.data);
})
.catch(function (error) {
console.log(error);
});

Sails 1.4.0 CSRF Token sent from React App getting rejected 403

for the last three days I got stuck at this problem and it is getting very frustrating. I don't know what else to try.
I am running a Sails app on localhost:1337 and a create-react-app on localhost:3000.
I enabled csrf on the backend and followed the sails documentation to implement it.
I have created the route
'GET /grant-csrf-token': { action: 'security/grant-csrf-token' } and it works fine, I get the token. If I use postman the token is accpeted and my login form works.
In React however, I receive the token but I get 403 Forbidden error when I submit the post request to login.
useEffect(async () => {
let csrfToken;
try {
let csrfTokenRequest = await Axios.get(
`${state.serverUrl}/grant-csrf-token`
);
csrfToken = csrfTokenRequest.data["_csrf"];
dispatch({
type: "csrf",
value: csrfToken,
});
} catch (err) {
dispatch({
type: "flashMessage",
value: "There was an error.",
});
}
}, []);
I tried various ways to send the token with my post request:
await Axios.post(
`${appState.serverUrl}/login`,
{
emailAddress,
password,
_csrf: appState.csrf,
},
{ withCredentials: true }
);
I also tried setting it as a default header like so:
Axios.defaults.headers.post["X-CSRF-Token"] = appState.csrf;
and set cors allowRequestHeaders parameter to allowRequestHeaders: 'content-type, X-CSRF-Token',
I also tried sending it as a query parameter
`/login?_csrf=${encodeURIComponent(appState.csrf)}`
I also tried various cors settings inside Sails, currently it is setup like so:
cors: {
allRoutes: true,
allowOrigins: [
'http://localhost:3000',
],
allowCredentials: true
}
So just to clarify once again:
The /grant-csrf-token route works fine. I am receiving the token
It works in Postman
In React I get 403 error
Could you try something like that:
Send the csrf in header and allow Sails to process the request header.
// config/security.js
cors: {
allRoutes: true,
allowOrigins: ['http://localhost:3000'],
allowCredentials: true,
allowRequestHeaders: ['content-type', 'x-csrf-token', 'authorization'],
},

How can we send OAuth2.0 with axios in React js

I am working on one authentication problem where i have to implement OAuth2.0 authentication for my React App. Is there any way that i can use that authentication with Axios Promise based library???
You will have to pass your Token in the header.
See below;
const instance = axios.create({
baseURL: 'http://localhost/api/',
headers: {'Authorization': 'basic '+ token}
});
instance.get('/path')
.then(response => {
return response.data;
})
OR
Set an Authorization cookie in the browser once you get your token.
The browser will always send the Authorization cookie in each request made to the server. You won't have to pass it through Axios get/post.
UPDATE:
In order to get the access token from the oAuth server, pass the client_id, client_secret, scope and grant_type as follows;
var axios = require("axios");
axios.request({
url: "/oauth/token",
method: "post",
baseURL: "http://sample.oauth.server.com/",
auth: {
username: "myUsername", // This is the client_id
password: "myPassword" // This is the client_secret
},
data: {
"grant_type": "client_credentials",
"scope": "public"
}
}).then(respose => {
console.log(respose);
});
I am assuming that you are using grant_type = "client_credentials", if not, then based on the grant_type you use, you will have to also change the request parameters accordingly.

Resources