I made an authentication api made with ruby on rails 5, and a react js frontend. It works by taking an email and password, and sending cookies back to the the browser. However when I try to make the request, the console shows the following error:
Access to XMLHttpRequest at 'http://localhost:3000/users/login' from origin 'http://localhost:3001' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: The value of the 'Access-Control-Allow-Credentials' header in the response is '' which must be 'true' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.
#react component - signin.js
axios(
`http://localhost:3000/users/login`, {
method: "post",
withCredentials: true,
data: {email: this.state.email, password: this.state.password}
})
.then(response => {
console.log(response)
})
.catch(error => console.log(error))
}
#rails login function
def login
#fetch user
user = User.find_by(email: params[:email].to_s.downcase)
#authenticate method
if user && user.authenticate(params[:password])
#disallow unconfirmed emails
if user.confirmed_at?
auth_token = JsonWebToken.encode({user_id: user.id})
##
cookies.signed[:jwt] = {value: auth_token, httponly: true}
render json: {auth_token: auth_token, email:user.email},
status: :ok
else
render json: {error: 'Email not verified' }, status:
:unauthorized
end
else
render json: {error: 'Invalid username / password'}, status:
:unauthorized
end
end
I think You should use rack cors.
Add gem 'rack-cors' in your rails app Gemfile
gem 'rack-cors'
Add below code to config/application.rb
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
origins 'example.com'
resource '*',
headers: :any,
methods: [:get, :post, :put, :patch, :delete, :options, :head]
end
end
Instead of 'example.com' in origins, you can provide your origin endpoint or use '*' to allow all.
Related
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
})
)
}
I am creating a React app with a Go server. I set the cookie on the login request's response with http.cookie, which sends it back as seen in the network tab. But the browser doesn't save it. Tried with Chrome and Firefox. What am I doing wrong?
// Cors handler
r.Use(cors.Handler(cors.Options{
AllowOriginFunc: AllowOriginFunc,
AllowedMethods: []string{"GET", "POST", "DELETE"},
AllowedHeaders: []string{"*"},
AllowCredentials: true,
}))
func AllowOriginFunc(r *http.Request, origin string) bool {
if origin == "http://localhost:3000" || origin == "http://127.0.0.1:3000" {
return true
}
return false
}
// End of Login route sending back the token
userDetails := types.User{Name: user.Name, Email: user.Email, Profile_Pic: user.Profile_Pic}
cookie := &http.Cookie{Name: "accessToken", Value: token, MaxAge: int(maxAge), Path: "/api", HttpOnly: true, SameSite: http.SameSiteLaxMode}
http.SetCookie(w, cookie)
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(userDetails)
Edit: Screenshots of the network tab.
Response headers
Request headers
Anybody else who comes across a similar problem, and is using the Fetch API, try setting 'credentials: "include"' in your fetch request that is EXPECTING A COOKIE IN THE RESPONSE. The browser then set the cookie it got in the response.
I had the wrong assumption that the 'credentials' flag must be set for requests that occur after the cookie is received. Finally working. Can't believe I spent 12 hours on setting a cookie smh.
fetch(`${url}/login`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
credentials: "include", // This here
body: JSON.stringify({
email: userDetails.email,
password: userDetails.password,
}),
}).then((response) => { ...
please try to put you cookie in header filed:"Set-Cookie".
e.g:
w.Header().Set("Set-Cookie","cookieName=cookieValue")
Make sure the response header has this field, and check it in you browser devtools.
I have a React app that calls into a Rails App.
The React app is hosted on Netlify, and the Rails app on Heroku.
Locally, I am able to create a session and have the rails app set a cookie in the browser.
The network requests work just fine. However, after deploying to Netlify the production app isn't setting cookies.
Here is my login function
fetch(`${API_ROOT}/api/sessions`, {
headers: {'Content-Type': 'application/json'},
method: 'post',
credentials: 'include',
withCredentials: true,
body: JSON.stringify(body)
})
.then(response => response.json())
.then(data => {
if (data.status === 'created' && data.user){
props.handleLogin(data)
navigate("/");
} else
{
setAuthErrorState({
isError: true,
message: data.message
});
}
});
}
And my session_store.rb and cors.rb files
cors (domain redacted)
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
origins "http://localhost:3000"
resource "*", headers: :any, methods: [:get, :post, :put, :patch, :delete, :options, :head],
credentials: true
end
allow do
origins "https://www.[domain].io"
resource "*", headers: :any, methods: [:get, :post, :put, :patch, :delete, :options, :head],
credentials: true
end
end
session_store.rb
Rails.application.config.session_store :cookie_store, key: "_authentication_app"
I would request you to go to the "Network" tab in the dev tools of your browser and go to the request :'${API_ROOT}/api/sessions', and and confirm 2 things in the response headers of this request:
Is your React app domain name(i.e,: "https://www.[domain].io") present in the header Access-Control-Allow-Origin of the response? If not, what is the header set to ?
Is the said cookie present in the Cookie header of the response ? If not, please mention.
Purpose of this activity: To pinpoint the issue to either a CORS red flag by the browser , or the cookie not having the proper domain name set in it, and hence the cookie might appear to vanish.
This post helped me resolve the issue:
https://medium.com/#ArturoAlvarezV/use-session-cookies-to-authenticate-a-user-with-a-rails-api-backend-hosted-in-heroku-on-one-domain-f702ddf8c07
I faced a problem during send GET request to a django view from react, and those view redirect to GOOGLE_AUTH_ENDPOINT., and this url hit a callback function. But after request from react, it give this error:
Access to fetch at "google auth url" (redirected from 'localhost:8000') from origin 'localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
view
class Glogin(APIView):
params = {
'client_id': CLIENT_ID,
'response_type': 'code',
'scope': 'openid email profile',
'redirect_uri': CALLBACK_DOMAIN,
'state': state,
}
if APPS_DOMAIN:
params['hd'] = APPS_DOMAIN
def get(self,request):
request.session['googleauth_csrf'] = state
request.session['next'] = request.META.get('HTTP_REFERER', None)
print('Here')
print(urlencode(self.params))
return HttpResponseRedirect("%s?%s" % (GOOGLE_AUTH_ENDPOINT, urlencode(self.params)))
#data = {'link':GOOGLE_AUTH_ENDPOINT, 'params':self.params}
#return Response(data)
ReactJs
static GLogIn() {
return fetch("http://127.0.0.1:8000/glogin/", {
//method: "POST",
method: "GET",
headers: {
"Content-Type": "application/json",
},
//body: JSON.stringify(body),
}).then((response) => response.json());
}
URL
urlpatterns = [
path('', include(router.urls)),
path('auth/', obtain_auth_token),
path('login/',views.LogInViewSet.as_view()),
path('logout/',views.LogOutViewSet.as_view()),
path('articles/',views.ArticlesView.as_view()),
path('articles/<int:pk>/',views.ArticlesView.as_view()),
path('glogin/',views.Glogin.as_view()),
path('callback/',views.Callback.as_view(), name='googleauth_callback'),
#path('articales/',views.ArticlesViewSet.as_view())
]
settings.py
CORS_ORIGIN_WHITELIST = (
'localhost:3000',
#'accounts.google.com',
#'accounts.google.com/o/oauth2/v2'
)
CORS_ALLOW_HEADERS = [
'accept',
'accept-encoding',
'authorization',
'content-type',
'dnt',
'origin',
'user-agent',
'x-csrftoken',
'x-requested-with',
]
Put a hosts entry in the /etc/hosts file for 127.0.0.1
127.0.0.1 myfakedomain.local
Then add this to the CORS_ORIGIN_WHITELIST
'myfakedomain.local:8000',
Then you can access cors redirects. Chrome blocks them unless they are on special domains. Especially localhost.
Then send your browser to http://myfakedomain.local:8000
I'm building a SPA with Reactjs and laravel as API. I use axios to make API calls, some requests work without any issue, however on some pages when I make a request i'll receive a error that the the request is blocked by the CORS policy.
In laravel i'm using the spatie/laravel-cors package to add CORS headers so the preflight requests won't get blocked, by default it will allow any type of request from any origin.
Request:
componentDidMount() {
const url = BACKEND_URL+API+'/XXX/';
let headers = {
"Authorization": "Bearer " + token,
"Content-Type": "application/json",
"Accept": "application/json",
}
axios({
url: url,
method: "GET",
headers: headers,
credentials: 'same-origin',
})
.then(response => {
const data = response.data.data;
this.setState({
data: data,
loading: false
})
})
.catch(error => {
console.log(error);
});
}
Expected response ( from localhost )
{
campaign_id: XXX
category: []
form_entry: {id: XXX, form_id: XXX, lead_id: XXX, fields: Array(1)}
form_entry_id: XXX
id: XXX
landing__page_id: XXX
last_contacted: "XXX"
name: "XXX"
notes: XXX
status: [{…}]
tag: []
}
Error message:
Access to XMLHttpRequest at 'XXX.XXX' from origin 'XXX.XXX' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: Redirect is not allowed for a preflight request.
from localhost everything works fine with the same setup, the strange thing is that i'm only getting the errors once I put both front and backend online.
Anyone has a clue why this is happening?