Haaremy SSO Auth SDK
The official Haaremy SSO Authentication SDK provides a comprehensive solution for integrating Haaremy's Single Sign-On system into JavaScript applications. It encapsulates token handling, login/logout flows, BroadcastChannel-based tab synchronization, and offers specific integrations for React and Next.js. Currently at version 2.0.0, this SDK is actively maintained with a focus on security and developer experience. Key differentiators include its 'zero localStorage' approach (tokens are memory-only), proactive token refresh, server-side replay detection for token families, and efficient offline JWT validation using JWKS caching with Ed25519 signatures. It offers distinct entrypoints for vanilla JS/framework-agnostic core logic, React hooks and components, and Next.js middleware/server-side helpers, making it adaptable to various application architectures.
Common errors
-
Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object.
cause Attempting to use a React component like `HmyAuthProvider` or `HmyLoginForm` imported from the main `@haaremy/auth` entrypoint instead of the `@haaremy/auth/react` subpath.fixChange `import { HmyAuthProvider } from '@haaremy/auth'` to `import { HmyAuthProvider } from '@haaremy/auth/react'` (and similarly for other React components/hooks). -
TypeError: Cannot destructure property 'createHmyMiddleware' of 'undefined' as it is undefined.
cause This error typically occurs if `createHmyMiddleware` is imported incorrectly from the main package or if the Next.js subpath is not correctly resolved.fixEnsure you are importing `createHmyMiddleware` from the correct subpath: `import { createHmyMiddleware } from '@haaremy/auth/next'`. -
ReferenceError: init is not defined
cause The `init` function was called without being imported, or imported incorrectly from a CommonJS `require` statement that doesn't align with the ESM-first design.fixAdd `import { init } from '@haaremy/auth'` to the top of your file. If using CommonJS, consider migrating to ESM or ensuring your build process correctly transpiles. -
ERR_INVALID_AUTH_STATE: Cannot perform authenticated request. User is not authenticated.
cause An `authFetch` request was attempted when the SDK's internal state indicated the user was 'unauthenticated' or 'loading', meaning no valid access token was available.fixBefore making requests with `authFetch`, ensure your application has completed initialization (`await init()`) and the user state is `authenticated`. You can use `subscribe` or `useAuth` to monitor the authentication state.
Warnings
- breaking Version 2.0.0 introduces a refactor of the SDK's internal architecture, particularly how subpath imports are organized. Ensure all React-specific components (`HmyAuthProvider`, `useAuth`, `HmyLoginForm`) are imported from `@haaremy/auth/react` and Next.js-specific utilities (`createHmyMiddleware`) from `@haaremy/auth/next`.
- gotcha The SDK strictly adheres to a 'zero localStorage' policy for Access Tokens, storing them only in JavaScript memory. This means that if a user closes all tabs or the browser, the Access Token is lost, requiring a re-login (which might be handled transparently by the refresh token in the cookie on next visit).
- gotcha The Next.js Middleware (`createHmyMiddleware`) relies on `publicPaths` to define routes that do not require authentication. Failing to correctly configure this array can lead to all routes being protected, including login/registration pages, resulting in redirect loops or inaccessible routes.
- breaking The `authFetch` utility, which automatically injects the Access Token into requests, expects an initialized SDK with a valid session. Using `authFetch` before `await init()` has completed or when the user is `unauthenticated` can lead to failed requests.
- gotcha The SDK uses Ed25519 for JWT signing. While highly secure, ensure your backend SSO system correctly issues Ed25519-signed JWTs and exposes a compatible JWKS endpoint (`/.well-known/jwks.json`). Incompatibilities can lead to token validation failures.
Install
-
npm install haaremy-auth -
yarn add haaremy-auth -
pnpm add haaremy-auth
Imports
- init
const { init } = require('@haaremy/auth')import { init } from '@haaremy/auth' - useAuth
import { useAuth } from '@haaremy/auth'import { useAuth } from '@haaremy/auth/react' - HmyAuthProvider
import { HmyAuthProvider } from '@haaremy/auth/react' - createHmyMiddleware
import createHmyMiddleware from '@haaremy/auth/next'
import { createHmyMiddleware } from '@haaremy/auth/next' - login
import { login } from '@haaremy/auth'
Quickstart
import { HmyAuthProvider, useAuth, HmyLoginForm } from '@haaremy/auth/react';
const ssoUrl = process.env.NEXT_PUBLIC_SSO_URL ?? 'https://sso.haaremy.de'; // Ensure SSO URL is configured
// This component would typically be in your _app.tsx or layout.tsx
function AppWrapper({ children }) {
return (
<HmyAuthProvider config={{ ssoUrl: ssoUrl }}>
{children}
</HmyAuthProvider>
);
}
// This component demonstrates how to use the auth state within your application
function MyAuthenticatedPage() {
const { state, user, login, logout, getAccessToken } = useAuth();
if (state === 'loading') {
return <div>Authentifizierungsstatus wird geladen...</div>;
}
if (state === 'unauthenticated') {
return (
<div>
<p>Sie sind nicht angemeldet.</p>
<HmyLoginForm
onSuccess={(loggedInUser) => {
console.log('Login erfolgreich:', loggedInUser.display_name);
}}
onError={(error) => {
console.error('Login fehlgeschlagen:', error.message);
}}
/>
</div>
);
}
// State is 'authenticated'
return (
<div>
<h1>Hallo, {user?.display_name}!</h1>
<p>Ihre E-Mail: {user?.email}</p>
<button onClick={logout}>Abmelden</button>
<button onClick={async () => {
const token = await getAccessToken();
console.log('Aktueller Access Token:', token ? token.substring(0, 20) + '...' : 'Kein Token');
}}>Token anzeigen</button>
</div>
);
}
// Example usage within a React component tree:
// function Root() {
// return (
// <AppWrapper>
// <MyAuthenticatedPage />
// </AppWrapper>
// )
// }
// export default Root;