I am trying to write an iOS app using Phonegap to communicate with my Rails3 app. I cannot figure out how to handle authentication. I am using Devise/Warden in my Rails app.
I am able to login successfully in my iOS app via ajax but then subsequent calls are giving me a "Unauthorized". It appears that the session cookie isn't getting set in my iOS app.
How do I keep my iOS app aware of my rails authenticated session?
The answer was two fold.
First I had to add this to my ajax requests in the iOS app:
xhrFields: {
withCredentials: true
}
as in...
$.ajax({
url: ....,
type: "GET",
xhrFields: {
withCredentials: true
},
complete: hideLoader,
error: function(xhr,txt,err) {
// you can't get back anyways
window.location.hash = "";
},
success: function(data,status,res) {
window.location.hash = "";
}
});
Second I had to add this to my Application Controller in the Rails app:
def protect_against_forgery?
unless request.format.json?
super
end
end
Related
currently i used Ruby on Rails as my backend to serve API to my apps. And my front end i use React-rails gem (React.js) to get/post data and generate output. Both API and apps are on different server.
My problem here is, i cannot store response from POST request i made in react. How can i do so ? I am new in react btw.
What i want to do now is when user want to login the apps will POST request to API and get "auth_token" response. I want to store this "auth_token" response in header and access into dashboard. But i stuck now how can i store the auth_token ftw.
Below is my code for POST data :
var LoginBody = React.createClass(
{
handleClick() {
var username = this.refs.username.value;
var password = this.refs.password.value;
$.ajax({
url: 'http://localhost:3001/api/v1/users/central/signin?',
type: 'POST',
dataType: 'json',
data: { user_login:{email: username, password:password} },
success: (data) => {
this.setState({auth_token: data.auth_token});
}.bind(this),
});
},
getInitialState(){
return {
auth_token: []
}
},
If there's someone that could solve my problem and teach me how can i store in header in my apps i really appreciate it.
Just store them in local storage like:
localStorage.authToken = data.auth_token
And then whenever you need them in your future requests, just read as:
localStorage.authToken
I have trouble making ajax requests to a local Rails server using Phonegap. I have a universal whitelist in config.xml for outgoing urls as so:
<access origin="*"/>
and I have the android app with Phonegap set to load the signin page on the rails app like so:
super.loadUrl("http://10.0.2.2:3000/signin")
and I have no problem accessing any of the pages served by the local rails server. However I cannot get ajax requests to go through, the Rails server logs never show a request being received. An example ajax request:
$(document).ready(function() {
if (localStorage.getItem("auth_token") != null) {
$.ajax({
type: 'POST',
url: 'http://10.0.2.2:3000/api/v2/api_login.json',
data: {"token": localStorage.getItem("auth_token")},
success: function (data) {
alert("token saved");
window.location = data.url
}
});
}
else
{
alert("no token");
}
});
I have also tried setting the url domain in the ajax request to both 127.0.0.1:3000 and www.lvh.me:3000 with no luck.
If I access the pages through a browser and not Phonegap everything works fine. If I push the code to heroku and access the url to point to heroku everything works fine. The problem seems to be submitting ajax requests to the local server.
When user wants to perform an action that need him to login in first, devise will redirect him to the signin page and set a flash message: 'Please signin or signup before continue'. This works well with no-ajax request. But with an ajax request that also need signin first, it will response with 401 error and we should handle it manually as follows:
$.ajax({
url: '/books/user_score',
type: 'POST',
data: { score: value, book_id: book_id },
dataType: 'json',
success: function(data) {
bookStarWidget.data('starInfo', data.book_star);
setVote(bookStarWidget);
$("[name='my_vote']").rating('disable');
},
error: function(jqXHR, textStatus, errorThrown) {
if (jqXHR.status == 401) {
window.location.assign('/users/sign_in');
}
}
});
In the above code, we manually open the signin page when received the 401 error devise sends out.
The problem is that there is NO flash message set in this way. So could someone knows how to set the flash message as usual to keep a consistent behavior?
It looks like you're using javascript to set the location in the error case. Devise has a configuration option that will redirect on failure instead of returning http headers. This setting defaults to true which forces you to handle failure on xhr manually. Here's the explanation from the source: https://github.com/plataformatec/devise/blob/master/lib/devise/failure_app.rb#L120
So, in your Devise config:
config.http_authenticatable_on_xhr = false
Try the unauthenticated ajax request again and Devise will do the redirect for you, which should set up the flash messages as you expect.
I'm trying to make a simple $.ajax request to a Rails app running locally.
The request works without any issues from the console but when I make the call from the safari extension the app returns a 401 Unauthorized. I'm not sure if I need to create an api token for each user and pass that in the url string to authenticate or if there's a simple reason why devise is not processing the request even though I'm logged in. My guess is that the culprit is the before filter I have on my controller which looks like this:
before_filter: authenticate_user!
But again, I get the 401 when I am signed in to the app. Just for reference, here's the call I'm making from the extension:
$.ajax({
type: 'GET',
url: 'http://localhost:3000/playlists.json?callback=?,
dataType: 'jsonp',
success: function(data) { console.log(data); },
error: function() { console.log('Uh Oh!'); },
jsonp: 'jsonp',
crossdomain: true
});
Any help would be much appreciated.
Ok figured it out. The cookie being set was set to expire after 'session' which basically means that as soon as you navigate away from the page the session is no longer there. Thus the extension did not have access to this extension cookie. The key was to set an expiration date by default on the session cookie which you can do like this in config/initializers/session_store.rb:
YourApp::Application.config.session_store :cookie_store, {
:key => '_yourapp_session',
:expire_after => 60*24*60*60
}
You can read more about cookie types here: http://en.wikipedia.org/wiki/HTTP_cookie#Session_cookie
I have a controller action that is expected to be called by jquery. Here's the call:
$.ajax({
url: "/comments.json",
type: "POST",
dataType: "json",
data: {
commentable_id: $("#addCommentForm").attr("data-site-update-id"),
commentable_type: 'SiteUpdate',
content: $("#addCommentForm textarea#content").val()
},
success: function(comment) {
}
});
It works just fine, but for some reason, "current_user" is nil inside of the controller. If I force authenticate_user! as a filter, rails returns an HTTP Unauthorized.
How can I pass the authentication details so that this controller action works with devise? Even better, is there a way I can make this transparent? I don't want to pass the authentication details over and over for each ajax request...
In Java, once a user is logged in... they are logged in. You don't have pass anything from url to url anymore - it's in a session somewhere and it's all occuring transparently. Is there a way I can do this with rails? I don't want to focus on these details.
Thanks
EDIT: The solution is to add the following to your javascript somewhere:
$(document).ajaxSend(function(e, xhr, options) {
var token = $("meta[name='csrf-token']").attr("content");
xhr.setRequestHeader("X-CSRF-Token", token);
});
Devise does maintain sessions.
This may be your problem.