npm package: @onsmart/auth-client
Description: Authorization management and authentication checking for react apps.
Author: Juan Pujol
Latest version: 1.6.0

# bkn-auth

Authorization management and authentication checking for react/redux apps.

_Wait a minute. Authentication checking? What you mean by that?_

Well. The complete authentication management is a concern of [Account](https://github.com/Beakyn/gsp-account-ui) app. So other apps **DO NOT** know how to login a user, but they check if a token is still valid.

**bkn-auth** uses some High Order Components (HOCs) and all processes are orchestrated by [redux](http://redux.js.org/) and [redux-saga](https://redux-saga.js.org/). So this library have things like _decorators_, _actions_, _reducers_ and _sagas_.

So, let's rock!

## Table of Contents

- [Features](#features)
- [Installation](#installation)
- [Getting Started](#getting-started)
- [API Reference](#api-reference)
- [Contributing](#contributing)

## Features

- Authorization by roles and teams
- Checks token expiration periodically
- Injects some special props in container components
- Wired to the store by actions dispatchs

## Installation

```sh
npm install -S -E bkn-auth
```

Or using _yarn_.

```sh
yarn add -E bkn-auth
```

It has some peer dependencies, so if you not installed them, you have to.

```sh
yarn add -E redux redux-saga bkn-ui
```

**Note:** _check_ [Installtion](https://github.com/Beakyn/bkn-ui#installation) _section in bkn-ui repo for more details._

## Getting Started

### Authentication checking

Start by forking `authenticationFlow` saga. You'll need to pass domain and account app url as parameters.

```javascript
import {fork} from 'redux-saga/effects';
import {sagas as bknAuthSagas} from 'bkn-auth';

const domain = 'localhost';
const accountAppUrl = 'http://localhost:9000';

export function* rootSaga() {
  yield fork(bknAuthSagas.authenticationFlow, domain, accountAppUrl);
  // other sagas
}
```

`authenticationFlow` saga will check periodically if authentication token is valid. If not, it will dispatch an action to update `tokenExpired` property in global state. But a dispatch of an action without a reducer is useless. So let's include some reducers.

```javascript
import {reducers as bknAuthReducers} from 'bkn-auth';

export const rootReducer = combineReducers({
  // other reducers
  ...bknAuthReducers,
});
```

This will include our reducers and `tokenExpired` and `user` properties will be available in global state. These reducers will catch our actions dispatched in the previous step.

**IMPORTANT:** _bkn-auth reducers have to be included in root reducer. Do not include them inside any redux modules of your application._

**Note:** _see_ [Reducers](https://github.com/Beakyn/bkn-auth/wiki/3.-Reducers) _section for more details._

Ok. Now we have our global state updating automatically. But what to do in UI level if token expires? Let's use the `authenticationRequired` HOC.

```jsx
import {authenticationRequired} from 'bkn-auth';

@authenticationRequired
export class SomeTopLevelComponent extends React.Component {
  // some code
}
```

`authenticationRequired` will read the `tokenExpired` property from global state. If token is expired, it opens a modal informing user that his session expired. This modal have a _LOGIN_ button. User can click on it to be redirected to login page or if user login in another tab (in the same browser), modal will be dismissed.

**Tip:** _use_ `authenticationRequired` _in a top level component. It's not necessary to use it in every page or route level container component._

**Note:** _do not forget to import_ `ConfirmModal` _styles from_ [bkn-ui](https://github.com/Beakyn/bkn-ui) _to present modal in a properly way._

```scss
// Layout.scss

@import "node_modules/bkn-ui/scss/components/confirm-modal";
```

So, authentication checking is configured. For the last part of this getting started, let's see how to manage authorization. If your application doesn't need authorization, we finish here. If so, keep reading.

### Authorization

We have a special HOC for it as well: `authorize`. Let's see an example:

```jsx
import {authorize} from 'bkn-auth';

// ListPage.js

@authorize({roles: ['editor']})
export class ListPage extends React.Component {
  // some code
}

// FormPage.js

@authorize({teams: ['sales']})
export class FormPage extends React.Component {
  // some code
}
```

As you can see, _ListPage_ can only be accessed by users with _editor_ role. _FormPage_, only by _sales_ team.

_admin_ is the default role. So if user is _admin_, he can access all pages. You don't need to use `authorize` HOC at all.

So based on above example, if a user with _author_ role try to access _ListPage_, `authorize` will dispatch a `redirectNotAuthorizedUser` action. Thus you can redirect user to a specific route. Let's listen for this action by using `takeLatest` effect from `redux-saga`.

```javascript
import {takeLatest} from 'redux-saga/effects';
import {push} from 'react-router-redux';
import {actions as bknAuthActions} from 'bkn-auth';

function* navigateToHomePage() {
  yield put(push('/home'));
}

export function* rootSaga() {
  // other sagas
  yield takeLatest(bknAuthActions.redirectNotAuthorizedUser, redirectToHomePage);
}
```

**Note:** _as you can see, we can use_ `put` _effect from_ [react-router-redux](https://github.com/reactjs/react-router-redux) _to change route from a saga. But be aware, react-router-redux is not related to this library._

And... that's it! You can see more details is next section, API Reference.

## API Reference

- [HOCs](https://github.com/Beakyn/bkn-auth/wiki/1.-HOCs)
- [Redux Actions](https://github.com/Beakyn/bkn-auth/wiki/2.-Redux-Actions)
- [Reducers](https://github.com/Beakyn/bkn-auth/wiki/3.-Reducers)
- [Sagas](https://github.com/Beakyn/bkn-auth/wiki/4.-Sagas)
- [Auth API](https://github.com/Beakyn/bkn-auth/wiki/5.-Auth-API)
- [Models](https://github.com/Beakyn/bkn-auth/wiki/6.-Models)
- [Theming](https://github.com/Beakyn/bkn-auth/wiki/7.-Theming)

## Contributing

1. Create an issue describing clearly the new feature or problem.
2. Create a branch with issue name.
3. Improve/Fix it.
4. Generate bundle and types by running:

    `npm run build`

5. Bump version in package.json based on [SemVer](http://semver.org/).
6. Create a PR and inform what issue is closed. For example: 

    `Closes #1`

7. Approve and merge PR.
8. Delete branch.
9. Publish to npm:

    `npm publish`

10. Create a tag for current version. For example:

    `git tag 1.0.0`

11. Push tag to Github.

    `git push --tags`

12. Write change log in tag body based on merged PRs. Example [here](https://github.com/Beakyn/bkn-auth/releases/tag/2.0.0).
13. Be happy. =]