I'm trying to implement custom auth with ember-simple-auth and I stuck at the start. I have app/autheticators/digest.js
import Base from 'ember-simple-auth/authenticators/base';
import Ember from 'ember';
export default Base.extend({
restore(data) {
//
},
authenticate(email, password) {
console.log(email, password);
return new Ember.RSVP.Promise(function(resolve, reject) {
Ember.run(function() {
resolve({email: email, password: password});
});
});
},
invalidate(data) {
//
}
});
app/authorizers/digest.js
import Base from 'simple-auth/authorizers/base';
import Ember from 'ember';
export default Base.extend({
header: function() {
return "test-digest";
},
authorize: function(sessionData, block) {
console.log('authorize...');
block('Authorization', this.get('header'));
}
});
Login component:
import Ember from 'ember';
import CryptoJS from 'npm:crypto-js';
export default Ember.Component.extend({
session: Ember.inject.service('session'),
actions: {
login() {
let { email, password } = this.getProperties('email', 'password');
this.get("session").authenticate('autheticator:digest',
email, CryptoJS.SHA256(password).toString()).catch((reason) => {
this.set('errorMessage', reason.error);
});
}
}
});
Authentication called properly (I hope), but "authorize" in authorizer never called. I also tried add some values to ENV:
ENV['simple-auth'] = {
authorizer: 'authorizer:digest',
crossOriginWhitelist: ['http://prod-drunkedguru.rhcloud.com:80/'] // ['*'] I also tried
};
But nothing changed. What I'm doing wrong?
P.S. I'm using EmberJS 1.13.0 with EAS 1.0.
I assume you're using ESA 1.0. In that version the authorizer isn't automatically called anymore but you need to call it manually. There is the DataAdapterMixin that you can use to automatically authorizer Ember Data requests though. See this blog post for guidance on migrating to 1.0: http://log.simplabs.com/post/131698328145/updating-to-ember-simple-auth-10
Related
It might be plain ignorance from my part but I have only managed to download a file generated by the api using the model method mention in the documentation. Using a component I am quite blind.
The specific question would be: where do I pass the mention arraybuffer:true to the application adapter or to a custom ajax request? Do you have a working example?
Here is a simple try using an ajax service:
import Component from '#ember/component';
import FileSaverMixin from 'ember-cli-file-saver/mixins/file-saver';
import { inject as service } from '#ember/service';
export default Component.extend(FileSaverMixin, {
tagName: 'div',
ajax: service(),
store: service(),
click() {
this.get('ajax').request('/excel', {
options: {
arraybuffer: true
}
}
).then((content) => {
console.log(content);
this.saveFileAs(this.get('filename'), content, this.get('contentType'));
}).catch((error) => {
console.log(error);
})
}
});
And this is my adapter:
import DS from 'ember-data';
import DataAdapterMixin from 'ember-simple-auth/mixins/data-adapter-mixin';
import AdapterArrayBufferMixin from 'ember-cli-file-saver/mixins/adapter-arraybuffer-mixin';
import ENV from 'efac-front/config/environment';
export default DS.JSONAPIAdapter.extend(
DataAdapterMixin,
AdapterArrayBufferMixin,
{
authorizer: 'authorizer:token',
namespace: 'api',
host: ENV.host
}
);
I keep getting an error of SyntaxError: Unexpected token P in JSON at position 0... because it is trying to interpret an array buffer or binary response as json data.
I very much appreciate any light you can throw here
Well it was super easy, but not very well documented. I just needed to add the dataType key to my ajax request like this:
this.get('ajax').request('/excel', {
dataType: 'arraybuffer',
options: {
arraybuffer: true
}
}
).then((content) => {
this.saveFileAs('reporte-asistencia.xlsx', content, 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
}).catch((error) => {
console.log(error);
});
It can also be done with dataType: 'blob'.
I'm trying make authorization in my ember application.
Its working on client side, but ember doesn't attach Bearer token to api request.
My adapter
import DS from 'ember-data';
import DataAdapterMixin from 'ember-simple-auth/mixins/data-adapter-mixin';
export default DS.JSONAPIAdapter.extend(DataAdapterMixin, {
host: 'http://localhost/money-app-api/web/app_dev.php/api',
authorizer: 'authorizer:application'
});
My authorizer:
import Ember from 'ember';
import OAuth2Bearer from 'ember-simple-auth/authorizers/oauth2-bearer';
const { isEmpty } = Ember;
export default OAuth2Bearer.extend({
authorize(data, block) {
const accessToken = data['access_token'];
if (!isEmpty(accessToken)) {
block('Authorization', `Bearer ${accessToken}`);
}
accessToken in authorizer is exists and is correct.
My api is correct too, i tested it by Postman.
I am writing you a full tutorial please just follow that hope it works for you.
//folders and files tree
adapters
--- application.js
authenticators
--- oauth2.js
authorizers
---- oauth2-bearer.js
Adapters/application.js
import DS from 'ember-data';
import DataAdapterMixin from 'ember-simple-auth/mixins/data-adapter-mixin';
export default DS.JSONAPIAdapter.extend(DataAdapterMixin,{
authorizer: 'authorizer:oauth2-bearer',
host: 'http://localhost/money-app-api/web/app_dev.php',
namespace: 'api'
});
authenticators/oauth2.js
import OAuth2PasswordGrant from 'ember-simple-auth/authenticators/oauth2-password-grant';
export default OAuth2PasswordGrant.extend({
serverTokenEndpoint: 'http://localhost/money-app-api/web/app_dev.php/token'
});
authorizers/oauth2-bearer.js
export { default } from 'ember-simple-auth/authorizers/oauth2-bearer';
so now in your route, application.js, you are able to use the following code: this is just for demo purpose you need to modify as you need.
this.get('session').authorize('authorizer:oauth2-bearer', (headerName, headerValue) => {
headers[headerName] = headerValue;
});
I am writing an authentication in route/application.js to clarify more. in this example I am getting Account and User information based on session which has already authenticated .
import Ember from 'ember';
import ApplicationRouteMixin from 'ember-simple-auth/mixins/application-route-mixin';
import config from '../config/environment';
export default Ember.Route.extend(ApplicationRouteMixin, {
model() {
return Ember.RSVP.hash({
account: new Ember.RSVP.Promise((resolve, reject) => {
if (!this.get('session.isAuthenticated')) {
resolve({});
return;
}
let store = this.store,
session = this.get('session');
let headers = {};
this.get('session').authorize('authorizer:oauth2-bearer', (headerName, headerValue) => {
headers[headerName] = headerValue;
});
return Ember.$.ajax(config.apiUrl + '/api/account', {
headers: headers
}).then(data => {
if (data) {
store.pushPayload(data);
resolve(store.peekRecord('user', data.data.id));
} else {
reject({});
session.invalidate();
}
}).fail(() => {
session.invalidate();
});
})
});
},
sessionAuthenticated() {
this.refresh();
this._super();
}
});
I hope, this can solve your problem.
an important tip:
The REST adapter allows your store to communicate with an HTTP server by
transmitting JSON via XHR. Most Ember.js apps that consume a JSON API
should use the REST adapter.
### Headers customization
Some APIs require HTTP headers, e.g. to provide an API key. Arbitrary
headers can be set as key/value pairs on the `RESTAdapter`'s `headers`
object and Ember Data will send them along with each ajax request.
```app/adapters/application.js
import DS from 'ember-data';
export default DS.RESTAdapter.extend({
headers: {
"API_KEY": "secret key",
"ANOTHER_HEADER": "Some header value"
}
});
```
`headers` can also be used as a computed property to support dynamic
headers. In the example below, the `session` object has been
injected into an adapter by Ember's container.
```app/adapters/application.js
import DS from 'ember-data';
export default DS.RESTAdapter.extend({
headers: Ember.computed('session.authToken', function() {
return {
"API_KEY": this.get("session.authToken"),
"ANOTHER_HEADER": "Some header value"
};
})
});
```
Source
My only problem was that i have not authorization in my accept headers in api
allow_headers: ['origin', 'X-Custom-Auth', 'Content-Type', 'Authorization']
That is strange, because when tested by Postman all was working.
Folks,
I've been trying to get ESA to redirect to specific pages after login and logout events without success.
I'm trying to do this by overriding the "sessionAuthenticated" method, but have also tried setting the "routeAfterConfiguration" setting with no luck.
At the moment, login sends me to "/", and logout sends the app to "/undefined".
I'm using simple-auth-token as a JWT authenticator strategy.
The code for my application route looks like this...
import Ember from 'ember';
import ApplicationRouteMixin from 'ember-simple-auth/mixins/application-route-mixin';
export default Ember.Route.extend(ApplicationRouteMixin,{
actions: {
sessionAuthenticated: function() {
console.log('sessionAuthenticated: authentications ok');
window.location.replace('/profile');
},
},
});
My login.js is as follows:
import Ember from 'ember';
const {service} = Ember.inject;
export default Ember.Route.extend({
session: service('session'),
errorMessage: null,
model: function(){
return Ember.Object.create({
identification:'',
password: '',
errorMessage: this.get('errorMessage')
});
},
setupController: function(controller, model) {
controller.set('credentials',model);
},
actions: {
authenticate: function(credentials) {
console.log(credentials);
this.get('session').authenticate('simple-auth-authenticator:jwt', credentials)
.catch((reason) => {
console.log('Login Error');
credentials.set('errorMessage', reason);
});
},
},
});
Does anyone have any idea what I might be doing wrong here?
Cheers,
Andy
OK. Found the problem. These are not actions - they're methods. So I just had to promote the methods out of the actions object and it's all come good.
So the correct routes/application.js looks like this:
import Ember from 'ember';
import ApplicationRouteMixin from 'ember-simple-auth/mixins/application-route-mixin';
export default Ember.Route.extend(ApplicationRouteMixin,{
sessionAuthenticated: function() {
console.log('sessionAuthenticated: authentications ok');
window.location.replace('/profile');
},
});
My authenticators/custom.js:
import Ember from 'ember';
import Base from 'simple-auth/authenticators/base';
export default Base.extend({
restore: function(data) {
},
authenticate: function(email, password, authenticateCallback) {
return new Ember.RSVP.Promise((resolve, reject) => {
Ember.$.ajax({
type: 'POST',
url: apiOrigin + '/api/v1/login',
data: {
email: email,
password: password
},
dataType: 'json'
}).then(function(userData){
console.log('login post success', userData)
authenticateCallback(userData)
Ember.run(function() {
resolve(userData.uuid)
})
})['catch'](function(main){
alert('login error ' + JSON.stringify(main))
console.error('\'caught\' error from login post request', arguments);
})
})
},
invalidate: function(data) {
}
});
And login/controller.js:
import Ember from 'ember';
export default Ember.Controller.extend({
session: Ember.inject.service('session'),
application: Ember.inject.controller(),
actions: {
authenticate() {
let { identification, password } = this.getProperties('identification', 'password');
this.get('session').authenticate('authenticator:custom', identification, password, (userData) => {
//TODO set these properties on ember-simple-auth's session object instead of application controller
this.get('application').setProperties(userData)
this.transitionToRoute('associate-device')
}).catch((reason) => {
this.set('errorMessage', reason.error);
})
}
}
});
My associate-device route is an AuthenticatedRoute.. I don't get an error, but instead, the last thing printed to the console is "Preparing to transition from 'login' to 'associate-device'"
Basically, ember simple auth documents here http://ember-simple-auth.com/api/classes/BaseAuthenticator.html#method_authenticate that "A resolving promise will result in the session becoming authenticated. Any data the promise resolves with will be saved in and accessible via the session service's data.authenticated property (see data). A rejecting promise indicates that authentication failed and will result in the session remaining unauthenticated."
However, my session does not seem to be authenticated after I successfully resolve my promise.
$.ajax has no catch method. This exception was hidden because I was copy-pasting away from the documentation for writing custom authenticators. To expose any exceptions occurring in your custom authenticators authenticate method, you should probably console.log them like so:
// app/controllers/login.js
import Ember from 'ember';
export default Ember.Controller.extend({
session: Ember.inject.service('session'),
actions: {
authenticate() {
let { identification, password } = this.getProperties('identification', 'password');
this.get('session').authenticate('authenticator:oauth2', identification, password).catch((reason) => {
// **CHANGE THE BELOW LINE**
console.error('exception in your authenticators authenticate method', reason)
});
}
}
});
I have a initializer like this:
import Ember from 'ember';
import Session from 'simple-auth/session';
var SessionWithCurrentUser = Session.extend({
store: Ember.inject.service(),
currentUser: function() {
console.log(this.get('store'));
console.log(this.store);
console.log(this.container.lookup('service:store'));
}.property('secure.access_token')
});
export default {
name: 'custom-session',
after: 'ember-data',
initialize(registry) {
registry.register('session:withCurrentUser', SessionWithCurrentUser);
}
};
currentUser gets called on user interaction, long after my app has finished loading. Only the last container lookup gives the store, the other 2 is an object:
{
_lastData: Object,
key: "ember_simple_auth:session"
[..]
}
What's going on? Why can't I inject the store?
It's because store in the current version of simple-auth is being overridden by an instance-initializer with the session storage. The next major version of simple-auth will turn the session storage into a service and we'll be able to do:
import Ember from 'ember';
const { service } = Ember.inject;
export default Ember.Service.extend({
session: service('session'),
store: Ember.inject.service(),
account: Ember.computed('session.content.secure.account_id', function() {
const accountId = this.get('session.content.secure.account_id');
if (!Ember.isEmpty(accountId)) {
return DS.PromiseObject.create({
promise: this.get('store').find('account', accountId)
});
}
})
});
From the dummy app, once https://github.com/simplabs/ember-simple-auth/pull/602 is merged.