diff --git a/tools/react/README.md b/tools/react/README.md
new file mode 100644
index 0000000..8d85b13
--- /dev/null
+++ b/tools/react/README.md
@@ -0,0 +1,53 @@
+# TokenProvider
+
+The MERN-template utilizes React's `useContext()` hook to share the auth-server's access token, effectively globally. Here is a quick rundown of how it works.
+
+# Enabling TokenProvider
+
+To enable the TokenProvider component, wrap your App component with it, like so:
+
+```jsx
+import React from 'react';
+import ReactDOM from 'react-dom';
+
+import App from './pages/app';
+import TokenProvider from './pages/utilities/token-provider';
+
+ReactDOM.render(
+
+
+ ,
+ document.querySelector('#root')
+);
+```
+
+# Accessing The Access Token
+
+To access the access token from your app, you simply use the `useContext` hook, like so:
+
+```jsx
+import React, { useContext } from 'react';
+import { TokenContext } from '../utilities/token-provider';
+
+const Component = props => {
+ //context
+ const authTokens = useContext(TokenContext);
+
+ //use the access token
+ console.log(authTokens.accessToken);
+
+ return
;
+};
+
+export default Component;
+```
+
+# Most Useful Features Provided
+
+The most useful features provided by TokenProvider are:
+
+* `tokenFetch()`, which wraps the `fetch()` API to ensure that your access token is valid
+* `tokenCallback()`, which passes the authTokens as a parameter to any function passed into it
+* `getPayload()`, which returns the payload of the accessToken (including as "email", "username", "admin", and "mod")
+* `accessToken`, this will be falsy if the user is not logged in
+
diff --git a/tools/react/token-provider.jsx b/tools/react/token-provider.jsx
new file mode 100644
index 0000000..da3ab63
--- /dev/null
+++ b/tools/react/token-provider.jsx
@@ -0,0 +1,127 @@
+import React, { useState, useEffect, createContext } from 'react';
+import decode from 'jwt-decode';
+
+export const TokenContext = createContext();
+
+//DOCS: tokenFetch() and tokenCallback() are actually closures here
+
+const TokenProvider = props => {
+ //state to be used
+ const [accessToken, setAccessToken] = useState('');
+ const [refreshToken, setRefreshToken] = useState('');
+
+ //make the access and refresh tokens persist between reloads
+ useEffect(() => {
+ setAccessToken(localStorage.getItem("accessToken") || '');
+ setRefreshToken(localStorage.getItem("refreshToken") || '');
+ }, []);
+
+ //update the stored copies
+ useEffect(() => {
+ localStorage.setItem("accessToken", accessToken);
+ localStorage.setItem("refreshToken", refreshToken);
+ }, [accessToken, refreshToken]);
+
+ //wrap the default fetch function
+ const tokenFetch = async (url, options) => {
+ //use this?
+ let bearer = accessToken;
+
+ //if expired (10 minutes, normally)
+ const expired = new Date(decode(accessToken).exp * 1000) < Date.now();
+
+ if (expired) {
+ //BUGFIX: if logging out, just skip over the refresh token
+ if (url === `${process.env.AUTH_URI}/auth/logout`) {
+ return fetch(url, {
+ method: 'DELETE',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Access-Control-Allow-Origin': '*',
+ 'Authorization': `Bearer ${bearer}`
+ },
+ body: JSON.stringify({
+ token: refreshToken
+ })
+ });
+ }
+
+ //ping the auth server for a new token
+ const response = await fetch(`${process.env.AUTH_URI}/auth/token`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Access-Control-Allow-Origin': '*'
+ },
+ body: JSON.stringify({
+ token: refreshToken
+ })
+ });
+
+ //any errors, throw them
+ if (!response.ok) {
+ throw `${response.status}: ${await response.text()}`;
+ }
+
+ //save the new auth stuff (setting bearer as well)
+ const newAuth = await response.json();
+
+ setAccessToken(newAuth.accessToken);
+ setRefreshToken(newAuth.refreshToken);
+ bearer = newAuth.accessToken;
+ }
+
+ //finally, delegate to fetch
+ return fetch(url, {
+ ...(options || {}),
+ headers: {
+ ...(options || { headers: {} }).headers,
+ 'Authorization': `Bearer ${bearer}`
+ }
+ });
+ };
+
+ //access the refreshed token via callback
+ const tokenCallback = async (cb) => {
+ //if expired (10 minutes, normally)
+ const expired = new Date(decode(accessToken).exp * 1000) < Date.now();
+
+ if (expired) {
+ //ping the auth server for a new token
+ const response = await fetch(`${process.env.AUTH_URI}/auth/token`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Access-Control-Allow-Origin': '*'
+ },
+ body: JSON.stringify({
+ token: refreshToken
+ })
+ });
+
+ //any errors, throw them
+ if (!response.ok) {
+ throw `${response.status}: ${await response.text()}`;
+ }
+
+ //save the new auth stuff (setting bearer as well)
+ const newAuth = await response.json();
+
+ setAccessToken(newAuth.accessToken);
+ setRefreshToken(newAuth.refreshToken);
+
+ //finally
+ return cb(newAuth.accessToken);
+ } else {
+ return cb(accessToken);
+ }
+ };
+
+ return (
+ decode(accessToken) }}>
+ {props.children}
+
+ )
+};
+
+export default TokenProvider;