What is the right way to proceed the logout action of the User when using JWT, Rails API and a JS front-end framework, for example Ember JS ? What I'm actually doing is:
use Rails 5.2 as API
use Ember JS 3.3 as front-end
use Ember Simple Auth as OAuth add-on
example app, its master branch, works as needed
example app, its without login branch fails to logout the User
check the presence and pass in a token in every request between Rails API and Ember JS apps.
The questions I have are:
Should I keep a token value in the backend model (User, for example) ?
I need it to make another request in the background on the backend side.
Should I set the token value to nil when the User logs out in the backend ?
What am I doing wrong with ESA as for logout action ?
Actually the token value is kept in a cookie on the client side (see https://github.com/simplabs/ember-simple-auth for more details). I followed their guides and the dummy app they provide.
I also had a discussion on Ember JS Forum and tried to follow some tips and advises, still no success.
Thank you.
This answer applies to Ember 1.13 through at least 3.x.
Authentication comes in so many flavors that I think the right way to do it is whatever is an easy-to-understand fit with the back end.
Since your JWT is in a cookie, let's think of that cookie as the source of truth. Rather than doing something complicated to parse the cookie in a model hook, you could define a Service that has functions to grab the cookie, parse it, and either save the results to values on the service or return the values you need.
This gets you a few benefits. You can get the values from anywhere in your app, including adapters, and all the logic for auth lives in once place. On the other hand, you would have to handle async behavior yourself (i.e. if a route depends on having login info, you will have to manage the order of operations between authentication and route transitions).
Ember Simple Auth is quite popular because of this issue. Although there aren't out of the box features for JWTs in cookies, if you have an app with different states based on logged-in behavior, it might be a good investment to learn it.
The user model is kind of a middle ground between a hand-rolled service and Ember Simple Auth, since you can get the user model and rely on it throughout your app, plus get a little help with async. Just be careful not to scatter your auth code across your whole app.
Lastly, to trigger logout, I would create a function that destroys the cookie by setting the max age/expiration like this. If you are handling auth on a service, this means you could use Router Service and then transitionTo a login page. If you are using Ember Simple Auth, that functionality can go in the invalidate hook of your custom authenticator. Example:
invalidate() {
this._super()
document.cookie = "some_token_name=; expires=Thu, 18 Dec 2013 12:00:00 UTC; path=/"
return Promise.resolve();
}
Lastly, for passing the token to authenticate requests, if you are using Ember Data, this can be done easily in the adapter's headers method.
Related
I want to configure the Ember Simple Auth session to be authenticated for a user generated by Ember CLI Mirage on startup. During testing I can use the Ember Simple Auth authenticateSession() helper, but as far as I can tell, this isn't available during development. Is there a way to update the session before the app boots?
If you're just looking to change the behavior of Ember Simple Auth on the client and in a situation where your Ember app never makes an XHR request, Mirage won't be able to help you out here. You could fake the login request (which may set some user data), but if you want to change the logged-in user without going through xhr authentication, you'll need to use environment-based config to change how Simple Auth behaves.
(I know this is not a complete answer to your question, I just wanted to point out that Mirage only knows how to handle XHR requests, and it sounds like your problem involves client-side state.)
I am using Django v1.8 and django-rest-framework v3.2.2. I have a site with a public-facing API, which is also consumed by my own site (on the same domain) as the Ajax back-end to a JavaScript application, using GET only.
I want public users of this API to be asked for a key parameter in the URL, which I will issue manually. But I also want my JavaScript application to be able to use the API, in a way that means that other users can't just steal the key and use it.
I have set up my custom key authentication as described here, and it's working well.
However, I'm unclear on how the JavaScript application should use the API. Obviously I could just pass a dedicated key parameter in the URL, but then won't other users trivially be able to spot the key and use it?
I think I need SessionAuthentication, but how do I even start to make this work? I can't see any instructions in the DRF documentation about how I need to change my JavaScript calls to use it.
Also I don't understand how SessionAuthentication allows the Ajax app to authenticate without other users being able to see and copy the authentication.
Very grateful for any advice.
I think I need SessionAuthentication, but how do I even start to make this work? I can't see any instructions in the DRF documentation about how I need to change my JavaScript calls to use it.
SessionAuthentication is the Django's one. It uses session to authenticate a user. It's mostly transparent for ajax request as the browser will send the cookie automatically. However, if you're posting data, you need to make sure you send the CSRF token in both headers and post body.
Also I don't understand how SessionAuthentication allows the Ajax app to authenticate without other users being able to see and copy the authentication.
As said above, it uses cookies for that. They are part of the headers and thus usually not seen on the urls.
To make sure no-one else can steal user's session you need to run the site through https.
This isn't much different from regular websites.
I have website that requires authorization on all its pages. To achieve that there is server that response based on cookie. If there is cookie send ember app, login site otherwise. This allows to facebook like behavior. I am using ember-simple-auth addon to help with authorization.
Since user is never inside app without being successfully authorized, one should be able to save ember-data record object into session. This works nice for one tab but breaks horrible with multiple tabs. Is there way how to have ember-data objects in session and yet support multiple browser tabs?
Edit:
I maybe found workaround, save index into session and use peekRecord in computed property.
I have done a few mobile apps using django as my backend, and now I am working on a chrome extension. I want my users to be able to send POST requests up to the server using the app/extension but is there a way to do it without first asking the server for a CSRF token? It just saves the app from making two requests every time we want to submit data. For example, I want to update my profile on my social media app or update a wallet from a chrome extension. It would be nice to open up the profile view input the data and push it to the server. It's less sleek if I have to open the profile, then wait for it to grab a token from the server and then I can submit the data. Is there another way to do this? Or am I stuck making multiple requests every time I want to submit data?
Also, a little clarification, CSRF prevents sites from submitting forms with user's data. But what is to stop me from making a site that uses ajax or something to grab the real site and steal the CSRF token and then paste that into my cross site request form? I feel like there is a loophole here. I know that I am not quite understanding this all the way.
You can, and should, make any API endpoint CSRF exempt.
Django offers the csrf_exempt decorator for exactly this, see https://docs.djangoproject.com/en/dev/ref/contrib/csrf/#django.views.decorators.csrf.csrf_exempt.
Also CSRF is intended to prevent unintended actions being performed via GET request forgeries. It is not intended to make it impossible for an automated system to submit forms, there are captchas for that.
As for what prevents you from using AJAX to grab the whole site and extract the token is something called the Same-Origin Policy. This is implemented by the browser and prevents any AJAX call from returning data when the target of the AJAX call is a different domain without the correct headers set. (I'm not entirely sure what sandboxing is applied to chrome extensions concerning this). As such it will, or at least should, fail to get data via AJAX for normal websites, e.g. a profile page. If you want to interact with third party websites you should look into whether or not they offer an API.
Does anyone have experience creating an authentication mechanism with the new router in pre4?
Here are some of my thoughts so far:
In order to completely separate the view (Ember app) from the server (Rails app) I want to use token authentication. I will likely use Devise on the Rails server.
I need something like a before_filter equivalent in the Ember app where I can check if there is a current user and if that user has an authentication token set.
The Rails server will return the current auth token on every call. If it returns a null auth token the Ember app should detect this and transition to the unauthenticated state, redirecting to the login view.
I suspect I should be using an Ember state machine for this but I'm not sure how to proceed. Anyone tackled this problem yet?
UPDATE: Like #DustMason says in his answer, check out the awesome embercasts for authentication best-practices.
Client Side Authentication Part I
Client Side Authentication Part II
In order to completely separate the view (Ember app) from the server (Rails app) I want to use token authentication. I will likely use Devise on the Rails server.
Makes sense.
I need something like a before_filter equivalent in the Ember app where I can check if there is a current user and if that user has an authentication token set.
You can add an enter hook on routes, this is roughly equivalent to a before_filter. But not sure that's the best place to check for an auth-token.
The Rails server will return the current auth token on every call.
Makes sense. We use cookie-auth and fetch current user profile by calling /api/me but either should work.
If it returns a null auth token the Ember app should detect this and transition to the unauthenticated state, redirecting to the login view.
Thing about this approach is that (unlike rails) it's not easy to "protect" access to a particular ember routes. And no matter what a user can always pop open JS console and enter whatever state they want. So instead of thinking "user can only get into this state if authenticated" consider "what if unauthenticated user somehow navigates to this route"
I suspect I should be using an Ember state machine for this but I'm not sure how to proceed. Anyone tackled this problem yet?
Our auth needs are pretty simple so we've not found the need for a state machine. Instead we have an isAuthenticated property on ApplicationController. We use this property in application.hbs to replace the main view with a login form when a user is not authenticated.
{{if isAuthenticated}}
{{render "topnav"}}
{{outlet}}
{{else}}
{{render "login"}}
{{/if}}
From ApplicationRoute, we fetch user profile:
App.ApplicationRoute = Ember.Route.extend({
model: function() {
var profiles;
profiles = App.Profile.find({ alias: 'me' });
profiles.on("didLoad", function() {
return profiles.resolve(profiles.get("firstObject"));
});
return profiles;
}
});
Then our ApplicationController computes it's isAuthenticated property based on the profile that was returned.
I would suggest using ember-auth for that. It implements all the needed functionality and works very well in my opinion.
Also there is a demo and tutorial with Devise on Rails by the same author.
I also have implemented a basic Ember application based on Ember-auth with Devise token authentication and example Oauth for Google and LinkedIn that can be found here and is live here: https://starter-app.herokuapp.com
I recently changed from a bespoke auth system to using ember-simple-auth and found it very easy to integrate with my app. It fulfills all of the OPs requirements and also has built in support for refresh tokens.
They have a really nice API and a great set of examples. Anyone interested in token based auth should check it out.
The newly released Ember async router makes setting up a nice auth flow easier in my opinion! Check out the two-part series on http://www.embercasts.com/ for a good example
Josep's example app is really nice. I made a copy of his repo to show how to do it with ActiveRecord instead of mongoid, and also enable the Devise confirmable module. You can find it here. This repo was reconstructed from scratch, rather than forked, as I wanted to force myself to go through all of the steps to get it working. I'll update this answer if I add a fork with the necessary changes to get it to work.