React-Client-Auth
Pluggable client-side authentication for react
react-client-auth
is a solution for handling client-side authentication in react. It supports any authentication provider that can supply a handful of key methods and exposes a user and session controls via the React Context API as well as a useAuth()
hook.
This is still pretty beta.
Setup
- Install the
@react-client-auth/core
plus a provider or configure your own.
yarn add @react-client-auth/core @react-client-auth/auth0
- Near the top of your component tree, add the
SessionProvider
, passing it a compatibleauth
service prop.
import React from "react";
import { SessionProvider } from "@react-client-auth/core";
import { Auth0Service } from "@react-client-auth/auth0";
const App = ({ Component, pageProps }) => {
const auth = new Auth0Service();
return (
<SessionProvider auth={auth}>
<Component {...pageProps} />
</SessionProvider>
);
};
- Use the
useAuth()
hook (orSessionContext.Consumer
in a class component) to access the activeuser
and other utilities.
In your authentication flow (here a typical 0Auth callback page like /auth/callback
):
import { useRouter } from "next/router";
import { useAuth } from "@react-client-auth/core";
const postLogin = (router) => {
const postLoginUrl = localStorage.getItem("postLoginUrl");
localStorage.removeItem("postLoginUrl");
router.push(postLoginUrl || "/");
};
const CallbackPage = () => {
const session = useAuth();
const router = useRouter();
React.useEffect(() => {
if (/access_token|id_token|error/.test(location.hash)) {
session.auth
.handleAuthentication()
.then((user) => {
session.setUser(user);
postLogin(router);
})
.catch((error) => {
console.warn("authentication rejected", error);
router.push("/");
});
}
}, []);
return <pre>Loading ...</pre>;
};
In a presentational component:
import { useAuth } from "@react-client-auth/core";
const SessionButton = () => {
const { user, auth } = useAuth();
return user.isLoggedIn ? (
<div>
<h2>Hello, {user.profile.nickname}</h2>
<button onClick={() => auth.logout()}>Log Out</button>
</div>
) : (
<div>
<h2>Who are you?</h2>
<button onClick={() => auth.authorize()}>Sign In</button>
</div>
);
};
API & Authentication Services
It is simple to write your own service for use with the SessionProvider by satisfying a minimal interface. Methods should be capable of working in a browser environment and politely exiting in a server environment.
Authentication Service Interface
reloadSession(): Promise<User>
- Required
The only hard dependency of the service, this method is used by the SessionProvider when mounting the app to load a user from the client. You will need to have some method of contacting an identity provider from your client and retrieving the necessary info to construct a User
.
handleAuthentication(): Promise<User>
This method should be used to trigger any post-login authentication steps - commonly a token exchange on a page like /auth/callback
. Like reloadSession()
it should return a Promise containing the authenticated user, but how you actually use this is up to you
authorize(): void
+ logout(): void
These methods should simply trigger the login + logout flows with your authentication provider along with any side effects (directing the user to a landing page, etc).
User
interface
The User interface is very simple and extendable. It should have a boolean isLoggedIn
property and a profile
object property which can hold any local user data (name, photo, etc). You can extend this interface as needed - see the Auth0 package's user
and service
for an example.
useAuth()
and SessionContext
The SessionContext
is a react Context containing a getter and setter for the current user, a reference to your authentication service and boolean for the loading state of the session. Aside from the required reloadSession()
method mentioned above, the interface on your auth
object is up to you. Please make a pull request to loosen any type restriction issues you have here.
interface Session {
isLoading: boolean;
user: User;
setUser: (u: User) => void;
auth: {
authorize: () => void;
logout: () => void;
handleAuthentication: () => {};
};
}
// In use:
const { isLoading user, setUser, auth } = useAuth() // = useContext(SessionContext)
<SessionContext.Consumer>
{ ({user}) => <h1>Hello, {user.profile.name}</h1> }
</SessionContext.Consumer>
Contributing
Contributors' Covenant. Issues and pull requests welcome.
License
MIT.