Ember Simple Auth Device persist authentication information in LocalStorage - ruby-on-rails

I can't seem to find a direct answer that deals with a code example of how it works but I'm using simple-auth 0.7.3. with ember-cli and simple-auth-devise.
I can authenticate just fine but once I refresh the page session is killed. I saw a previous post and the guy didn't have the right object but what about a case when the object is correct?
{"authenticator":"simple-auth-authenticator:devise","user_id":53,"user_token":"Vm2TwefZCwaAo8hfg&pT","user_email":"user9#redphone.com"}
Im not the sharpest knife in the draw but I'm hoping someone can shed some light on why a session is killed and how/where to prevent it.

Based on the what is being put in the localstorage:
{"authenticator":"simple-auth-authenticator:devise","user_id":53,"user_token":"Vm2TwefZCwaAo8hfg&pT","user_email":"user9#redphone.com"}
Ember simple auth is using "user" as a resourceName in the config:
Ember Simple Auth Devise
The resourceName is normally added when the server endpoint is expecting more than email,password. In this case the endpoint expects user_email,user_password.
And by default When you refresh a page simple auth checks the localstorage for
email,token. You have to also change that to user_email, user_token.
in the Config:
ENV['simple-auth-devise'] = {
authorizer: 'simple-auth-authorizer:devise',
serverTokenEndpoint: ENV.APP.HOST+'/' + ENV.NAMESPACE +'/users/sign_in',
resourceName: 'user',
tokenAttributeName: 'user_token',
identificationAttributeName: 'user_email'};
And that resolves the persistence issue.

Related

Can login or register only with Firefox using Sanctum API authentication (CSRF token mismatch)

I am developing an SPA with Laravel 9, Vuejs 3 and Sanctum. I am newbie to vue and to Sanctum and I use the sanctum API authentication instead of the token authentication.
At this stage I am in dev and run the embedded laravel server for laravel app and vite server for SPA.
Everything is going smoothly when I sign in and out using the Firefox browser. But when I use Google Chrome or other browser based upon chrome (Brave, Vivaldi, chromium) I cannot sign in nor register. I get a CSRF token mismatch response.
Here are my login an register methods from vuex 's store
actions: {
async register({ commit }, form) {
console.log("in register of index");
await axiosClient.get("/sanctum/csrf-cookie");
return axiosClient.post("/api/register", form).then(({ data }) => {
console.log("data dans index");
console.log(data);
return data;
});
},
async login({ commit }, user) {
await axiosClient.get("/sanctum/csrf-cookie");
return axiosClient
.post("/api/login", user)
.then(({ data }) => {
commit("SET_USER", data);
commit("SET_AUTHENTICATED", true);
//commit("setAuth", true);
return data;
})
.catch(({ response: { data } }) => {
commit("SET_USER", {});
commit("SET_AUTHENTICATED", false);
});
},
Could somebody help me making out what is wrong or missing?
Edited after Suben's response
I read from somebody that the trouble in Chrome could come from the domain being localhost instead of http://localhost in sanctum config.
Thus I did that and could manage to login with both browser. The trouble is that even with a satisfactory answer to login and the reception of the csrf-token now in both browser the store state is not set despite the answer in the .then function being a valid user object.
Moreover, doing 3 similar requests after that strange situation, the 3 of them being under the auth:sanctum middleware, the first failed with csrf-token mismatch, the second succeeded and the third failed also with csrf-token mismatch. Looking at the requests, they have exactly the same 3 cookies including one with the csrf-token.
My guess is, that RESTful APIs are stateless. That means, they do not worry about sessions. https://restfulapi.net/statelessness/
As per the REST (REpresentational “State” Transfer) architecture, the server does not store any state about the client session on the server-side. This restriction is called Statelessness.
When you login a user with Laravel's SPA authentication, then you ARE storing client session data on the server-side.
So you have two options:
You are moving the endpoint /api/login to web.php (logout too!) OR...
You are using the API token based login.
EDIT:
I had my problems at first too with Laravel Sanctums SPA authentication and Vue. There is a video, which goes through a lot of cases, that might help you aswell for the future (Configuration of cors.php and more): https://www.youtube.com/watch?v=It2by1dL50I

How are cookie-http-only sessions supposed to work on a SPA with a separate API server?

When trying to figure out how to authenticate with Facebook/Google in the context of an SPA I'm building, someone pointed me to Stop using JWT for sessions.
I'm trying to give it a try, using HTTP-Only Cookies. My server is Ruby on Rails with Devise and my client is JavaScript with React, although the conceptual solution is independent of specific tech I believe.
My app gets loaded by going to projectx.lvh.me and then it makes a query to api.projectx.lvh.me to fetch the current user. At the beginning it's null because the user is not logged in. When a call request is made to sign in, the response from api.projectx.lvh.me contains the session cookie, hurra! But the next request that projectx.lvh.me makes to api.projectx.lvh.me doesn't carry the cookie, so, it seems the cookie is forever lost. Even opening api.projectx.lvh.me on another tab doesn't show the cookie. Is this supposed to work? What am I missing?
I thought this was blocked by third-party cookie blocking and that's why we can't use cookies in this scenario and we have to use jwt tokens (stored on a cookie, local storage or session storage).
I managed to get cookies working in this scenario by adding config/initializers/session_store.rb to my Rails app containing:
Rails.application.config.session_store :cookie_store, key: 'session', domain: :all
which caused the session cookie to not be for api.projectx.lvh.me but for .projectx.lvh.me.
On the frontend, the API calls needed to include withCredentials, which with Axios it was the withCredentials option set to true:
Axios.post(`${apiEndPoint()}/users`, { user: values }, { withCredentials: true })
and with fetch it was the credentials option set to "include":
fetch(`${apiEndPoint()}/graphql`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: "include",
body: JSON.stringify({
query: operation.text,
variables,
}),
})

What is the best way to dynamically specify the redirect url for OAuth strategies in passport.js?

I have setup my facebook auth per passportjs docs:
var passport = require('passport')
, FacebookStrategy = require('passport-facebook').Strategy;
passport.use(new FacebookStrategy({
clientID: FACEBOOK_APP_ID,
clientSecret: FACEBOOK_APP_SECRET,
callbackURL: "http://www.example.com/facebook/callback"
},
function(accessToken, refreshToken, profile, done) { ... });
}
));
app.get('/login/facebook', passport.authenticate('facebook'))
.get('/facebook/callback', passport.authenticate('facebook', {successRedirect: '/', failureRedirect: '/login'}));
All this works fine. However, there are cases (such as token expiration) when I want to automatically redirect the user to the page that the user was on before initiating the login request. So I tried to plumb a query string param through the login request (from client to server to facebook and back). But I cant see a way to specify that in the callbackURL.
Furthermore, when I tried hard-coding some context param to the config callbackURL (eg: "http://www.example.com/facebook/callback?redir=lastUserPage") I get an OAuth parse error. Interestingly enough, Facebook does respond correctly with the access code as well as the redir param, but it fails with OAUTH exception:
FacebookTokenError: Error validating verification code. Please make sure your redirect_uri is identical to the one you used in the OAuth dialog request
at Strategy.parseErrorResponse (C:\Sources\node_modules\passport-facebook\lib\strategy.js:198:12)
at Strategy.OAuth2Strategy._createOAuthError (C:\Sources\node_modules\passport-facebook\node_modules\passport-oauth2\lib\strategy.js:345:16)
at C:\Sources\node_modules\passport-facebook\node_modules\passport-oauth2\lib\strategy.js:171:43
at C:\Sources\node_modules\passport-facebook\node_modules\passport-oauth2\node_modules\oauth\lib\oauth2.js:177:18
at passBackControl (C:\Sources\node_modules\passport-facebook\node_modules\passport-oauth2\node_modules\oauth\lib\oauth2.js:124:9)
at IncomingMessage.<anonymous> (C:\Sources\node_modules\passport-facebook\node_modules\passport-oauth2\node_modules\oauth\lib\oauth2.js:143:7)
at IncomingMessage.emit (events.js:117:20)
at _stream_readable.js:943:16
at process._tickCallback (node.js:419:13)
Note that I had this working using WIF before. I don't see any security concerns with passing additional query string parameters through the OAuth process..
Any idea how I can get past this?
I'm not sure how to do what you're asking, but for your desired end goal you could:
Save a cookie before authenticating
Authenticate the user
on the resulting callback page, check for the cookie and redirect if present.
Wouldn't this work just as easily?

Change server token end point for ember simple auth devise add on

I have an ember-cli app that uses ember simple auth(ember-simple-auth-devise) add on for authentication. I need to change the token end point of my authorizer to
http://example.com/api/v1/users/sign_in.
In my environment.js file I have added
ENV['simple-auth'] = {
authorizer: 'simple-auth-authorizer:devise',
crossOriginWhitelist: ['http://example.com'] //For CORS
};
ENV['simple-auth-devise'] = {
serverTokenEndPoint : 'http://example.com/api/vi/users/sign_in'
}
But on logging in its still posts the credentials to the default url i.e.
http://example1.com/users/sign_in.
How can I change this url to use my rails app endpoint.
Maybe the problem is that the property key is serverTokenEndpoint with a lowercase p. If you go to API docs you can see the correct property name.

Shared authentication between rails and node.js with redis store

I have a rails app and a node.js app and I use Devise to authenticate users. I store the session with Redis. Now I'd like that when a user go to the node app, the app checks through socket.io whether the user is logged in or not. I managed to get the session datas from redis but I don't know how to interpret them to check if the user is logged in.
Here is my code for the node app which checks if the _session_id exists in the database and retrieves the session datas:
io.set('authorization', function (data, accept) {
if (data.headers.cookie) {
data.cookie = cookie.parse(data.headers.cookie);
data.sessionID = data.cookie['_session_id'];
redis.get(data.sessionID, function (err, session) {
if (err || !session) {
accept('Error', false);
} else {
data.session = session;
console.log(session);
accept(null, true);
}
});
} else {
return accept('No cookie transmitted.', false);
}
});
This is what the console.log(session) gives me:
{I"_csrf_token:EFI"1HPglfkCCagvb1LLraU1CEEyx7AtDzztqAEPY5G5lNgY=;FI"warden.user.user.key;TI" User;F[iI""$2a$10$IHq2WAhwbaqR4WWajRE/Yu;T
How can I check if a user is logged in the rails app with the node app?
Thanks
EDIT: It appears that the redis store gem I use calls a Marshalling method before storing the session in database. So I bypassed the problem by overriding the Marshalling method and stored the session datas in JSON format. It's not very elegant so if you find a better way to share sessions between rails and node.js, please let me know.
It might be easier to create your own oauth api(there's a railscast on how to do this oauth). As far as I know, devise is a ruby gem and isn't really cross-platform but oauth can be used in almost any language. You can add an oauth token to devise which should allow you to pass that token to node.js.
You can easily do it doing few tweaks to the index.js method in Rails-Cookie-Parser for Express https://github.com/instore/rails-cookie-parser library to use it without Express.
This library uses Marshal npm as its dependency
Note: You might need to decodeURIComponent("cookie value") since the original cookie is URL encoded

Resources