Skip to content

Commit 1fdd234

Browse files
authored
Merge pull request #305 from fenichelar/master
Switch to GitHub Actions, add refresh request retry options, and add support for ESA v4
2 parents e6b23e0 + f262a43 commit 1fdd234

File tree

18 files changed

+12250
-27250
lines changed

18 files changed

+12250
-27250
lines changed

.eslintrc.js

+1-3
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@ module.exports = {
55
sourceType: 'module'
66
},
77
plugins: [
8-
'ember',
9-
'no-unsafe-regex'
8+
'ember'
109
],
1110
extends: [
1211
'eslint:recommended',
@@ -34,7 +33,6 @@ module.exports = {
3433
'no-trailing-spaces': ['error'],
3534
'no-undef': ['error'],
3635
'no-unexpected-multiline': ['error'],
37-
'no-unsafe-regex/no-unsafe-regex': ['error'],
3836
'no-unused-vars': ['error'],
3937
'no-var': ['error'],
4038
'one-var': ['error', 'never'],

.github/workflows/test.yml

+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
name: ember test
2+
on:
3+
push:
4+
branches: [ master ]
5+
pull_request:
6+
branches: [ master ]
7+
jobs:
8+
build:
9+
runs-on: ubuntu-latest
10+
continue-on-error: ${{ matrix.experimental }}
11+
strategy:
12+
matrix:
13+
scenario:
14+
- ember-lts-2.16-ember-simple-auth-1.6
15+
- ember-lts-2.16-ember-simple-auth-2.0
16+
- ember-lts-2.18-ember-simple-auth-1.6
17+
- ember-lts-2.18-ember-simple-auth-2.0
18+
- ember-lts-3.4-ember-simple-auth-1.6
19+
- ember-lts-3.4-ember-simple-auth-2.0
20+
- ember-lts-3.4-ember-simple-auth-3.0
21+
- ember-lts-3.4-ember-simple-auth-4.0
22+
- ember-lts-3.8-ember-simple-auth-1.6
23+
- ember-lts-3.8-ember-simple-auth-2.0
24+
- ember-lts-3.8-ember-simple-auth-3.0
25+
- ember-lts-3.8-ember-simple-auth-4.0
26+
- ember-lts-3.12-ember-simple-auth-1.6
27+
- ember-lts-3.12-ember-simple-auth-2.0
28+
- ember-lts-3.12-ember-simple-auth-3.0
29+
- ember-lts-3.12-ember-simple-auth-4.0
30+
- ember-lts-3.16-ember-simple-auth-1.6
31+
- ember-lts-3.16-ember-simple-auth-2.0
32+
- ember-lts-3.16-ember-simple-auth-3.0
33+
- ember-lts-3.16-ember-simple-auth-4.0
34+
- ember-lts-3.20-ember-simple-auth-1.6
35+
- ember-lts-3.20-ember-simple-auth-2.0
36+
- ember-lts-3.20-ember-simple-auth-3.0
37+
- ember-lts-3.20-ember-simple-auth-4.0
38+
- ember-lts-3.24-ember-simple-auth-1.6
39+
- ember-lts-3.24-ember-simple-auth-2.0
40+
- ember-lts-3.24-ember-simple-auth-3.0
41+
- ember-lts-3.24-ember-simple-auth-4.0
42+
- ember-release-ember-simple-auth-1.6
43+
- ember-release-ember-simple-auth-2.0
44+
- ember-release-ember-simple-auth-3.0
45+
- ember-release-ember-simple-auth-4.0
46+
experimental:
47+
- false
48+
include:
49+
- scenario: ember-beta-ember-simple-auth-1.6
50+
experimental: true
51+
- scenario: ember-beta-ember-simple-auth-2.0
52+
experimental: true
53+
- scenario: ember-beta-ember-simple-auth-3.0
54+
experimental: true
55+
- scenario: ember-beta-ember-simple-auth-4.0
56+
experimental: true
57+
- scenario: ember-canary-ember-simple-auth-1.6
58+
experimental: true
59+
- scenario: ember-canary-ember-simple-auth-2.0
60+
experimental: true
61+
- scenario: ember-canary-ember-simple-auth-3.0
62+
experimental: true
63+
- scenario: ember-canary-ember-simple-auth-4.0
64+
experimental: true
65+
steps:
66+
- uses: actions/checkout@v2
67+
- name: Test scenario ${{ matrix.scenario }}
68+
uses: actions/setup-node@v2
69+
with:
70+
node-version: 10.x
71+
- run: npm ci
72+
- run: npm run lint:js
73+
- run: node_modules/.bin/ember try:one ${{ matrix.scenario }} --skip-cleanup

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,4 @@ testem.log
2121
.node_modules.ember-try/
2222
bower.json.ember-try
2323
package.json.ember-try
24+
package-lock.json.ember-try

.npmignore

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
.gitignore
1313
.watchmanconfig
1414
.travis.yml
15+
.github
1516
bower.json
1617
ember-cli-build.js
1718
testem.js

.travis.yml

-74
This file was deleted.

README.md

+8-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Ember Simple Auth Token
22

3-
[![travis-image]][travis]
3+
[![github-actions-image]][github-actions]
44
[![ember-observer-image]][ember-observer]
55
[![npm-image]][npm]
66

@@ -185,7 +185,11 @@ ENV['ember-simple-auth-token'] = {
185185
serverTokenRefreshEndpoint: '/api/token-refresh/', // Server endpoint to send refresh request
186186
refreshTokenPropertyName: 'refresh_token', // Key in server response that contains the refresh token
187187
tokenExpireName: 'exp', // Field containing token expiration
188-
refreshLeeway: 0 // Amount of time to send refresh request before token expiration
188+
refreshLeeway: 0, // Amount of time in seconds to send refresh request before token expiration
189+
tokenRefreshInvalidateSessionResponseCodes: [401, 403], // Array of response codes that cause an immediate session invalidation if received when attempting to refresh the token
190+
refreshAccessTokenRetryAttempts: 0, // Number of token retry attempts to make
191+
refreshAccessTokenRetryTimeout: 1000, // Amount of time in milliseconds to wait between token refresh retry attempts
192+
tokenRefreshFailInvalidateSession: false // Enables session invalidation if all token refresh retry requests fail
189193
};
190194
```
191195

@@ -219,8 +223,8 @@ ENV['ember-simple-auth-token'] = {
219223
- `config.timeFactor` has been removed since version 2.1.0
220224

221225

222-
[travis-image]: https://travis-ci.org/jpadilla/ember-simple-auth-token.svg?branch=master
223-
[travis]: https://travis-ci.org/jpadilla/ember-simple-auth-token
226+
[github-actions-image]: https://github.com/jpadilla/ember-simple-auth-token/actions/workflows/test.yml/badge.svg
227+
[github-actions]: https://github.com/jpadilla/ember-simple-auth-token/actions/workflows/test.yml
224228
[ember-observer-image]: https://emberobserver.com/badges/ember-simple-auth-token.svg
225229
[ember-observer]: https://emberobserver.com/addons/ember-simple-auth-token
226230
[npm-image]: https://img.shields.io/npm/v/ember-simple-auth-token.svg

addon/authenticators/jwt.js

+40-17
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ export default TokenAuthenticator.extend({
5151
this.refreshTokenPropertyName = config.refreshTokenPropertyName || 'refresh_token';
5252
this.tokenExpireName = config.tokenExpireName || 'exp';
5353
this.refreshLeeway = config.refreshLeeway || 0;
54+
this.tokenRefreshInvalidateSessionResponseCodes = config.tokenRefreshInvalidateSessionResponseCodes || [401, 403];
55+
this.refreshAccessTokenRetryAttempts = config.refreshAccessTokenRetryAttempts || 0;
56+
this.refreshAccessTokenRetryTimeout = config.refreshAccessTokenRetryTimeout || 1000;
57+
this.tokenRefreshFailInvalidateSession = config.tokenRefreshFailInvalidateSession === true ? true : false;
5458
},
5559

5660
/**
@@ -64,8 +68,8 @@ export default TokenAuthenticator.extend({
6468
If `refreshAccessTokens` is true, `scheduleAccessTokenRefresh` will be called and an automatic token refresh will be initiated.
6569
6670
@method restore
67-
@param {Object} data The data to restore the session from
68-
@return {Promise} A promise that when it resolves results in the session being authenticated
71+
@param {Object} data Data to restore the session from
72+
@return {Promise} Promise that when it resolves results in the session being authenticated
6973
*/
7074
restore(data) {
7175
const dataObject = EmberObject.create(data);
@@ -102,14 +106,14 @@ export default TokenAuthenticator.extend({
102106
}
103107
return resolve(data);
104108
} else if (this.refreshAccessTokens) {
105-
return resolve(this.refreshAccessToken(refreshToken));
109+
return resolve(this.refreshAccessToken(refreshToken, 0));
106110
} else {
107111
return reject(new Error('unable to refresh token'));
108112
}
109113
} else {
110114
// The refresh token might not be expired, we can't test this on the client so attempt to refresh the token. If the server rejects the token the user session will be invalidated
111115
if (this.refreshAccessTokens) {
112-
return resolve(this.refreshAccessToken(refreshToken));
116+
return resolve(this.refreshAccessToken(refreshToken, 0));
113117
} else {
114118
return reject(new Error('token is expired'));
115119
}
@@ -125,9 +129,9 @@ export default TokenAuthenticator.extend({
125129
An automatic token refresh will be scheduled with the new expiration date from the returned refresh token. That expiration will be merged with the response and the promise resolved.
126130
127131
@method authenticate
128-
@param {Object} credentials The credentials to authenticate the session with
129-
@param {Object} headers Optional headers to send with the authentication request
130-
@return {Promise} A promise that resolves when an auth token is successfully acquired from the server and rejects otherwise
132+
@param {Object} credentials Credentials to authenticate the session with
133+
@param {Object} headers Headers to send with the authentication request
134+
@return {Promise} Promise that resolves when an auth token is successfully acquired from the server and rejects otherwise
131135
*/
132136
authenticate(credentials, headers) {
133137
return this.makeRequest(this.serverTokenEndpoint, credentials, assign({}, this.headers, headers)).then(response => {
@@ -141,6 +145,8 @@ export default TokenAuthenticator.extend({
141145
If both `token` and `expiresAt` are non-empty, and `expiresAt` minus the optional refres leeway is greater than the calculated `now`, the token refresh will be scheduled through later.
142146
143147
@method scheduleAccessTokenRefresh
148+
@param {Integer} expiresAt Timestamp when the token expires
149+
@param {String} refreshToken Refresh token
144150
*/
145151
scheduleAccessTokenRefresh(expiresAt, refreshToken) {
146152
if (this.refreshAccessTokens) {
@@ -152,7 +158,7 @@ export default TokenAuthenticator.extend({
152158
if (wait > 0) {
153159
cancel(this._refreshTokenTimeout);
154160
delete this._refreshTokenTimeout;
155-
this._refreshTokenTimeout = later(this, this.refreshAccessToken, refreshToken, wait);
161+
this._refreshTokenTimeout = later(this, this.refreshAccessToken, refreshToken, 0, wait);
156162
} else if (expiresAt > now) {
157163
throw new Error('refreshLeeway is too large which is preventing token refresh.');
158164
}
@@ -170,16 +176,19 @@ export default TokenAuthenticator.extend({
170176
The session will be updated via the trigger `sessionDataUpdated`.
171177
172178
@method refreshAccessToken
179+
@param {String} refreshToken Refresh token
180+
@param {Integer} attempts Number of attempts that have been made so far
181+
@return {Promise} Promise that resolves when an auth token is successfully acquired from the server and rejects otherwise
173182
*/
174-
refreshAccessToken(token) {
175-
const data = this.makeRefreshData(token);
183+
refreshAccessToken(refreshToken, attempts) {
184+
const data = this.makeRefreshData(refreshToken);
176185

177186
return this.makeRequest(this.serverTokenRefreshEndpoint, data, this.headers).then(response => {
178187
const sessionData = this.handleAuthResponse(response.json);
179188
this.trigger('sessionDataUpdated', sessionData);
180189
return sessionData;
181190
}).catch(error => {
182-
this.handleTokenRefreshFail(error.status);
191+
this.handleTokenRefreshFail(error.status, refreshToken, attempts);
183192
return Promise.reject(error);
184193
});
185194
},
@@ -189,7 +198,8 @@ export default TokenAuthenticator.extend({
189198
Example: If `refreshTokenPropertyName` is "data.user.refreshToken", `makeRefreshData` will return {data: {user: {refreshToken: "token goes here"}}}
190199
191200
@method makeRefreshData
192-
@return {object} An object with the nested property name.
201+
@param {String} refreshToken Refresh token
202+
@return {object} Object with the nested property name.
193203
*/
194204
makeRefreshData(refreshToken) {
195205
const data = {};
@@ -211,7 +221,8 @@ export default TokenAuthenticator.extend({
211221
Returns the decoded token with accessible returned values.
212222
213223
@method getTokenData
214-
@return {object} An object with properties for the session.
224+
@param {String} token Token
225+
@return {object} Object with properties for the session.
215226
*/
216227
getTokenData(token) {
217228
const payload = token.split('.')[1];
@@ -229,8 +240,7 @@ export default TokenAuthenticator.extend({
229240
Cancels any outstanding automatic token refreshes and returns a resolving promise.
230241
231242
@method invalidate
232-
@param {Object} data The data of the session to be invalidated
233-
@return {Promise} A resolving promise
243+
@return {Promise} Resolving promise
234244
*/
235245
invalidate() {
236246
cancel(this._refreshTokenTimeout);
@@ -254,6 +264,7 @@ export default TokenAuthenticator.extend({
254264
Handles authentication response from server, and returns session data
255265
256266
@method handleAuthResponse
267+
@param {Object} response Response body
257268
*/
258269
handleAuthResponse(response) {
259270
const token = get(response, this.tokenPropertyName);
@@ -289,9 +300,20 @@ export default TokenAuthenticator.extend({
289300
Handles token refresh fail status. If the server response to a token refresh has a status of 401 or 403 then the token in the session will be invalidated and the sessionInvalidated provided by ember-simple-auth will be triggered.
290301
291302
@method handleTokenRefreshFail
303+
@param {Integer} refreshStatusCode Status code received when attempting to refresh token
304+
@param {String} refreshToken Refresh token
305+
@param {Integer} attempts Number of attempts that have been made so far
292306
*/
293-
handleTokenRefreshFail(refreshStatus) {
294-
if (refreshStatus === 401 || refreshStatus === 403) {
307+
handleTokenRefreshFail(refreshStatusCode, refreshToken, attempts) {
308+
if (this.tokenRefreshInvalidateSessionResponseCodes.includes(refreshStatusCode)) {
309+
return this.invalidate().then(() => {
310+
this.trigger('sessionDataInvalidated');
311+
});
312+
} else if (attempts++ < this.refreshAccessTokenRetryAttempts) {
313+
cancel(this._refreshTokenTimeout);
314+
delete this._refreshTokenTimeout;
315+
this._refreshTokenTimeout = later(this, this.refreshAccessToken, refreshToken, attempts, this.refreshAccessTokenRetryTimeout);
316+
} else if (this.tokenRefreshFailInvalidateSession) {
295317
return this.invalidate().then(() => {
296318
this.trigger('sessionDataInvalidated');
297319
});
@@ -302,6 +324,7 @@ export default TokenAuthenticator.extend({
302324
Schedules session invalidation at the time token expires.
303325
304326
@method scheduleAccessTokenExpiration
327+
@param {Integer} expiresAt Timestamp when the token expires
305328
*/
306329
scheduleAccessTokenExpiration(expiresAt) {
307330
const now = this.getCurrentTime();

0 commit comments

Comments
 (0)