@entropy-tamer/reynard-auth-composables
v0.17.1
Published
SolidJS composables for authentication in Reynard framework
Maintainers
Readme
Reynard Auth Composables
SolidJS composables and components for authentication in the Reynard ecosystem. This package provides reactive authentication state management and pre-built UI components for SolidJS applications.
Features
- Reactive State: SolidJS signals for authentication state
- Pre-built Components: LoginForm, RegisterForm, and PasswordStrengthMeter
- Context Provider: AuthProvider for app-wide authentication state
- Type Safety: Full TypeScript support with comprehensive interfaces
- Validation Integration: Built-in form validation with reynard-validation
- Theme Integration: Styled components that work with Reynard themes
Installation
pnpm add reynard-auth-composablesBasic Usage
Setup AuthProvider
import { AuthProvider } from "reynard-auth-composables";
function App() {
return (
<AuthProvider
config={{
apiBaseUrl: "https://api.example.com",
autoRefresh: true,
enableRememberMe: true,
}}
>
<YourApp />
</AuthProvider>
);
}Use Authentication State
import { useAuth } from "reynard-auth-composables";
function UserProfile() {
const { user, isAuthenticated, login, logout, isLoading } = useAuth();
return (
<div>
{isAuthenticated() ? (
<div>
<h2>Welcome, {user()?.username}!</h2>
<button onClick={logout}>Logout</button>
</div>
) : (
<div>
<p>Please log in</p>
<button onClick={() => login({ identifier: "user", password: "pass" })}>
Login
</button>
</div>
)}
</div>
);
}Components
LoginForm
A complete login form with validation and error handling:
import { LoginForm } from "reynard-auth-composables";
function LoginPage() {
const handleLogin = async (credentials) => {
try {
await login(credentials);
// Handle successful login
} catch (error) {
// Handle login error
}
};
return (
<LoginForm
onLogin={handleLogin}
loading={isLoading()}
showRememberMe={true}
requireEmail={false}
/>
);
}LoginForm Props
interface LoginFormProps {
onLogin: (credentials: LoginCredentials) => Promise<void>;
loading?: boolean;
showRememberMe?: boolean;
requireEmail?: boolean;
className?: string;
}RegisterForm
A complete registration form with validation:
import { RegisterForm } from "reynard-auth-composables";
function RegisterPage() {
const handleRegister = async (data) => {
try {
await register(data);
// Handle successful registration
} catch (error) {
// Handle registration error
}
};
return (
<RegisterForm
onRegister={handleRegister}
loading={isLoading()}
requireEmail={true}
requireFullName={true}
/>
);
}RegisterForm Props
interface RegisterFormProps {
onRegister: (data: RegisterData) => Promise<void>;
loading?: boolean;
requireEmail?: boolean;
requireFullName?: boolean;
className?: string;
}PasswordStrengthMeter
A password strength indicator component:
import { PasswordStrengthMeter } from "reynard-auth-composables";
function PasswordField() {
const [password, setPassword] = createSignal("");
return (
<div>
<input
type="password"
value={password()}
onInput={(e) => setPassword(e.target.value)}
/>
<PasswordStrengthMeter password={password()} />
</div>
);
}PasswordStrengthMeter Props
interface PasswordStrengthMeterProps {
password: string;
className?: string;
}Composables
useAuth
The main authentication composable that provides reactive state and actions:
import { useAuth } from "reynard-auth-composables";
function MyComponent() {
const {
// State
authState,
user,
isAuthenticated,
isLoading,
error,
isRefreshing,
// Actions
login,
register,
logout,
refreshTokens,
changePassword,
initialize,
// Utilities
tokenManager,
} = useAuth();
// Use the state and actions
return (
<div>
{isAuthenticated() ? (
<p>Welcome, {user()?.username}!</p>
) : (
<p>Please log in</p>
)}
</div>
);
}useAuth Return Value
interface UseAuthReturn {
// State
authState: Accessor<AuthState>;
user: Accessor<User | null>;
isAuthenticated: Accessor<boolean>;
isLoading: Accessor<boolean>;
error: Accessor<string | null>;
isRefreshing: Accessor<boolean>;
// Actions
login: (credentials: LoginCredentials) => Promise<void>;
register: (data: RegisterData) => Promise<void>;
logout: () => Promise<void>;
refreshTokens: () => Promise<void>;
changePassword: (data: PasswordChangeData) => Promise<void>;
initialize: () => Promise<void>;
// Utilities
tokenManager: AuthTokenManager;
}usePasswordStrength
A composable for password strength analysis:
import { usePasswordStrength } from "reynard-auth-composables";
function PasswordField() {
const [password, setPassword] = createSignal("");
const { strength, score, feedback } = usePasswordStrength(password);
return (
<div>
<input
type="password"
value={password()}
onInput={(e) => setPassword(e.target.value)}
/>
<div>Strength: {strength()}</div>
<div>Score: {score()}/4</div>
{feedback().length > 0 && (
<ul>
{feedback().map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
)}
</div>
);
}Context Provider
AuthProvider
The AuthProvider component provides authentication context to your entire application:
import { AuthProvider } from "reynard-auth-composables";
function App() {
return (
<AuthProvider
config={{
apiBaseUrl: "https://api.example.com",
autoRefresh: true,
enableRememberMe: true,
refreshThreshold: 300,
}}
callbacks={{
onLoginSuccess: (user) => console.log("Welcome:", user.username),
onLogout: () => console.log("Goodbye!"),
onSessionExpired: () => console.log("Session expired"),
}}
>
<YourApp />
</AuthProvider>
);
}AuthProvider Props
interface AuthProviderProps {
config: AuthConfig;
callbacks?: {
onLoginSuccess?: (user: User) => void;
onLogout?: () => void;
onSessionExpired?: () => void;
onError?: (error: string) => void;
};
fallback?: Component;
children: any;
}Form Validation
The components integrate with reynard-validation for form validation:
import { validateEmail, validateUsername } from "reynard-validation";
// Email validation
const emailResult = validateEmail("[email protected]");
if (!emailResult.isValid) {
console.error(emailResult.error);
}
// Username validation
const usernameResult = validateUsername("myusername");
if (!usernameResult.isValid) {
console.error(usernameResult.error);
}Styling
The components are styled to work with Reynard themes and can be customized:
import { LoginForm } from "reynard-auth-composables";
function StyledLoginForm() {
return (
<LoginForm
onLogin={handleLogin}
className="custom-login-form"
// Components use CSS classes that can be overridden
/>
);
}CSS Classes
.login-form- Main login form container.register-form- Main registration form container.password-strength-meter- Password strength meter container.form-field- Individual form field.form-error- Error message styling.form-button- Form button styling
Error Handling
The composables provide comprehensive error handling:
import { useAuth } from "reynard-auth-composables";
function LoginComponent() {
const { login, error, isLoading } = useAuth();
const handleLogin = async (credentials) => {
try {
await login(credentials);
// Success
} catch (err) {
// Error is automatically set in the error signal
console.error("Login failed:", error());
}
};
return (
<div>
{error() && <div class="error">{error()}</div>}
<LoginForm onLogin={handleLogin} loading={isLoading()} />
</div>
);
}Integration Examples
With React Router
import { useAuth } from "reynard-auth-composables";
import { Navigate } from "@solidjs/router";
function ProtectedRoute({ children }) {
const { isAuthenticated } = useAuth();
return isAuthenticated() ? children : <Navigate href="/login" />;
}With Notifications
import { useAuth } from "reynard-auth-composables";
import { useNotifications } from "reynard-core";
function LoginComponent() {
const { login } = useAuth();
const { notify } = useNotifications();
const handleLogin = async (credentials) => {
try {
await login(credentials);
notify("Login successful!", "success");
} catch (error) {
notify("Login failed", "error");
}
};
return <LoginForm onLogin={handleLogin} />;
}Testing
The composables include test utilities for SolidJS testing:
import { render, screen } from "@solidjs/testing-library";
import { AuthProvider } from "reynard-auth-composables";
function TestComponent() {
const { isAuthenticated } = useAuth();
return <div>{isAuthenticated() ? "Authenticated" : "Not authenticated"}</div>;
}
test("shows authentication state", () => {
render(() => (
<AuthProvider config={{ apiBaseUrl: "http://localhost:3000" }}>
<TestComponent />
</AuthProvider>
));
expect(screen.getByText("Not authenticated")).toBeInTheDocument();
});API Reference
Components
AuthProvider- Authentication context providerLoginForm- Pre-built login formRegisterForm- Pre-built registration formPasswordStrengthMeter- Password strength indicator
Composables
useAuth- Main authentication composableusePasswordStrength- Password strength analysis
Types
AuthProviderProps- AuthProvider configurationLoginFormProps- LoginForm configurationRegisterFormProps- RegisterForm configurationUseAuthReturn- useAuth return type
License
MIT License - see LICENSE file for details.
