I am struggling to debug failing tests on an Ember 3.0 app. I have a login form using ember-simple-auth. The authenticate action looks like this:
let { identification, password } = this.getProperties('identification', 'password');
this.get('session').authenticate('authenticator:devise', identification, password)
.catch((reason) => {
this.set('loginError', reason.errors || reason);
});
My test simply fills in the form and clicks a button to trigger this action, then checks a user is logged in:
invalidateSession();
await visit('/');
fillIn('#identification', 'test#email.com');
fillIn('#password', 'secret');
await click('.login-btn');
let sesh = currentSession();
let isAuthed = get(sesh, 'isAuthenticated');
assert.equal(
isAuthed,
true,
'after a user submits good creds to login form, they are logged in'
);
Using a Mirage backend, this works every time. However, using a Rails API which returns the same response, this test fails about half the time. It seems to be random whether it will pass or fail.
I'm not sure where the problem is here. The Rails app is running locally on port 3000 so it is not a network issue. I wonder if the test is timing out because the API takes longer to respond than Mirage - although this does seem unlikely as the tests run in under a second. Has anyone encountered a problem like this?
Thanks
My hunch is that you may be evaluating isAuth before ember-simple-auth has completed authenticating with the backend. For my tests that are similar to yours, I have await settled(); after await click();.
If that doesn't do the trick then you could try using waitFor or waitUntil from the ember-test-helpers to make sure that authentication has finished.
Related
I have built a react shopping Cart application which has a login page Signup Page Products Add to Cart and other features like Checkout through Api . Now the other person from my team has build a backend api on django. I want to call that api from my react code now. How should I do it? Do I need to learn something specific? I have searched in internet and all of the sources we to make serve react and django from same project. I have a React project with me and a separate api developed by someone else. Please guide me through this.
You want to make an HTTP request to the API your colleague made. This video explains three methods you can use really well: https://www.youtube.com/watch?v=4K33w-0-p2c
XMLHttpRequest
fetch() API
Axios. Here the video from the same channel.
You should use Axios because it gives you the simplest way to make these requests:
// Make a request for a user with a given ID
axios.get('/user?ID=12345')
.then(function (response) {
// handle success
console.log(response);
})
.catch(function (error) {
// handle error
console.log(error);
})
.finally(function () {
// always executed
});
The page to login to our application is a jsp hosted on another machine. I have managed to get requests firing to this machine by modifying authenticated-route-mixin by allowing window.location.replace to be called if the route start with http.
beforeModel(transition) {
if (!this.get('session.isAuthenticated')) {
Ember.assert('The route configured as Configuration.authenticationRoute cannot implement the AuthenticatedRouteMixin mixin as that leads to an infinite transitioning loop!', this.get('routeName') !== Configuration.authenticationRoute);
transition.abort();
this.set('session.attemptedTransition', transition);
debugger;
if (Configuration.authenticationRoute.startsWith('http')) {
window.location.replace(Configuration.authenticationRoute);
} else {
this.transitionTo(Configuration.authenticationRoute);
}
} else {
return this._super(...arguments);
}
}
This is working but when I am redirected back to my application, ember-simple-auth thinks I am no longer logged in and redirects be back to the remote machine, which then sends me back to the application in an infinite loop.
Obviously I need to set something to let ember-simple-auth know that it it is actually logged in. Why is it not doing this automatically? What am I doing wrong?
I am pretty new to oAuth so I could be missing some basic setting here.
Here is the URL.
ENV['ember-simple-auth'] = {
authenticationRoute: 'https://our-server.com/opensso/oauth2/authorize?client_id=test-client-1&response_type=code&redirect_uri=http%3A%2F%2Flocalhost%3A4200%2Fsecure'
};
Instead of modifying the AuthenticatedRouteMixin, I'd recommend handling your app-specific login in an Authenticator-- the key configuration primitive that Ember Simple Auth provides as part of its public API.
To the best of my understanding, on first loading the app, and checking to see if a user is authenticated, Ember Simple Auth will use the restore method, defined as part of the Authenticator API.
You can return a promise from restore that resolves or rejects to indicate whether the user is authenticated. How you check this is an implementation detail of your auth system.
I don't know how you're storing credential(s) on the client (would be great if you could provide more detail), but here's an example flow, using cookies for authentication:
Ember boots, ESA attempts to restore the session.
restore makes a simple AJAX request to a secured, "dummy" resource on your Java server-- and checks if it gets back a 200 or a 401.
We get a 401 back. The user isn't authenticated, so reject in the Promise returned from restore.
Let ESA redirect the user to your authentication route. Ideally, don't override the AuthenticatedRouteMixin-- instead, use the beforeModel hook in the authentication route to send users to your JSP login page.
The user correctly authenticates against the JSP form.
In its response, your Java server sets some kind of encrypted, signed session cookie (this is how it generally works with Rails) as a credential. In addition, it sends a redirect back to your Ember app.
Ember boots again, ESA calls restore again.
restore pings your Java server again, gets a 200 back (thanks to the cookie), and thus resolves its Promise.
ESA learns that the user's authenticated, and redirects to the 'route after authentication'.
Keep in mind that, at its core, ESA can only indicate to the client whether the backend considers it 'authenticated' or not. ESA can never be used to deny access to a resource-- only to show something different on the client, based on the last thing it heard from the backend.
Let me know if any of that was helpful.
I'm using ember-cli-simple-auth and the cookie-store add-on, I've set up my application and login routes and my authenticated routes, pretty much all that is working, but if I reload the page, or if livereload does it for me, I get kicked back out to the login page, have to enter credentials, and then get brought back to the page I was on. I thought this wasn't supposed to happen given what the docs say:
Ember Simple Auth persists the session state so it survives page reloads.
https://github.com/simplabs/ember-simple-auth
What should I look for to fix this?
Edit: adding cookie that's set after authenticating
cookie name: ember_simple_auth:session
%7B%22authenticator%22%3A%22authenticator%3Acustom%22%2C%22auth_token%22%3A%226hxR0eEL0EbHjPpfWFmdiWfKqHXLfXdYqG9wdZKgnlh3BacNvd41OHl6aOLFAv5C%22%2C%22account_id%22%3A%22A461225%22%2C%22full_name%22%3A%22Jess%20Hines%22%7D
EDIT
This issue is similar, so looks like what I really need is help writing the restore() function.
Examples provided by #marcoow on this github issue.
Here is my restore function:
restore: function(data) {
return new Ember.RSVP.Promise(function(resolve, reject) {
if (!Ember.isEmpty(data)) {
resolve(data);
} else {
reject();
}
});
}
I'm using Ember Simple Auth Devise v 0.6.4 in an Ember-cli app.
I can log in fine but when I refresh the page the session is lost. (Tested in Firefox and Chrome.)
Right after logging in, inspecting the localStorage shows the session and after refreshing localStorage is empty.
Here's what's in the local storage when I sign in:
The problem is that you have neither user_token nor user_email in the session which are required for the session to be authenticated. So as soon as you reload the page the authenticator's restore method rejects the session. Also without user_token and user_email the authorizer is not going to actually authorize any requests.
You'll need to change your server side devise setup as described here.
I have run into the same issue with simple-auth-devise.
The problem was that inconfig/environment.js the identificationAttributeName was overridden.
ENV['simple-auth-devise'] = {
identificationAttributeName: 'email'
};
By doing so, it no longer matched the data returned by Users::SessionsController on successful authentication, taken from the ember-simple-auth-devise Readme:
data = {
token: user.authentication_token,
user_email: user.email
}
The attribute names must match, so the solution is to use the identificationAttributeName in the JSON returned by the controller:
data = {
token: user.authentication_token,
email: user.email
}
Like marcoow pointed out, it is all in the implementation of the Devise authorizer restore() method.
I'm experiencing the same issue, e.g. my session is getting nuked on refresh.
This is undesired behavior, and for me at least doesn't appear to have anything to do with server side devise setup.
No requests are being sent to the server, it's just a matter of keeping the session alive by using the cookies which should be checked first.
I had this issue as well. It turns out that the restore method in the authenticator did not take into account the resource name.
In particular, changing the line indicated here: https://github.com/simplabs/ember-simple-auth/blob/master/packages/ember-simple-auth-devise/lib/simple-auth-devise/authenticators/devise.js#L95
as follows:
if (!Ember.isEmpty(propertiesObject.get(_this.resourceName)[_this.tokenAttributeName]) && !Ember.isEmpty(propertiesObject.get(_this.resourceName)[_this.identificationAttributeName])) {
solved the problem.
Note that my local storage looked like:
{"secure":{"authenticator":"simple-auth-authenticator:devise","user":{"id":1,"email":"test#gmail.com","created_at":"2015-07-20T22:30:47.966Z","updated_at":"2015-07-23T17:45:41.874Z","authentication_token":"7Uv6LysQ2h3x-P4WUMmU","token":"7Uv6LysQ2h3x-P4WUMmU"}}}
As a result, this required the additional changes in the config/environment.js
ENV['simple-auth-devise'] = {
identificationAttributeName: 'email',
resourceName: 'user',
tokenAttributeName: 'authentication_token',
crossOriginWhitelist: ['*']
};
Changing bower_components/ember-simple-auth/simple-auth-devise.amd.js is what allowed me to see that this indeed was my problem.
Even though Ember Auth doesn't automatically inject custom objects returned upon Sign In, it allows you to grab them by inspecting the response object:
actions:
signIn: ->
#auth.signIn(
data:
user:
email: #get 'email'
password: #get 'password'
).then( (response) ->
console.log response['user_context']['roles']
)
The Sign In action is triggered by my Log In page's submit button, and, in turn, calls the authentication API service.
However, when rememberable is used and the page is refreshed, Ember Auth calls the API service with the remember token and retrieves the same response object as the Sign In action.
How can I grab the response object in this scenario? I need to be able to have access to objects such as user permissions/roles, so that I can build menu bar items based on them. This is just one example.
UPDATE:
Pretty much right after asking this question I realized EmberAuth wasn't gonna cut it for my needs. So I rolled my own Authentication solution. Wasn't as hard as I thought, and much lighter and easier for me to use/customize than the alternative - SimpleAuth.