-
Notifications
You must be signed in to change notification settings - Fork 140
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Invalidate session after token expiration #195
Conversation
Been using this in production for a while now without any issues. Would love to get this merged..... @jpadilla |
@jpadilla Thoughts? |
@jpadilla Any word on this? |
@fenichelar I obviously need help maintaining this addon, in case you're interested. I started updating to latest ember-cli (#222) to hopefully see what's up with CI. Any help with that and updating this PR is more than welcome. |
@jpadilla We use this library on a number of projects, it has been extremely helpful, thank you!! I just upgraded one of our applications to the latest version of Ember. I am still in the progress of getting everything working. I am testing #219 on that application. ETA - early this week. We have been using this pull request (#195) in production for a long time, it has been working well. After I get the application all squared away, I'll be able to report back on #195, #219, and #222 and make any modifications as needed. I can then assist with any other pull requests or issues. |
@fenichelar that would be amazing, thank you! ✨ |
I have got one of my applications updated to Ember CLI v3.1.2. I am using the changes defined on this PR and the changes defined on PR #219 by overriding So far everything looks good. Beta users are testing now, I will report back any issues we find. This is my import EmberObject from '@ember/object';
import { get } from '@ember/object';
import { Promise, resolve } from 'rsvp';
import { isEmpty } from '@ember/utils';
import { assign } from '@ember/polyfills';
import { cancel, later } from '@ember/runloop';
import JWT from 'ember-simple-auth-token/authenticators/jwt';
export default JWT.extend({
getAuthenticateData(credentials) {
var authentication = {
[this.identificationField]: credentials.identification,
[this.passwordField]: credentials.password,
otp: credentials.otp
};
return authentication;
},
handleAuthResponse(response) {
const token = get(response, this.tokenPropertyName);
if (isEmpty(token)) {
throw new Error('Token is empty. Please check your backend response.');
}
const tokenData = this.getTokenData(token);
const expiresAt = get(tokenData, this.tokenExpireName);
const tokenExpireData = {};
tokenExpireData[this.tokenExpireName] = expiresAt;
this.scheduleAccessTokenExpiration(expiresAt);
if (this.refreshAccessTokens) {
const refreshToken = get(response, this.refreshTokenPropertyName);
if (isEmpty(refreshToken)) {
throw new Error('Refresh token is empty. Please check your backend response.');
}
this.scheduleAccessTokenRefresh(expiresAt, refreshToken);
}
return assign(this.getResponseData(response), tokenExpireData);
},
restore(data) {
const dataObject = EmberObject.create(data);
return new Promise((resolve, reject) => {
const now = this.getCurrentTime();
const token = dataObject.get(this.tokenPropertyName);
const refreshToken = dataObject.get(this.refreshTokenPropertyName);
let expiresAt = dataObject.get(this.tokenExpireName);
if (isEmpty(token)) {
return reject(new Error('empty token'));
}
if (isEmpty(expiresAt)) {
const tokenData = this.getTokenData(token);
expiresAt = tokenData[this.tokenExpireName];
if (isEmpty(expiresAt)) {
return resolve(data);
}
}
if (expiresAt > now) {
const wait = (expiresAt - now - this.refreshLeeway) * 1000;
this.scheduleAccessTokenExpiration(expiresAt);
if (wait > 0) {
if (this.refreshAccessTokens) {
this.scheduleAccessTokenRefresh(dataObject.get(this.tokenExpireName), refreshToken);
}
resolve(data);
} else if (this.refreshAccessTokens) {
resolve(this.refreshAccessToken(refreshToken));
} else {
reject(new Error('unable to refresh token'));
}
} else {
if (this.refreshAccessTokens) {
resolve(this.refreshAccessToken(refreshToken));
} else {
reject(new Error('token is expired'));
}
}
});
},
invalidate() {
cancel(this._refreshTokenTimeout);
delete this._refreshTokenTimeout;
cancel(this._tokenExpirationTimeout);
delete this._tokenExpirationTimeout;
return new resolve();
},
scheduleAccessTokenExpiration(expiresAt) {
const now = this.getCurrentTime();
const wait = Math.max((expiresAt - now) * 1000, 0);
if (!isEmpty(expiresAt)) {
cancel(this._tokenExpirationTimeout);
delete this._tokenExpirationTimeout;
this._tokenExpirationTimeout = later(this, this.handleAccessTokenExpiration, wait);
}
},
handleAccessTokenExpiration() {
return this.invalidate().then(() => {
this.trigger('sessionDataInvalidated');
});
}
}); This is my import { get } from '@ember/object';
import { inject } from '@ember/service';
import { isEmpty } from '@ember/utils';
import DS from 'ember-data';
import ENV from '../config/environment';
import DataAdapterMixin from 'ember-simple-auth/mixins/data-adapter-mixin';
export default DS.RESTAdapter.extend(DataAdapterMixin, {
session: inject('session'),
host: ENV.api,
coalesceFindRequests: true,
authorize(xhr) {
const data = get(this, 'session.data.authenticated');
const token = get(data, 'token');
if (this.get('session.isAuthenticated') && !isEmpty(token)) {
xhr.setRequestHeader('Authorization', `Bearer ${token}`);
}
},
updateRecord(store, type, snapshot) {
let data = {};
let serializer = store.serializerFor(type.modelName);
serializer.serializeIntoHash(data, type, snapshot);
let id = snapshot.id;
let url = this.buildURL(type.modelName, id, snapshot, 'updateRecord');
return this.ajax(url, 'PATCH', { data: data });
}
}); |
@jpadilla All seems well. I will fork, make the following changes, and submit a PR:
Please let me know if you don't like the idea of a single, new PR. Expect the PR sometime this week. |
Getting all the unit tests and demo working is taking sometime. I was having some issues with the app not getting cleaned up between tests causing configuration changes to leak to other tests. I ended up changing a lot of the configuration stuff to get it working, it is much simpler now. Just need to get the demo working and do some actual testing. |
@jembezmamy These changes are awesome! Your commits have been pulled in through a different PR: #226 |
Here's mine attempt to resolve #174. I've added
handleAccessTokenExpiration
method that invalidates session as soon as token expires. I had to change some existing tests since they were just checking ifEmber.run.later
was called and now I use it also for scheduling expiration.