Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/hasura/react-check-auth

Add auth protection anywhere in your react/react-native app
https://github.com/hasura/react-check-auth

authentication npm-module react react-authentication

Last synced: 5 days ago
JSON representation

Add auth protection anywhere in your react/react-native app

Awesome Lists containing this project

README

        

## react-check-auth

`react-check-auth` is a tiny react component that helps you make auth checks declarative in your react or react-native app.

This component uses [React 16's new context API](https://reactjs.org/docs/context.html) and is just ~100 LOC. It can also serve as a boilerplate for getting familiar with using the context API to pass information from a parent component to arbitrarily deep child components.

## Motivation

In a typical app UI, depending on whether the user logs in, components in the application display different information.

For example, a "welcome user" label or a "login button" on a header. Or using this information with routing, `/home` should redirect to `/login` if the user is not logged in, and `/login` should redirect to `/home` if the user is logged in.

### Before `react-check-auth`

1. On load, your app must make a request to some kind of a `/verifyUser` or a `/fetchUser` endpoint to check if the existing persisted token/cookie is available and valid.
2. You need to store that information in app state and pass it as a prop all through your component tree just so that that child components can access it or use `redux` to store the state and `connect()` the consuming component.

### After `react-check-auth`

1. You specify the `authUrl` endpoint as a prop to a wrapper component called `` that has the latest props.

You don't need to make an API request, or pass props around, or manage state/reducers/connections in your app.

## Example

### 1) Add AuthProvider

Wrap your react app in a `AuthProvider` component that has an endpoint to fetch basic user information. This works because if the user had logged in, a cookie would already be present. For using authorization headers, check the docs after the examples.

```javascript
import React from "react";
import ReactDOM from "react-dom";

import {AuthProvider} from "react-check-auth";
import {Header, Main} from "./components";

const App = () => (


// The rest of your react app goes here




);

ReactDOM.render(, document.getElementById("root"));
```

### 2) Show a "welcome user" or a Login button

Now, in any arbitrary component, like a Header, you can check if the user is currently logged in. Typically you would use this for either showing a "welcome" label or a login button.

``` javascript
import {AuthConsumer} from 'react-check-auth';

const Header = () => (


// Use the AuthConsumer component to check
// if userInfo is available

{({userInfo, isLoading, error}) => (
userInfo ?
(Hi {userInfo.username}) :
(Login)
)}


);
```

### 3) Redirect not-logged in users to /login

You can mix and match `react-check-auth` with other declarative components like routing:

``` javascript
import {AuthConsumer} from 'react-check-auth';

const Main = () => (




);

const Home = () => {
return (

{({userInfo}) => {

// Redirect the user to login if they are not logged in
if (!userInfo) {
return ();
}

// Otherwise render the normal component
else {
return (

Welcome Home!
);
}
}}

);
}
);
```

## Usage guide

### I. Backend requirements

These are the backend requirements that are assumed by `react-check-auth`.

#### 1) API endpoint to return user information

An API request to fetch user information. It should take a cookie, or a header or a body for current session information.

For example:
```http
GET https://my-backend.com/api/user
Content-Type: application/json
Cookie: <...>
Authorization: Bearer <...>
```

#### 2) Success or logged-in response

If the user is logged in, the API should return a `200` status code with a `JSON` object.

For example:
```json
{
"username": "iamuser",
"id": 123
}
```

#### 3) Not logged-in response

If the user is not logged-in, the API should return a **non `200`** status code:

For example:
```http
Status: 403
```

### II. Installation

``` bash
$ npm install --save react-check-auth
```

### III. Set up `AuthProvider`

The `AuthProvider` component should be at the top of the component tree so that any other component in the app can consume the `userInfo` information.

The `AuthProvider` takes a required prop called `authUrl` and an optional prop called `reqOptions`.

```javascript

```

##### `authUrl` :: String
Should be a valid HTTP endpoint. Can be an HTTP endpoint of any method.

##### `reqOptions` :: Object || Function
Should be or return a valid `fetch` options object as per https://github.github.io/fetch/#options.

**Note: This is an optional prop that does not need to be specified if your `authUrl` endpoint is a GET endpoint that accepts cookies.**

Default value that ensures cookies get sent to a `GET` endpoint:
```json
{
"method": "GET",
"credentials": "include",
"headers": {
"Content-Type": "application/json"
},
}
```

#### Example 1: Use a GET endpoint with cookies

``` javascript
import React from 'react';
import {AuthProvider} from 'react-check-auth';

const authUrl = "https://my-backend.com/verifyAuth";

const App = () => (

// The rest of your app goes here

);
```

#### Example 2: Use a GET endpoint with a header

``` javascript
import React from 'react';
import {AuthProvider} from 'react-check-auth';

const authUrl = "https://my-backend.com/verifyAuth";
const reqOptions = {
'method': 'GET',
'headers': {
'Content-Type': 'application/json',
'Authorization' : 'Bearer ' + window.localStorage.myAuthToken
},
};

const App = () => (

// The rest of your app goes here

);
```

#### Example 3: Use a POST endpoint with updated token

``` javascript
import React from 'react';
import {AuthProvider} from 'react-check-auth';

const authUrl = "https://my-backend.com/verifyAuth";
const reqOptions = () => {
'method': 'POST',
'headers': {
'Content-Type': 'application/json',
'Authorization' : 'Bearer ' + window.localStorage.myAuthToken
},
};

const App = () => (

// The rest of your app goes here

);
```

### IV. Consuming auth state with ``

Any react component or element can be wrapped with an `` to consume the latest contextValue. You must write your react code inside a function that accepts the latest contextValue. Whenver the contextValue is updated then the AuthComponent is automatically re-rendered.

For example,
```javascript

{(props) => {

props.userInfo = {..} // returned by the API
props.isLoading = true/false // if the API has not returned yet
props.error = {..} // if the API returned a non-200 or the API call failed
}}

```

##### `props.userInfo` :: JSON

If the API call returned a 200 meaning that the current session is valid, `userInfo` contains as returned by the API.

If the API call returned a non-200 meaning that the current session is absent or invalid, `userInfo` is set to `null`.

##### `props.isLoading` :: Boolean

If the API call has not returned yet, `isLoading: true`. If the API call has not been made yet, or has completed then `isLoading: false`.

##### `props.error` :: JSON

If the API call returned a non-200 or there was an error in making the API call itself, `error` contains the parsed JSON value.

### V. Refresh state (eg: logout)

If you implement a logout action in your app, the auth state needs to be updated. All you need to do is call the `refreshAuth()` function available as an argument in the renderProp function of the `AuthConsumer` component.

For example:
```javascript

{(refreshAuth) => (
refreshAuth()
);
}}>
Logout


```

This will re-run the call to `authUrl` and update all the child components accordingly.

### VI. Using with React Native

Usage with React Native is exactly the same as with React. However you would typically use a Authorization header instead of cookies. Here's a quick example:

``` javascript

import { AuthProvider, AuthConsumer } from 'react-vksci123';

export default class App extends Component {
render() {
const sessionToken = AsyncStorage.getItem("@mytokenkey");
const reqOptions = {
"method": "GET",
"headers": sessionToken ? { "Authorization" : `Bearer ${sessionToken}` } : {}
}
return (



Welcome to React Native!


{({isLoading, userInfo, error}) => {
if (isLoading) {
return ();
}
if (error) {
return ( Unexpected );
}
if (!userInfo) {
return ();
}
return ();
}}



);
}
}

```

## Plug-n-play with existing auth providers

All Auth backend providers provide an endpoint to verify a "session" and fetch user information. This component was motivated from creating documentation for integrating Hasura's auth backend into a react app with minimum boilerplate. That said this package is meant to be used with any auth provider, including your own.

### Hasura

Hasura's Auth API can be integrated with this module with a simple auth get endpoint and can also be used to redirect the user to Hasura's Auth UI Kit in case the user is not logged in.

```javascript
// replace CLUSTER_NAME with your Hasura cluster name.
const authEndpoint = 'https://auth.CLUSTER_NAME.hasura-app.io/v1/user/info';

// pass the above reqObject to CheckAuth


{ ({ isLoading, userInfo, error }) => {
// your implementation here
} }


```

Read the docs here.

### Firebase

`CheckAuth` can be integrated with Firebase APIs.

```javascript
// replace API_KEY with your Firebase API Key and ID_TOKEN appropriately.
const authUrl = 'https://www.googleapis.com/identitytoolkit/v3/relyingparty/getAccountInfo?key=[API_KEY]';
const reqObject = { 'method': 'POST', 'payload': {'idToken': '[ID_TOKEN]'}, 'headers': {'content-type': 'application/json'}};

// pass the above reqObject to CheckAuth


{ ({ isLoading, userInfo, error }) => {
// your implementation here
} }


```

### Custom Provider

`CheckAuth` can be integrated with any custom authentication provider APIs.

Lets assume we have an endpoint on the backend `/api/check_token` which reads a header `x-access-token` from the request and provides with the associated user information

```javascript
const authEndpoint = 'http://localhost:8080/api/check_token';
const reqOptions = {
'method': 'GET',
'headers': {
'Content-Type': 'application/json',
'x-access-token': 'jwt_token'
}
};



{ ( { isLoading, userInfo, error, refreshAuth }) => {
if ( !userInfo ) {
return (
Please login
);
}
return (
Hello { userInfo ? userInfo.username.name : '' }
);
}}


```

It will render as `Please login` if the user's token is invalid and if the token is a valid one it will render Hello username

## How it works

![How it works](./how-it-works.png?raw=true)

1. The `AuthProvider` component uses the `authUrl` and `reqOptions` information given to it to make an API call
2. While the API call is being made, it sets the context value to have `isLoading` to `true`.
```json
{
"userInfo": null,
"isLoading": true,
"error": null
}
```
3. Once the API call returns, in the context value `isLoading` is set to `false' and:
4. Once the API call returns, if the user is logged in, the AuthProvider sets the context to `userInfo: `
```json
{
"userInfo": ,
"isLoading": false,
"error": null
}
```
5. If the user is not logged in, in the context value, `userInfo` is set to `null` and `error` is set to the error response sent by the API, if the error is in JSON.
```json
{
"userInfo": null,
"isLoading": false,
"error":
}
```
6. If the API call fails for some other reason, `error` contains the information

```json
{
"userInfo": null,
"isLoading": false,
"error":
}
```
7. Whenever the contextValue is updated, any component that is wrapped with `AuthConsumer` will be re-rendered with the contextValue passed to it as an argument in the renderProp function:

```javascript

{ ({userInfo, isLoading, error}) => {
return (...);
}}

```

## Contributing

Clone repo

````
git clone https://github.com/hasura/react-check-auth.git
````

Install dependencies

`npm install` or `yarn install`

Start development server

`npm start` or `yarn start`

Runs the demo app in development mode.

Open [http://localhost:3000](http://localhost:3000) to view it in the browser.

#### Source code

The source code for the react components are located inside `src/lib`.

#### Demo app

A demo-app is located inside `src/demo` directory, which you can use to test your library while developing.

#### Testing

`npm run test` or `yarn run test`

#### Build library

`npm run build` or `yarn run build`

Produces production version of library under the `build` folder.

## Maintainers

This project has come out of the work at [hasura.io](https://hasura.io).
Current maintainers [@Praveen](https://twitter.com/praveenweb), [@Karthik](https://twitter.com/k_rthik1991), [@Rishi](https://twitter.com/_rishichandra).