Skip to content

Latest commit

 

History

7 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

@ferrumec/auth

Framework-agnostic authentication for React apps: session management, automatic token refresh, an authenticated HTTP client, and reusable auth UI — behind a small integration surface. No backend assumptions, no business logic.

Install

npm install @ferrumec/auth

react-router-dom is an optional peer dependency, only needed if you use @ferrumec/auth/router.

Quick start

import {
  AuthProvider,
  JwtProvider,
  LocalStorageTokenStorage,
  useAuth,
  LoginPage,
  AuthGuard,
} from "@ferrumec/auth";
import "@ferrumec/auth/theme.css"; // optional — CSS custom properties, not required

const provider = new JwtProvider({
  baseUrl: "/api/auth",
  storage: new LocalStorageTokenStorage(),
  idleTimeoutMs: 30 * 60_000, // optional — log out after 30 min idle
});

function Root() {
  return (
    <AuthProvider provider={provider} apiBaseUrl="/api">
      <App />
    </AuthProvider>
  );
}

function App() {
  const auth = useAuth();
  if (!auth.authenticated) return <LoginPage />;

  return (
    <AuthGuard>
      <Dashboard />
    </AuthGuard>
  );
}

// Elsewhere in the app — use auth.client instead of fetch()
function Dashboard() {
  const auth = useAuth();
  useEffect(() => {
    auth.client.request("/orders").then(setOrders);
  }, []);
  // ...
}

Registration + email confirmation

JwtProvider (and any other provider implementing RegistrableAuthProvider) supports self-service sign-up: creating the account starts a session immediately, same as login(), and a follow-up step confirms the address with a code the backend emails out.

import { RegisterPage } from "@ferrumec/auth";

<RegisterPage
  title="Create your account"
  onSuccess={() => navigate("/")}
  onSwitchToLogin={() => navigate("/login")}
/>;

RegisterPage is a two-step wizard — username/email/password, then a confirmation-code screen — built from useAuth() plus the standalone RegisterForm and EmailConfirmationForm components, exactly the way LoginPage is built from LoginForm. useAuth().register / requestEmailConfirmation / confirmEmailConfirmation are only defined when the configured provider supports them; redirect-flow providers (OAuth2/OIDC, Keycloak, Auth0, Azure) leave them undefined since those identity providers own sign-up themselves.

const auth = useAuth();

await auth.register?.({ username, password }); // starts a session, like login()
const { nonce } = await auth.requestEmailConfirmation?.(email) ?? {};
await auth.confirmEmailConfirmation?.(nonce, codeFromEmail);

By default this targets POST {baseUrl}/auth/register to create the account, then POST {baseUrl}/notifications/console/preferences/set / .../confirm to request and redeem the confirmation code (configurable via emailConfirmationChannel / emailConfirmationSubject on JwtProvider, or override endpoints.register for the registration call itself). Skip the confirmation step with <RegisterPage skipEmailConfirmation /> if your backend doesn't require it.

Switching providers

Every provider implements the same AuthProvider interface — swap the constructor, change nothing else in your app:

// First-party JSON/JWT backend
new JwtProvider({ baseUrl: "/api/auth", storage: new LocalStorageTokenStorage() });

// Generic OAuth2 / OIDC (Authorization Code + PKCE)
new OAuthProvider({
  endpoints: { authorize: "...", token: "...", userInfo: "..." },
  clientId: "...",
  redirectUri: window.location.origin + "/callback",
  storage: new LocalStorageTokenStorage(),
});

// Keycloak
new KeycloakProvider({ baseUrl: "https://id.example.com", realm: "acme", clientId: "...", redirectUri: "...", storage });

// Auth0
new Auth0Provider({ domain: "acme.us.auth0.com", clientId: "...", redirectUri: "...", storage });

// Azure AD / Entra ID
new AzureProvider({ tenantId: "...", clientId: "...", redirectUri: "...", storage });

For redirect-flow providers (OAuth2/OIDC family), call auth.login() from a button click — it navigates the browser to the identity provider. The resulting ?code= is exchanged automatically the next time AuthProvider mounts and calls initialize(), so make sure your redirect URI renders the app with <AuthProvider> at the root.

What the package owns vs. what it doesn't

  • Owns: tokens, refresh scheduling and deduplication, idle timeout, cross-tab logout/refresh sync, request auth injection/retry, auth UI.
  • Does not own: authorization (roles/permissions/scopes — keep that in your app), routing (only @ferrumec/auth/router touches react-router-dom, and it's optional), business logic, styling (Tailwind utility classes ship on every element alongside a stable fka-* class you can target instead).

Replacing built-in UI

AuthProvider accepts loadingScreenComponent and sessionExpiredDialogComponent overrides for the two pieces it renders automatically. LoginPage and UserMenu are plain compositions of useAuth() — write your own and never import ours.

Package layout

src/
  api/          AuthClient, HttpClient, SessionManager, TokenStorage
  components/   LoginPage, LoginForm, RegisterPage, RegisterForm,
                EmailConfirmationForm, LogoutButton, UserMenu, LoadingScreen,
                AuthGuard, SessionExpiredDialog
  context/      AuthProvider, AuthContext
  hooks/        useAuth, useSession
  providers/    BaseAuthProvider, JwtProvider, OAuthProvider,
                KeycloakProvider, Auth0Provider, AzureProvider
  router/       AuthRoutes (ProtectedRoute, PublicOnlyRoute) — optional
  types/        the AuthProvider / TokenStorage contracts
  utils/        EventEmitter, BroadcastBus, PKCE helpers

About

reusable frontend package for the auth service

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages