Skip to content

Commit

Permalink
Merge pull request #305 from fenichelar/master
Browse files Browse the repository at this point in the history
Switch to GitHub Actions, add refresh request retry options, and add support for ESA v4
  • Loading branch information
fenichelar authored Oct 23, 2021
2 parents e6b23e0 + f262a43 commit 1fdd234
Show file tree
Hide file tree
Showing 18 changed files with 12,250 additions and 27,250 deletions.
4 changes: 1 addition & 3 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ module.exports = {
sourceType: 'module'
},
plugins: [
'ember',
'no-unsafe-regex'
'ember'
],
extends: [
'eslint:recommended',
Expand Down Expand Up @@ -34,7 +33,6 @@ module.exports = {
'no-trailing-spaces': ['error'],
'no-undef': ['error'],
'no-unexpected-multiline': ['error'],
'no-unsafe-regex/no-unsafe-regex': ['error'],
'no-unused-vars': ['error'],
'no-var': ['error'],
'one-var': ['error', 'never'],
Expand Down
73 changes: 73 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
name: ember test
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
continue-on-error: ${{ matrix.experimental }}
strategy:
matrix:
scenario:
- ember-lts-2.16-ember-simple-auth-1.6
- ember-lts-2.16-ember-simple-auth-2.0
- ember-lts-2.18-ember-simple-auth-1.6
- ember-lts-2.18-ember-simple-auth-2.0
- ember-lts-3.4-ember-simple-auth-1.6
- ember-lts-3.4-ember-simple-auth-2.0
- ember-lts-3.4-ember-simple-auth-3.0
- ember-lts-3.4-ember-simple-auth-4.0
- ember-lts-3.8-ember-simple-auth-1.6
- ember-lts-3.8-ember-simple-auth-2.0
- ember-lts-3.8-ember-simple-auth-3.0
- ember-lts-3.8-ember-simple-auth-4.0
- ember-lts-3.12-ember-simple-auth-1.6
- ember-lts-3.12-ember-simple-auth-2.0
- ember-lts-3.12-ember-simple-auth-3.0
- ember-lts-3.12-ember-simple-auth-4.0
- ember-lts-3.16-ember-simple-auth-1.6
- ember-lts-3.16-ember-simple-auth-2.0
- ember-lts-3.16-ember-simple-auth-3.0
- ember-lts-3.16-ember-simple-auth-4.0
- ember-lts-3.20-ember-simple-auth-1.6
- ember-lts-3.20-ember-simple-auth-2.0
- ember-lts-3.20-ember-simple-auth-3.0
- ember-lts-3.20-ember-simple-auth-4.0
- ember-lts-3.24-ember-simple-auth-1.6
- ember-lts-3.24-ember-simple-auth-2.0
- ember-lts-3.24-ember-simple-auth-3.0
- ember-lts-3.24-ember-simple-auth-4.0
- ember-release-ember-simple-auth-1.6
- ember-release-ember-simple-auth-2.0
- ember-release-ember-simple-auth-3.0
- ember-release-ember-simple-auth-4.0
experimental:
- false
include:
- scenario: ember-beta-ember-simple-auth-1.6
experimental: true
- scenario: ember-beta-ember-simple-auth-2.0
experimental: true
- scenario: ember-beta-ember-simple-auth-3.0
experimental: true
- scenario: ember-beta-ember-simple-auth-4.0
experimental: true
- scenario: ember-canary-ember-simple-auth-1.6
experimental: true
- scenario: ember-canary-ember-simple-auth-2.0
experimental: true
- scenario: ember-canary-ember-simple-auth-3.0
experimental: true
- scenario: ember-canary-ember-simple-auth-4.0
experimental: true
steps:
- uses: actions/checkout@v2
- name: Test scenario ${{ matrix.scenario }}
uses: actions/setup-node@v2
with:
node-version: 10.x
- run: npm ci
- run: npm run lint:js
- run: node_modules/.bin/ember try:one ${{ matrix.scenario }} --skip-cleanup
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@ testem.log
.node_modules.ember-try/
bower.json.ember-try
package.json.ember-try
package-lock.json.ember-try
1 change: 1 addition & 0 deletions .npmignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
.gitignore
.watchmanconfig
.travis.yml
.github
bower.json
ember-cli-build.js
testem.js
Expand Down
74 changes: 0 additions & 74 deletions .travis.yml

This file was deleted.

12 changes: 8 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Ember Simple Auth Token

[![travis-image]][travis]
[![github-actions-image]][github-actions]
[![ember-observer-image]][ember-observer]
[![npm-image]][npm]

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

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


[travis-image]: https://travis-ci.org/jpadilla/ember-simple-auth-token.svg?branch=master
[travis]: https://travis-ci.org/jpadilla/ember-simple-auth-token
[github-actions-image]: https://github.com/jpadilla/ember-simple-auth-token/actions/workflows/test.yml/badge.svg
[github-actions]: https://github.com/jpadilla/ember-simple-auth-token/actions/workflows/test.yml
[ember-observer-image]: https://emberobserver.com/badges/ember-simple-auth-token.svg
[ember-observer]: https://emberobserver.com/addons/ember-simple-auth-token
[npm-image]: https://img.shields.io/npm/v/ember-simple-auth-token.svg
Expand Down
57 changes: 40 additions & 17 deletions addon/authenticators/jwt.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ export default TokenAuthenticator.extend({
this.refreshTokenPropertyName = config.refreshTokenPropertyName || 'refresh_token';
this.tokenExpireName = config.tokenExpireName || 'exp';
this.refreshLeeway = config.refreshLeeway || 0;
this.tokenRefreshInvalidateSessionResponseCodes = config.tokenRefreshInvalidateSessionResponseCodes || [401, 403];
this.refreshAccessTokenRetryAttempts = config.refreshAccessTokenRetryAttempts || 0;
this.refreshAccessTokenRetryTimeout = config.refreshAccessTokenRetryTimeout || 1000;
this.tokenRefreshFailInvalidateSession = config.tokenRefreshFailInvalidateSession === true ? true : false;
},

/**
Expand All @@ -64,8 +68,8 @@ export default TokenAuthenticator.extend({
If `refreshAccessTokens` is true, `scheduleAccessTokenRefresh` will be called and an automatic token refresh will be initiated.
@method restore
@param {Object} data The data to restore the session from
@return {Promise} A promise that when it resolves results in the session being authenticated
@param {Object} data Data to restore the session from
@return {Promise} Promise that when it resolves results in the session being authenticated
*/
restore(data) {
const dataObject = EmberObject.create(data);
Expand Down Expand Up @@ -102,14 +106,14 @@ export default TokenAuthenticator.extend({
}
return resolve(data);
} else if (this.refreshAccessTokens) {
return resolve(this.refreshAccessToken(refreshToken));
return resolve(this.refreshAccessToken(refreshToken, 0));
} else {
return reject(new Error('unable to refresh token'));
}
} else {
// 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
if (this.refreshAccessTokens) {
return resolve(this.refreshAccessToken(refreshToken));
return resolve(this.refreshAccessToken(refreshToken, 0));
} else {
return reject(new Error('token is expired'));
}
Expand All @@ -125,9 +129,9 @@ export default TokenAuthenticator.extend({
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.
@method authenticate
@param {Object} credentials The credentials to authenticate the session with
@param {Object} headers Optional headers to send with the authentication request
@return {Promise} A promise that resolves when an auth token is successfully acquired from the server and rejects otherwise
@param {Object} credentials Credentials to authenticate the session with
@param {Object} headers Headers to send with the authentication request
@return {Promise} Promise that resolves when an auth token is successfully acquired from the server and rejects otherwise
*/
authenticate(credentials, headers) {
return this.makeRequest(this.serverTokenEndpoint, credentials, assign({}, this.headers, headers)).then(response => {
Expand All @@ -141,6 +145,8 @@ export default TokenAuthenticator.extend({
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.
@method scheduleAccessTokenRefresh
@param {Integer} expiresAt Timestamp when the token expires
@param {String} refreshToken Refresh token
*/
scheduleAccessTokenRefresh(expiresAt, refreshToken) {
if (this.refreshAccessTokens) {
Expand All @@ -152,7 +158,7 @@ export default TokenAuthenticator.extend({
if (wait > 0) {
cancel(this._refreshTokenTimeout);
delete this._refreshTokenTimeout;
this._refreshTokenTimeout = later(this, this.refreshAccessToken, refreshToken, wait);
this._refreshTokenTimeout = later(this, this.refreshAccessToken, refreshToken, 0, wait);
} else if (expiresAt > now) {
throw new Error('refreshLeeway is too large which is preventing token refresh.');
}
Expand All @@ -170,16 +176,19 @@ export default TokenAuthenticator.extend({
The session will be updated via the trigger `sessionDataUpdated`.
@method refreshAccessToken
@param {String} refreshToken Refresh token
@param {Integer} attempts Number of attempts that have been made so far
@return {Promise} Promise that resolves when an auth token is successfully acquired from the server and rejects otherwise
*/
refreshAccessToken(token) {
const data = this.makeRefreshData(token);
refreshAccessToken(refreshToken, attempts) {
const data = this.makeRefreshData(refreshToken);

return this.makeRequest(this.serverTokenRefreshEndpoint, data, this.headers).then(response => {
const sessionData = this.handleAuthResponse(response.json);
this.trigger('sessionDataUpdated', sessionData);
return sessionData;
}).catch(error => {
this.handleTokenRefreshFail(error.status);
this.handleTokenRefreshFail(error.status, refreshToken, attempts);
return Promise.reject(error);
});
},
Expand All @@ -189,7 +198,8 @@ export default TokenAuthenticator.extend({
Example: If `refreshTokenPropertyName` is "data.user.refreshToken", `makeRefreshData` will return {data: {user: {refreshToken: "token goes here"}}}
@method makeRefreshData
@return {object} An object with the nested property name.
@param {String} refreshToken Refresh token
@return {object} Object with the nested property name.
*/
makeRefreshData(refreshToken) {
const data = {};
Expand All @@ -211,7 +221,8 @@ export default TokenAuthenticator.extend({
Returns the decoded token with accessible returned values.
@method getTokenData
@return {object} An object with properties for the session.
@param {String} token Token
@return {object} Object with properties for the session.
*/
getTokenData(token) {
const payload = token.split('.')[1];
Expand All @@ -229,8 +240,7 @@ export default TokenAuthenticator.extend({
Cancels any outstanding automatic token refreshes and returns a resolving promise.
@method invalidate
@param {Object} data The data of the session to be invalidated
@return {Promise} A resolving promise
@return {Promise} Resolving promise
*/
invalidate() {
cancel(this._refreshTokenTimeout);
Expand All @@ -254,6 +264,7 @@ export default TokenAuthenticator.extend({
Handles authentication response from server, and returns session data
@method handleAuthResponse
@param {Object} response Response body
*/
handleAuthResponse(response) {
const token = get(response, this.tokenPropertyName);
Expand Down Expand Up @@ -289,9 +300,20 @@ export default TokenAuthenticator.extend({
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.
@method handleTokenRefreshFail
@param {Integer} refreshStatusCode Status code received when attempting to refresh token
@param {String} refreshToken Refresh token
@param {Integer} attempts Number of attempts that have been made so far
*/
handleTokenRefreshFail(refreshStatus) {
if (refreshStatus === 401 || refreshStatus === 403) {
handleTokenRefreshFail(refreshStatusCode, refreshToken, attempts) {
if (this.tokenRefreshInvalidateSessionResponseCodes.includes(refreshStatusCode)) {
return this.invalidate().then(() => {
this.trigger('sessionDataInvalidated');
});
} else if (attempts++ < this.refreshAccessTokenRetryAttempts) {
cancel(this._refreshTokenTimeout);
delete this._refreshTokenTimeout;
this._refreshTokenTimeout = later(this, this.refreshAccessToken, refreshToken, attempts, this.refreshAccessTokenRetryTimeout);
} else if (this.tokenRefreshFailInvalidateSession) {
return this.invalidate().then(() => {
this.trigger('sessionDataInvalidated');
});
Expand All @@ -302,6 +324,7 @@ export default TokenAuthenticator.extend({
Schedules session invalidation at the time token expires.
@method scheduleAccessTokenExpiration
@param {Integer} expiresAt Timestamp when the token expires
*/
scheduleAccessTokenExpiration(expiresAt) {
const now = this.getCurrentTime();
Expand Down
Loading

0 comments on commit 1fdd234

Please sign in to comment.