A dead simple auth state manager for React. No magic, no suprises. This library is intentionally small. Many teams may prefer to inline similar logic instead of adding a dependency — this project exists to make that logic explicit and correct.
This is just a state machine that holds your auth tokens and user data. That's it. It doesn't authenticate anyone, it doesn't talk to Supabase or Firebase or whatever. It just manages the logged-in/logged-out state on the frontend.
Think of it like useState but spacifically for auth stuff.
Because every time I start a new React project, I end up writing the same auth boilerplate. Login form, logout button, protect some routes, store tokens in localStorage... you know the drill.
I got tired of copy-pasting this code around, so I made a library. Maybe you'll find it usefull too.
- Manages login/logout state
- Stores tokens in localStorage (or wherever you want)
- Gives you hooks to check if someone's logged in
- Protects routes that need authentication
- Handles token refresh when you tell it to
- No OAuth flows (build that yourself)
- No cookie auth (we use tokens)
- No automatic background token refresh (you call refresh when needed)
- No fancy UI components
- No backend (you need to build your own API)
npm install authbase-reactWrap your app:
import { AuthProvider } from 'authbase-react';
const authConfig = {
endpoints: {
login: 'https://your-api.com/auth/login',
refresh: 'https://your-api.com/auth/refresh',
logout: 'https://your-api.com/auth/logout',
},
};
function App() {
return (
<AuthProvider config={authConfig}>
<YourApp />
</AuthProvider>
);
}That's it. Now you can use the hooks anywhere. Pretty simple right?
import { useAuth } from 'authbase-react';
function LoginPage() {
const { signIn, isLoading, error } = useAuth();
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const handleSubmit = async (e) => {
e.preventDefault();
try {
await signIn(email, password);
// user is now logged in baby
} catch (err) {
// error is in the error state, you know what to do?
}
};
return (
<form onSubmit={handleSubmit}>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
<button disabled={isLoading}>
{isLoading ? 'Loading...' : 'Login'}
</button>
{error && <p>{error.message}</p>}
</form>
);
}import { RequireAuth } from 'authbase-react';
function Dashboard() {
return (
<RequireAuth redirectTo="/login">
<YourDashboard />
</RequireAuth>
);
}If the user isn't logged in, they get redirected to /login. Simple as that.
import { useIsAuthenticated } from 'authbase-react';
function Navigation() {
const isLoggedIn = useIsAuthenticated();
return (
<nav>
{isLoggedIn ? (
<a href="/dashboard">Dashboard</a>
) : (
<a href="/login">Login</a>
)}
</nav>
);
}import { useUser } from 'authbase-react';
function Profile() {
const user = useUser();
if (!user) return null;
return <div>Hey {user.name}!</div>;
}import { useAuth } from 'authbase-react';
function LogoutButton() {
const { signOut } = useAuth();
return <button onClick={signOut}>Logout</button>;
}import { useAuth } from 'authbase-react';
function SomeComponent() {
const { refresh, accessToken } = useAuth();
const callAPI = async () => {
let response = await fetch('/api/data', {
headers: { Authorization: `Bearer ${accessToken}` }
});
if (response.status === 401) {
// token expired, fuckin' refresh it
await refresh();
// now let it try again with the new token folks....
}
};
}Your backend needs to match this:
Login endpoint:
POST /auth/login
Body: { identifier: string, secret: string }
Response: {
access_token: string,
refresh_token?: string,
user: object
}
Failure cases:
- 401/403 for invalid credentials (will surface as an error in the auth state)
- 4xx/5xx for API issues (will surface as an error in the auth state)
Refresh endpoint (optional):
POST /auth/refresh
Headers: Authorization: Bearer {refresh_token}
Response: {
access_token: string
}
Failure cases:
- 401/403 for expired/invalid refresh token (will transition to unauthenticated)
- 4xx/5xx for API issues (will surface as an error in the auth state)
Logout endpoint (optional):
POST /auth/logout
Headers: Authorization: Bearer {access_token}
Response: 204
Failure cases:
- 4xx/5xx errors are captured but local auth state still clears
That's all we support right now. If your API looks different, this library won't work for you (yet). Sorry bout that.
By default we use localStorage. Want sessionStorage instead? Easy peasy.
const authConfig = {
endpoints: { /* ... */ },
storage: window.sessionStorage,
};useAuth() - Everything
const {
signIn, // (identifier, secret) => Promise<void>
signOut, // () => Promise<void>
refresh, // () => Promise<void>
user, // object | null
accessToken, // string | null
isAuthenticated, // boolean
isLoading, // boolean
error, // Error | null
} = useAuth();useUser() - Just the user object
useIsAuthenticated() - Just a boolean, yep
Authbase-react is intentionally deterministic. These are the only possible states and transitions:
States
idle→ initial state before storage is checkedloading→ a login/logout/refresh/init is in progressauthenticated→ user + access token are presentunauthenticated→ no valid sessionerror→ an operation failed (error is stored in state)
Common transitions
idle→loading→authenticated | unauthenticatedunauthenticated→loading→authenticated(login success)authenticated→loading→unauthenticated(logout)authenticated→loading→authenticated | unauthenticated(refresh)
This library is intentionally boring. There's no clever tricks, no abstractions, no "magic". It's just a state machine that stores some data. Boring is good sometimes.
We don't do automatic token refresh because that's complicated and every app needs it differently. Just call refresh() when you need it. Simple.
We don't support OAuth because that's a whole different beast. Build your OAuth flow, then use this library to store the result. Makes sense right?
We don't have fancy loading states or retry logic because your app probably needs custom handling anyway. You know your app better than we do.
-
You need OAuth social login (use next-auth or similar, seriously)
-
You use cookie-based auth (this is for tokens only)
-
You need SSR/Next.js support (maybe later)
-
You want something that "just works" with zero config (this needs backend work, sorry)
authbase-react does not attempt to provide the most secure possible browser auth model.
Storing access and refresh tokens in JavaScript-accessible storage (localStorage / sessionStorage) is vulnerable to XSS. For high-security apps, prefer:
httpOnly cookie-based refresh tokens
backend-managed sessions
This library is best suited for apps where those trade-offs are acceptable.
Found a bug? Open an issue. Want to add something? Check out CONTRIBUTING.md for the full contribution guide.
MIT - do whatever you want with it, seriously!!!!!!!!!
Open an issue and I'll try to help. No guarantees but I'll do my best.