hive-authentication
v1.0.0
Published
A React package for Hive blockchain authentication with a simple callback-based API and Zustand state management.
Downloads
1,179
Readme
Hive Authentication
A React package for Hive blockchain authentication with a simple callback-based API and Zustand state management.
Installation
npm install hive-authenticationQuick Start
1. Import CSS
import 'hive-authentication/build.css';2. Use the Auth Button with Callback
import { AuthButton, useAuthStore } from 'hive-authentication';
import { initAioha } from '@aioha/aioha'
import { AiohaProvider } from '@aioha/react-provider'
const aioha = initAioha(
{
hivesigner: {
app: 'hive-auth-demo.app',
callbackURL: window.location.origin + '/hivesigner.html',
scope: ['login', 'vote']
},
hiveauth: {
name: 'Hive Authentication Demo',
description: 'A demo app for testing Hive authentication'
}
}
)
function App() {
const { currentUser, loggedInUsers } = useAuthStore();
// Subscribe to store changes using Zustand
useEffect(() => {
const unsubscribe = useAuthStore.subscribe((state) => {
console.log('Store state changed:', state);
});
return unsubscribe;
}, []);
// Your Hive authentication callback - works for both login AND adding accounts
const handleAuthenticate = async (hiveResult) => {
// Make your API call here
const response = await fetch('/api/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
challenge: hiveResult.challenge,
username: hiveResult.username,
pubkey: hiveResult.publicKey,
proof: hiveResult.proof,
}),
});
if (!response.ok) {
throw new Error('Server authentication failed');
}
const data = await response.json();
// Return your server response as JSON string
return JSON.stringify(data);
};
// Optional Web2 callback (Google / Email)
const handleWeb2Authenticate = async (web2Result) => {
const response = await fetch('/api/web2-login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(web2Result),
});
if (!response.ok) {
throw new Error('Web2 authentication failed');
}
const data = await response.json();
return JSON.stringify(data);
};
return (
<AiohaProvider aioha={aioha}>
<div>
<h1>My App</h1>
<AuthButton
encryptionKey={import.meta.env.VITE_APP_ENCRYPTION_KEY || 'your-secure-encryption-key'}
onAuthenticate={handleAuthenticate}
aioha={aioha}
shouldShowSwitchUser={true}
isActiveFieldVisible={false}
onClose={() => {
console.log("AuthButton dialog closed");
}}
onSignMessage={(username) => {
return `${new Date().toISOString()}:${username}`;
}}
web2Config={import.meta.env.VITE_FIREBASE_CONFIG ? JSON.parse(import.meta.env.VITE_FIREBASE_CONFIG) : undefined}
onWeb2Authenticate={handleWeb2Authenticate}
theme={"light"}
/>
{currentUser && (
<p>Welcome, {currentUser.username}!</p>
)}
</div>
</AiohaProvider>
);
}Programmatic login for private posting key
import { useProgrammaticAuth } from "./hooks/useProgrammaticAuth";
function YouComponent() {
const { loginWithPrivateKey } = useProgrammaticAuth(aioha);
const handleProgrammaticLogin = async () => {
const userInfo = await loginWithPrivateKey(user, key, async (hiveResult) => {
console.log("Hive result:", hiveResult);
// TODO: Add server validation
return JSON.stringify({ message: "Server validation successful" });
});
console.log("User logged in:", userInfo);
};
return (
<div>
<button onClick={handleProgrammaticLogin} className="btn btn-primary">
Programmatic Login
</button>
</div>
);
}Private key login (optional Active Key)
When users choose "Private Key" login, they enter a Posting Key (required). You can optionally show an Active Key field by passing isActiveFieldVisible={true} to AuthButton. If provided, the active key is validated against the account’s active authority and included in hiveResult.privateActiveKey and in the stored user. It is never required for login.
Note: Both configurations are required even if you only plan to use one provider. This ensures the Aioha library is properly initialized with all necessary settings.
HiveAuth Support: The package now fully supports HiveAuth login! When using HiveAuth:
- Event Listening: The package automatically listens for
hiveauth_login_requestevents from the Aioha library - QR Code Display: When a HiveAuth login request is received, a QR code is displayed for the user to scan
- Wallet Integration: Users can scan the QR code with their HiveAuth wallet app to approve the login
- Automatic Handling: The package handles the entire flow from login request to authentication completion
How It Works
- User clicks login → Package shows login modal
- User enters username → Package authenticates with Hive blockchain
- Package calls your callback → Your app makes API call to your server
- Your app returns result → Package stores the data and updates state
- If callback fails → Package automatically logs out the user
Important: The same callback function is used for:
- Initial login (when user first logs in)
- Adding accounts (when user clicks "Add Account" in the switch user modal)
This ensures consistent authentication flow for all user operations.
HiveAuth Login Flow
When a user chooses HiveAuth login:
- Login Request: User clicks "Login with HiveAuth" button
- Event Emission: Aioha library emits a
hiveauth_login_requestevent - Form Hiding: Login form (username input and buttons) is automatically hidden
- QR Code Generation: Package generates a QR code from the HiveAuth payload
- QR Code Display: Large, scannable QR code is displayed with 30-second countdown timer
- Wallet Scan: User scans QR code with their HiveAuth wallet app
- Wallet Approval: User approves the login in their wallet
- Authentication Complete: Package receives the authentication result and proceeds with your callback
- Timer Expiry: If 30 seconds pass without approval, QR code expires and login form reappears
Key Features:
- Automatic Form Hiding: Login form disappears when QR code is shown
- Real QR Code: Actual QR code generated from HiveAuth payload (not placeholder)
- 30-Second Timer: Countdown timer with automatic expiry
- Cancel Option: User can manually cancel QR code display
- Seamless UX: Smooth transition between login form and QR code display
Programmatic Authentication
For developers who need to authenticate users programmatically (e.g., when they already have private keys stored securely), the package provides a programmatic authentication API.
Using the Hook (Recommended)
import { useProgrammaticAuth } from 'hive-authentication';
function MyComponent() {
const {
loginWithPrivateKey,
switchToUser,
logout,
logoutAll,
getCurrentUser,
getAllUsers
} = useProgrammaticAuth(aioha);
const handleLogin = async () => {
try {
// Login with private key (no server validation)
const user = await loginWithPrivateKey('username', '5J...');
console.log('Logged in:', user.username);
} catch (error) {
console.error('Login failed:', error.message);
}
};
const handleLoginWithServerValidation = async () => {
try {
// Login with private key + server validation
const user = await loginWithPrivateKey('username', '5J...', async (hiveResult) => {
const response = await fetch('/api/validate', {
method: 'POST',
body: JSON.stringify(hiveResult)
});
return response.json();
});
console.log('Logged in with server validation:', user.username);
} catch (error) {
console.error('Login failed:', error.message);
}
};
return (
<div>
<button onClick={handleLogin}>Login Programmatically</button>
<button onClick={handleLoginWithServerValidation}>Login with Server Validation</button>
</div>
);
}Using the Service Class
import { ProgrammaticAuth } from 'hive-authentication';
const programmaticAuth = new ProgrammaticAuth(aioha);
// Login with private key
const user = await programmaticAuth.loginWithPrivateKey('username', '5J...');
// Switch to another user
await programmaticAuth.switchToUser('other-username');
// Get current user
const currentUser = programmaticAuth.getCurrentUser();
// Logout current user
await programmaticAuth.logout();
// Logout all users
await programmaticAuth.logoutAll();API Reference
loginWithPrivateKey(username, privatePostingKey, serverCallback?)
Authenticates a user with their private posting key.
- username: The Hive username
- privatePostingKey: The private posting key (starts with '5J')
- serverCallback: Optional callback function for server validation
- Returns: Promise
switchToUser(username)
Switches to a previously logged-in user.
- username: The username to switch to
- Returns: Promise
logout()
Logs out the current user.
- Returns: Promise
logoutAll()
Logs out all users.
- Returns: Promise
getCurrentUser()
Gets the currently logged-in user.
- Returns: LoggedInUser | null
getAllUsers()
Gets all logged-in users.
- Returns: LoggedInUser[]
State Management
The package uses Zustand for state management. You can access the authentication state directly:
const { currentUser, loggedInUsers, isLoading, error } = useAuthStore();State Properties
currentUser: Currently logged-in user (or null)loggedInUsers: Array of all logged-in usersisLoading: Whether authentication is in progresserror: Any error message (or null)
Subscribing to Changes
Use Zustand's built-in subscription to react to state changes:
useEffect(() => {
const unsubscribe = useAuthStore.subscribe((state) => {
// React to any state changes
console.log('Auth state changed:', state);
});
return unsubscribe;
}, []);API Reference
Components
AuthButton
The main authentication button.
Props:
interface AuthButtonProps {
encryptionKey: string; // Required: encryption key for local storage (e.g. from your env/config)
onAuthenticate: (hiveResult: HiveAuthResult) => Promise<string>;
aioha: Aioha;
theme?: "light" | "dark"; // default: "light"
shouldShowSwitchUser?: boolean; // default: true
isActiveFieldVisible?: boolean; // default: false — when true, shows optional Active Key field in private key login
onClose?: () => void;
onSignMessage: (username: string) => string;
/**
* Optional colors for the Login button.
* - Single color: ['#ff0000']
* - Gradient: ['#ff0000', '#00ff00', '#0000ff']
*/
loginButtonColors?: string[];
/** Optional color for the "Login" text when the user is not logged in. */
loginButtonTextColor?: string;
/** Optional Firebase config for Web2 login (Google & Email). If omitted, Web2 button is hidden. */
web2Config?: Web2Config;
/** Optional callback for Web2 authentication result. Required when web2Config is provided. */
onWeb2Authenticate?: (web2Result: Web2AuthResult) => Promise<string>;
}Usage:
<AuthButton
encryptionKey={import.meta.env.VITE_APP_ENCRYPTION_KEY || 'your-secure-encryption-key'}
onAuthenticate={handleAuthenticate}
aioha={aioha}
shouldShowSwitchUser={true}
isActiveFieldVisible={false}
onSignMessage={(username) => `${new Date().toISOString()}:${username}`}
// Solid color example
// loginButtonColors={["#ef4444"]}
// Gradient example
// loginButtonColors={["#ec4899", "#8b5cf6"]}
// Custom text color
// loginButtonTextColor="#ffffff"
// Optional Web2 login support (Google / Email)
// web2Config={firebaseConfig}
// onWeb2Authenticate={handleWeb2Authenticate}
/>Store
useAuthStore()
Access authentication state and actions.
State:
const { currentUser, loggedInUsers, isLoading, error } = useAuthStore();Note: The store provides read-only access to state. All modifications are handled internally by the package.
Types
interface HiveAuthResult {
provider: string; // 'keychain' | 'hiveauth' | 'privatePostingKey'
challenge: string; // Hive signature
publicKey: string; // User's public key
username: string; // Hive username
proof: string; // Timestamp
privatePostingKey?: string; // Present for private key login
privateActiveKey?: string; // Optional; present when user entered active key (private key login, only if isActiveFieldVisible was true)
}
interface LoggedInUser {
username: string;
provider: string;
challenge: string;
publicKey: string;
proof: string;
serverResponse: string; // Your server response
privatePostingKey?: string;
privateActiveKey?: string; // Optional active key when provided at login
}
interface Web2Config {
apiKey: string;
authDomain: string;
projectId: string;
databaseURL?: string;
storageBucket?: string;
messagingSenderId?: string;
appId: string;
measurementId?: string;
}
interface Web2AuthResult {
provider: "google" | "email";
uid: string;
email: string | null;
displayName: string | null;
photoURL: string | null;
idToken: string;
}License
MIT
