obabichev / react-token-auth

84 stars 22 forks source link

Disclaimer

In general, I would suggest using cookie sessions for web applications. But still, you may have a case when you have to use JWT tokens and store them in the local storage, here this library might be useful. React Native is also a good reason for using such a library.

React Token Auth (v2)

npm install react-token-auth

Motivation

react-token-auth is a small library to manage token in the auth process. It doesn't solve all the possible use cases but helps with the one of the most common: when you have accessToken and refreshToken, you need to store them in localStorage and update if necessary.

One of the problems with auth in React apps is that you need to keep in sync the state of the app (for example fact that user has been logged in) and token in the localStorage. Also, you need to be sure that the token you use in fetch is valid.

Features

Examples

This library has typings for TypeScript code, but nothing prevents you from just remove types from the examples and use as a JavaScript code.

Access and Refresh tokens

Let's assume you have a backend that uses accessToken and refreshToken to auth users and provides services /login, /register, and /update-token. All services return tokens in the next format:

{
  "accessToken": "...",
  "refreshToken": "..."
}

The first step you need to do is to create an instance of an authProvider.

import { createAuthProvider } from 'react-token-auth';

type Session = { accessToken: string; refreshToken: string };

export const { useAuth, authFetch, login, logout } = createAuthProvider<Session>({
    getAccessToken: session => session.accessToken,
    storage: localStorage,
    onUpdateToken: token =>
        fetch('/update-token', {
            method: 'POST',
            body: token.refreshToken,
        }).then(r => r.json()),
});

Parameter getAccessToken helps the library to find access token in the whole session object.

If the access token is expired and onUpdateToken function is provided library will try to update the token.

Function createAuthProvider returns some new functions to interact with the token provider.

login() might be used to save a new token after successful authorization or registration. For example:

const Login = () => {
    const onSubmit = (e: FormEvent) => {
        e.preventDefault();

        fetch('/login', {
            /**/
        })
            .then(r => r.json())
            .then(session => login(session));
    };

    return <form onSubmit={onSubmit}>{/**/}</form>;
};

In the same way function logout() might be used to remove token and clean storage.

The hook useAuth might be used to get access to the auth from React component to render (and rerender) the app depending on the current auth state. For example:

const Router = () => {
    const [logged, session] = useAuth();

    return (
        <BrowserRouter>
            <Switch>
                {!logged && (
                    <>
                        <Route path="/register" component={Register} />
                        <Route path="/login" component={Login} />
                        <Redirect to="/login" />
                    </>
                )}
                {logged && (
                    <>
                        <Route path="/dashboard" component={Dashboard} exact />
                        <Redirect to="/dashboard" />
                    </>
                )}
            </Switch>
        </BrowserRouter>
    );
};

onHydratation, when provided, receives the token when useAuth is called, inside onHydratation you can extract any information you need from it.

And the function authFetch() is the wrapper for function fetch(). authFetch() and fetch have the same API, but authFetch automatically passes the token from token provider to the network requests. For example from thunk:

export const getUser = (userId: string) => (dispatch: Dispatch) => {
    authFetch(`/user/${userId}`)
        .then(r => r.json())
        .then(user => {
            dispatch(getUserAction(user));
        });
};

Also if the token already saved in the localStorage, it will be restored after refreshing the page.

API

createAuthProvider<Session>(config: IAuthProviderConfig<Session>) -> IAuthProvider<Session>

IAuthProviderConfig<Session>

IAuthProvider<Session>

createAsyncAuthProvider<Session>(config: IAsyncAuthProviderConfig<Session>) -> IAsyncAuthProvider<Session>

IAsyncAuthProviderConfig<Session>

IAsyncAuthProvider<Session>

Storage interfaces

export interface IAuthStorage {
    getItem: (key: string) => Maybe<string>;
    setItem: (key: string, value: string) => void;
    removeItem: (key: string) => void;
}
export interface IAsyncAuthStorage {
    getItem: (key: string) => Promise<Maybe<string>>;
    setItem: (key: string, value: string) => Promise<void>;
    removeItem: (key: string) => Promise<void>;
}