Skip to content

Commit

Permalink
pre-publish v3.0.0-beta.8
Browse files Browse the repository at this point in the history
  • Loading branch information
maxmantz committed Jul 12, 2017
1 parent 56179fa commit a2202da
Show file tree
Hide file tree
Showing 10 changed files with 197 additions and 76 deletions.
7 changes: 2 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# redux-oidc
# redux-oidc
[![Build Status](https://travis-ci.org/maxmantz/redux-oidc.svg?branch=master)](https://travis-ci.org/maxmantz/redux-oidc)

A package for managing OpenID-Connect authentication in ReactJS / Redux apps.
A package for managing OpenID-Connect authentication in ReactJS / Redux apps. It wraps the popular oidc-client library to redux actions and reducers.

### Installation
`npm install --save redux-oidc`
Expand All @@ -12,9 +12,6 @@ In order to install this run:

`npm install --save oidc-client`

There is also a dependency on [co](https://www.npmjs.com/package/co).

`npm install --save co`

In addition there is a peer dependency for [immutable.js](https://facebook.github.io/immutable-js/), if you want to use it.

Expand Down
2 changes: 1 addition & 1 deletion dist/redux-oidc.js

Large diffs are not rendered by default.

9 changes: 4 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "redux-oidc",
"version": "3.0.0-beta.7",
"version": "3.0.0-beta.8",
"description": "A package for managing OpenID Connect authentication in redux apps",
"main": "dist/redux-oidc.js",
"scripts": {
Expand Down Expand Up @@ -56,10 +56,9 @@
"webpack-node-externals": "^1.5.4"
},
"peerDependencies": {
"co": ">=4.6.0",
"react": ">=0.14.0",
"oidc-client": ">=1.2.0",
"prop-types": ">=15.5.0"
"react": ">=0.15.0",
"oidc-client": ">=1.3.0",
"prop-types": ">=15.5.0"
},
"optionalDependencies": {
"immutable": ">=3.6.0"
Expand Down
9 changes: 8 additions & 1 deletion src/actions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import {
USER_EXPIRING,
SESSION_TERMINATED,
LOADING_USER,
USER_SIGNED_OUT
USER_SIGNED_OUT,
LOAD_USER_ERROR
} from '../constants'

// dispatched when the existing user expired
Expand Down Expand Up @@ -67,3 +68,9 @@ export function userSignedOut() {
type: USER_SIGNED_OUT
};
}

export function loadUserError() {
return {
type: LOAD_USER_ERROR
};
}
1 change: 1 addition & 0 deletions src/constants/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ export const USER_EXPIRING = 'redux-oidc/USER_EXPIRING';
export const USER_FOUND = 'redux-oidc/USER_FOUND';
export const LOADING_USER = 'redux-oidc/LOADING_USER';
export const USER_SIGNED_OUT = 'redux-oidc/USER_SIGNED_OUT';
export const LOAD_USER_ERROR = 'redux-oidc/LOAD_USER_ERROR';
49 changes: 36 additions & 13 deletions src/helpers/loadUser.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,36 @@
import co from 'co';
import { userFound, userExpired } from '../actions';
import { userFound, userExpired, loadUserError } from '../actions';

export function* loadUserHandler(store, userManager) {
// stores the redux store here to be accessed by all functions
let reduxStore;

// helper function to set the redux store (for testing)
export function setReduxStore(newStore) {
reduxStore = newStore;
}

// helper function to get the redux store (for testing)
export function getReduxStore() {
return reduxStore;
}

// callback function called when the user has been loaded
export function getUserCallback(user) {
if (user && !user.expired) {
reduxStore.dispatch(userFound(user));
} else if (user && user.expired) {
reduxStore.dispatch(userExpired());
}
}

// error callback called when the userManager's loadUser() function failed
export function errorCallback(error) {
console.error(`redux-oidc: Error in loadUser() function: ${error.message}`);
reduxStore.dispatch(loadUserError());
}

// function to load the current user into the store
// NOTE: use only when silent renew is configured
export default function loadUser(store, userManager) {
if (!store || !store.dispatch) {
throw new Error('redux-oidc: You need to pass the redux store into the loadUser helper!');
}
Expand All @@ -10,15 +39,9 @@ export function* loadUserHandler(store, userManager) {
throw new Error('redux-oidc: You need to pass the userManager into the loadUser helper!');
}

const user = yield userManager.getUser();

if (user && !user.expired) {
store.dispatch(userFound(user));
} else {
store.dispatch(userExpired());
}
}
reduxStore = store;

export default function loadUser(store, userManager) {
co(loadUserHandler(store, userManager));
userManager.getUser()
.then(getUserCallback)
.catch(errorCallback);
}
47 changes: 36 additions & 11 deletions src/oidcMiddleware.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
import { userExpired, userFound, loadingUser } from './actions';
import { userExpired, userFound, loadingUser, loadUserError } from './actions';
import { USER_EXPIRED, LOADING_USER } from './constants';
import co from 'co';

// store the user here to prevent future promise calls to getUser()
export let storedUser = null;
// store the next middleware here so it can be accessed by the getUserCallback
export let nextMiddleware = null;

// helper function to set the stored next middleware (for testing)
export function setNext(newNext) {
nextMiddleware = newNext;
}

// a function to get the next middleware (for testing)
export function getNext() {
return nextMiddleware;
}

// helper function to set the stored user manually (for testing)
export function setStoredUser(user) {
Expand All @@ -15,22 +26,36 @@ export function removeStoredUser() {
storedUser = null;
}

// callback function to the userManager's getUser
export function getUserCallback(user) {
if (!user || user.expired) {
nextMiddleware(userExpired());
} else {
storedUser = user;
nextMiddleware(userFound(user));
}
}

// callback for the userManager's getUser.catch
export function errorCallback(error) {
console.error(`redux-oidc: Error loading user in oidcMiddleware: ${error.message}`);
nextMiddleware(loadUserError());
}

// the middleware handler function
export function* middlewareHandler(next, action, userManager) {
export function middlewareHandler(next, action, userManager) {
// prevent an infinite loop of dispatches of these action types (issue #30)
if (action.type === USER_EXPIRED || action.type === LOADING_USER) {
return next(action);
}

nextMiddleware = next;

if (!storedUser || storedUser.expired) {
next(loadingUser());
let user = yield userManager.getUser();
if (!user || user.expired) {
next(userExpired());
} else {
storedUser = user;
next(userFound(user));
}
userManager.getUser()
.then(getUserCallback)
.catch(errorCallback);
}
return next(action);
}
Expand All @@ -43,6 +68,6 @@ export default function createOidcMiddleware(userManager) {

// the middleware
return (store) => (next) => (action) => {
co(middlewareHandler(next, action, userManager));
middlewareHandler(next, action, userManager);
}
};
30 changes: 28 additions & 2 deletions tests/actions/index.test.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,27 @@
import '../setup';
import expect from 'expect';
import sinon from 'sinon';
import { USER_EXPIRED, REDIRECT_SUCCESS, USER_FOUND, SILENT_RENEW_ERROR, USER_EXPIRING, SESSION_TERMINATED, LOADING_USER, USER_SIGNED_OUT } from '../../src/constants';
import { userExpired, userFound, silentRenewError, sessionTerminated, userExpiring, redirectSuccess, loadingUser, userSignedOut } from '../../src/actions';
import {
USER_EXPIRED,
REDIRECT_SUCCESS,
USER_FOUND,
SILENT_RENEW_ERROR,
USER_EXPIRING,
SESSION_TERMINATED,
LOADING_USER, USER_SIGNED_OUT,
LOAD_USER_ERROR
} from '../../src/constants';
import {
userExpired,
userFound,
silentRenewError,
sessionTerminated,
userExpiring,
redirectSuccess,
loadingUser,
userSignedOut,
loadUserError
} from '../../src/actions';

describe('action - userExpired', () => {
it('should return the correct action object', () => {
Expand Down Expand Up @@ -68,3 +87,10 @@ describe('action - userSignedOut', () => {
expect(action.type).toEqual(USER_SIGNED_OUT);
});
});

describe('action - loadUserError', () => {
it('should return the correct action object', () => {
const action = loadUserError();
expect(action.type).toEqual(LOAD_USER_ERROR);
})
})
46 changes: 34 additions & 12 deletions tests/helpers/loadUser.test.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
import '../setup';
import sinon from 'sinon';
import expect from 'expect';
import { loadUserHandler } from '../../src/helpers/loadUser';
import { userExpired, userFound } from '../../src/actions';

const coMocha = require('co-mocha');
const mocha = require('mocha');
coMocha(mocha);
import loadUserHandler, { getUserCallback, errorCallback, setReduxStore, getReduxStore } from '../../src/helpers/loadUser';
import { userExpired, userFound, loadUserError } from '../../src/actions';

describe('helper - loadUser()', () => {
let userManagerMock;
let storeMock;
let getUserStub;
let dispatchStub;
let thenStub;
let catchStub;

beforeEach(() => {
dispatchStub = sinon.stub();
getUserStub = sinon.stub();
thenStub = sinon.stub();
catchStub = sinon.stub();

userManagerMock = {
getUser: getUserStub
Expand All @@ -25,23 +25,45 @@ describe('helper - loadUser()', () => {
storeMock = {
dispatch: dispatchStub
};

getUserStub.returns({
then: thenStub
});

thenStub.returns({
catch: catchStub
});

setReduxStore(storeMock);
});

it('should dispatch a valid user to the store', function* () {
it('should dispatch a valid user to the store', () => {
const validUser = { some: 'user' };
getUserStub.returns(validUser);

yield* loadUserHandler(storeMock, userManagerMock);
getUserCallback(validUser);

expect(dispatchStub.calledWith(userFound(validUser))).toEqual(true);
});

it('should dispatch USER_EXPIRED when no valid user is present', function* () {
it('should dispatch USER_EXPIRED when no valid user is present', () => {
const invalidUser = { expired: true };
getUserStub.returns(invalidUser);

yield* loadUserHandler(storeMock, userManagerMock);
getUserCallback(invalidUser);

expect(dispatchStub.calledWith(userExpired())).toEqual(true);
});

it('should set the redux store', () => {
loadUserHandler(storeMock, userManagerMock);

expect(getReduxStore() === storeMock).toEqual(true);
});

it('errorCallback should dispatch LOAD_USER_ERROR', () => {
setReduxStore(storeMock);

errorCallback({ message: 'Some message!'});

expect(dispatchStub.calledWith(loadUserError())).toEqual(true);
})
});
Loading

0 comments on commit a2202da

Please sign in to comment.