├── .babelrc
├── .eslintrc
├── .gitignore
├── .npmignore
├── .travis.yml
├── LICENSE
├── README.md
├── package.json
└── src
├── authentication.js
├── index.js
└── test
├── authentication-test.js
└── init.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["es2015", "react"]
3 | }
4 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "eslint-config-airbnb",
3 | "env": {
4 | "browser": true,
5 | "mocha": true,
6 | "node": true
7 | },
8 | "plugins": [
9 | "react"
10 | ],
11 | "globals": {
12 | "expect": true
13 | },
14 | "parser": "babel-eslint",
15 | "rules": {
16 | "semi": [1, "never"],
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 |
5 | # Runtime data
6 | pids
7 | *.pid
8 | *.seed
9 |
10 | # Directory for instrumented libs generated by jscoverage/JSCover
11 | lib-cov
12 |
13 | # Coverage directory used by tools like istanbul
14 | coverage
15 |
16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
17 | .grunt
18 |
19 | # node-waf configuration
20 | .lock-wscript
21 |
22 | # Compiled binary addons (http://nodejs.org/api/addons.html)
23 | build/Release
24 |
25 | # Dependency directory
26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
27 | node_modules
28 | lib
29 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | src
2 | example
3 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - stable
4 | - 4
5 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Jackong
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # redux-authentication
2 |
3 | [![NPM version][npm-image]][npm-url]
4 | [![build status][travis-image]][travis-url]
5 | [![David deps][david-image]][david-url]
6 | [![node version][node-image]][node-url]
7 | [![Gittip][gittip-image]][gittip-url]
8 |
9 | [npm-image]: https://img.shields.io/npm/v/redux-authentication.svg?style=flat-square
10 | [npm-url]: https://npmjs.org/package/redux-authentication
11 | [travis-image]: https://travis-ci.org/Jackong/redux-authentication.svg?branch=master
12 | [travis-url]: https://travis-ci.org/Jackong/redux-authentication
13 | [david-image]: https://img.shields.io/david/Jackong/redux-authentication.svg?style=flat-square
14 | [david-url]: https://david-dm.org/Jackong/redux-authentication
15 | [node-image]: https://img.shields.io/badge/node.js-%3E=_0.11-green.svg?style=flat-square
16 | [node-url]: http://nodejs.org/download/
17 | [gittip-image]: https://img.shields.io/gratipay/Jackong.svg
18 | [gittip-url]: https://gratipay.com/~Jackong
19 |
20 | authentication component for redux
21 |
22 | # Install
23 |
24 | [](https://nodei.co/npm/redux-authentication/)
25 |
26 | # Examples
27 |
28 | ```js
29 | import authentication from 'redux-authentication'
30 | import {connect} from 'react-redux'
31 | import React, { PropTypes } from 'react'
32 | import {goToLogin} from 'your-actions'
33 |
34 | @connect(state => ({
35 | isAuthenticated: state.isAuthenticated
36 | }), {
37 | goToLogin,//map to props.goToLogin OR props.actions.goToLogin
38 | })
39 | @authentication
40 | class App extends React.Component {
41 | // will be called until isAuthenticated is updated to true
42 | render() {
43 | return (
44 |
45 | App
46 |
47 | )
48 | }
49 | }
50 |
51 | export default App
52 | ```
53 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "redux-authentication",
3 | "version": "2.0.0",
4 | "description": "authentication component for redux",
5 | "main": "lib/index.js",
6 | "devDependencies": {
7 | "babel-cli": "^6.2.0",
8 | "babel-core": "^6.2.1",
9 | "babel-eslint": "^4.1.6",
10 | "babel-preset-es2015": "^6.1.18",
11 | "babel-preset-react": "^6.1.18",
12 | "chai": "^3.4.1",
13 | "enzyme": "^2.3.0",
14 | "eslint": "^1.10.1",
15 | "eslint-config-airbnb": "^1.0.2",
16 | "eslint-plugin-react": "^3.10.0",
17 | "jsdom": "^9.4.1",
18 | "mocha": "^2.3.4",
19 | "react": "^15.2.0",
20 | "react-addons-test-utils": "^15.2.0",
21 | "react-dom": "^15.2.0",
22 | "react-redux": "^4.0.0",
23 | "redux": "^3.0.4",
24 | "sinon": "^1.17.4"
25 | },
26 | "scripts": {
27 | "test": "mocha --compilers js:babel-core/register --recursive --require src/test/init.js src/test/**/*-test.js",
28 | "test:watch": "mocha --compilers js:babel-core/register --recursive --require src/test/init.js -w src/test/**/*-test.js",
29 | "build": "babel src --out-dir lib",
30 | "clean": "rm -rf lib",
31 | "prepublish": "npm run clean && npm run build"
32 | },
33 | "repository": {
34 | "type": "git",
35 | "url": "git+https://github.com/Jackong/redux-authentication.git"
36 | },
37 | "keywords": [
38 | "react",
39 | "redux",
40 | "authenticate",
41 | "authentication",
42 | "auth"
43 | ],
44 | "author": "jackong ",
45 | "license": "MIT",
46 | "bugs": {
47 | "url": "https://github.com/Jackong/redux-authentication/issues"
48 | },
49 | "homepage": "https://github.com/Jackong/redux-authentication#readme",
50 | "dependencies": {}
51 | }
52 |
--------------------------------------------------------------------------------
/src/authentication.js:
--------------------------------------------------------------------------------
1 | import React, { PropTypes } from 'react'
2 |
3 | const authentication = Component => {
4 | class Authentication extends React.Component {
5 | constructor(props) {
6 | super(props)
7 | this.isAuthenticating = false
8 | this.authenticate(this.props)
9 | }
10 | componentWillReceiveProps(nextProps) {
11 | this.authenticate(nextProps)
12 | }
13 | authenticate({ isAuthenticated, goToLogin, actions }) {
14 | if (isAuthenticated) {
15 | this.isAuthenticating = false
16 | return true
17 | }
18 |
19 | // prevent to call goToLogin multiple times
20 | if (this.isAuthenticating) {
21 | return false
22 | }
23 | this.isAuthenticating = true
24 |
25 | if (goToLogin) {
26 | goToLogin()
27 | return false
28 | }
29 | if (actions && actions.goToLogin) {
30 | actions.goToLogin()
31 | return false
32 | }
33 | return false
34 | }
35 | render() {
36 | if (!this.props.isAuthenticated) {
37 | return null
38 | }
39 | return (
40 |
41 | )
42 | }
43 | }
44 | Authentication.propTypes = {
45 | isAuthenticated: PropTypes.bool,
46 | }
47 | Authentication.displayName = `Authentication(${Component.displayName || Component.name})`
48 | return Authentication
49 | }
50 |
51 | export default authentication
52 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | export { default as default} from './authentication'
2 |
--------------------------------------------------------------------------------
/src/test/authentication-test.js:
--------------------------------------------------------------------------------
1 | import sinon from 'sinon'
2 | import React from 'react'
3 | import { mount } from 'enzyme'
4 |
5 | import authentication from '../authentication'
6 |
7 | class App extends React.Component {
8 | componentWillMount() {
9 | }
10 | componentDidMount() {
11 | }
12 | componentWillReceiveProps() {
13 | }
14 | render() {
15 | return (
16 |
17 | App
18 |
19 | )
20 | }
21 | }
22 | const AppWithAuth = authentication(App)
23 |
24 | describe('Authentication', () => {
25 | beforeEach(() => {
26 | sinon.restore()
27 | })
28 |
29 | describe('with authentication flow', () => {
30 | it('should be ok', () => {
31 | const goToLogin = sinon.spy()
32 | const cwm = sinon.spy(App.prototype, 'componentWillMount')
33 | const cdm = sinon.spy(App.prototype, 'componentDidMount')
34 | const cwr = sinon.spy(App.prototype, 'componentWillReceiveProps')
35 | const render = sinon.spy(App.prototype, 'render')
36 | const authenticate = sinon.spy(AppWithAuth.prototype, 'authenticate')
37 |
38 |
39 | // from unauthenticated status
40 | const wrapper = mount(
41 |
42 | )
43 |
44 | expect(cwm.callCount).to.be.equal(0)
45 | expect(cdm.callCount).to.be.equal(0)
46 | expect(cwr.callCount).to.be.equal(0)
47 | expect(render.callCount).to.be.equal(0)
48 | expect(authenticate.callCount).to.be.equal(1)
49 |
50 | expect(goToLogin.callCount).to.be.equal(1)
51 |
52 | // to authenticated status
53 | wrapper.setProps({ isAuthenticated: true })
54 |
55 | expect(cwm.callCount).to.be.equal(1)
56 | expect(cdm.callCount).to.be.equal(1)
57 | expect(cwr.callCount).to.be.equal(0)
58 | expect(render.callCount).to.be.equal(1)
59 | expect(authenticate.callCount).to.be.equal(2)
60 |
61 | // keeping called once
62 | expect(goToLogin.callCount).to.be.equal(1)
63 |
64 | // set others props
65 | wrapper.setProps({ user: {} })
66 | expect(cwm.callCount).to.be.equal(1)
67 | expect(cdm.callCount).to.be.equal(1)
68 | expect(cwr.callCount).to.be.equal(1)
69 |
70 | // will re-render
71 | expect(render.callCount).to.be.equal(2)
72 |
73 | // should call authenticate to check
74 | expect(authenticate.callCount).to.be.equal(3)
75 |
76 | // and back to unauthenticated status
77 | wrapper.setProps({ isAuthenticated: false })
78 |
79 | expect(cwm.callCount).to.be.equal(1)
80 | expect(cdm.callCount).to.be.equal(1)
81 | expect(cwr.callCount).to.be.equal(1)
82 | expect(render.callCount).to.be.equal(2)
83 |
84 | // should call authenticate
85 | expect(authenticate.callCount).to.be.equal(4)
86 |
87 | // should go to login again
88 | expect(goToLogin.callCount).to.be.equal(2)
89 | })
90 | })
91 | })
92 |
--------------------------------------------------------------------------------
/src/test/init.js:
--------------------------------------------------------------------------------
1 | import {expect} from 'chai'
2 | import { jsdom } from 'jsdom'
3 |
4 | global.expect = expect
5 |
6 | const exposedProperties = ['window', 'navigator', 'document']
7 |
8 | global.document = jsdom('')
9 | global.window = document.defaultView
10 | Object.keys(document.defaultView).forEach((property) => {
11 | if (typeof global[property] === 'undefined') {
12 | exposedProperties.push(property)
13 | global[property] = document.defaultView[property]
14 | }
15 | })
16 |
17 | global.navigator = {
18 | userAgent: 'node.js',
19 | }
20 |
--------------------------------------------------------------------------------