Reputation: 141
Setup is an Ember frontend with a rails backend using JSON api.
Everything is going fine but some questions do come up:
How do I ensure only the emberjs application consumes the api? I wouldn't want a scripter to write an application to consume the backend api.
It all seems pretty insecure because the EmberJS application would come in a .js file to the client.
How would I ensure a user is really that user if everyone has access to a JS console?
Upvotes: 1
Views: 546
Reputation: 6366
I am using Ember Simple Auth to great effect for user authentication and API authorisation.
I use the Oauth 2 user password grant type for authentication of the user and authorising the application by way of a bearer token which must be sent on all future API requests. This means the user enters their username/email and password into the client app which then sends to the server via HTTPS to get an authorisation token and possibly a refresh token. All requests must be over HTTPS to protect disclosure of the bearer token.
I have this in app/initializers/auth:
Em.Application.initializer
name: 'authentication'
initialize: (container, application) ->
Em.SimpleAuth.Authenticators.OAuth2.reopen
serverTokenEndpoint: 'yourserver.com/api/tokens'
Em.SimpleAuth.setup container, application,
authorizerFactory: 'authorizer:oauth2-bearer'
crossOriginWhitelist: ['yourserver.com']
In app/controllers/login.coffee:
App.LoginController = Em.Controller.extend Em.SimpleAuth.LoginControllerMixin,
authenticatorFactory: 'ember-simple-auth-authenticator:oauth2-password-grant'
In app/routes/router.coffee:
App.Router.map ->
@route 'login'
# other routes as required...
In app/routes/application.coffee:
App.ApplicationRoute = App.Route.extend Em.SimpleAuth.ApplicationRouteMixin
In app/routes/protected.coffee:
App.ProtectedRoute = Ember.Route.extend Em.SimpleAuth.AuthenticatedRouteMixin
In templates/login.hbs (I am using Ember EasyForm):
{{#form-for controller}}
{{input identification
label="User"
placeholder="[email protected]"
hint='Enter your email address.'}}
{{input password
as="password"
hint="Enter your password."
value=password}}
<button type="submit" {{action 'authenticate' target=controller}}>Login</button>
{{/form-for}}
To protect a route I just extend from App.ProtectedRoute
or use the protected route mixin.
Your server will need to handle the Oauth 2 request and response at the configured server token endpoint above. This is very easy to do, Section 4.3 of RFC 6749 describes the request and response if your server side framework doesn't have built-in support for Oauth2. You will need to store, track and expire these tokens on your server however. There are approaches to avoiding storage of tokens but that's beyond the scope of the question :)
I have answered the backend question and provided example rails example code for user authentication, API authorisation and token authentication here
Upvotes: 0
Reputation: 9092
You can extend the RESTAdapter
and override the ajax
method to include your authentication token in the hash, and you need make sure your controllers validate that token.
In my environment (.NET), I have the authentication token in a hidden field of the document which my app renders, so my ajax
override looks like this:
App.Adapter = DS.RESTAdapter.extend({
ajax: function(url, type, hash, dataType) {
hash.url = url;
hash.type = type;
hash.dataType = dataType || 'json';
hash.contentType = 'application/json; charset=utf-8';
hash.context = this;
if (hash.data && type !== 'GET') {
hash.data = JSON.stringify(hash.data);
}
var antiForgeryToken = $('#antiForgeryTokenHidden').val();
if (antiForgeryToken) {
hash = {
'RequestVerificationToken': antiForgeryToken
};
}
jQuery.ajax(hash);
}
});
The token can come from a cookie or whatever you define, as long as you're able to include it in the request header and have your controllers validate it (possibly in before_filter
), it should enough.
Then in the Store, pass the new adapter instead of the default (which is RESTAdapter)
App.Store = DS.Store.extend({
revision: 12,
adapter: App.Adapter.create()
})
Note: RESTAdapter#ajax
will be changed in favor or Ember.RSVP
, making this override deprecated. It must be updated after the next release, but should be ok for revision 12.
Upvotes: 1