Archive post
Day 8 - Secure π Login with AIOHA & LocalStorage Magic π§
β’ sagarkothari88
implement login using AIOHA π and call Distriator login API π, storing encrypted user data in localStorage πΎ
## π£ Hello Hive Community Members!
---
Welcome back to my wild ReactJS learning ride π’ β and guess what? Weβre already on **Day 8**! If you've missed previous episodes, don't worry. Grab some popcorn πΏ and catch up below:
---
### π **ReactJS Journey So Far**
- [π **Day One**](https://peakd.com/hive-139531/@sagarkothari88/integrate-aioha-with-your-react-app-from-scratch-vite--tailwind--daisyui): Fresh React app + AIOHA integration!
- [π§ **Day Two**](https://peakd.com/hive-139531/@sagarkothari88/integrating-react-router-my-routing-adventures-with-react): Routing drama & rebellious NavBar π€
- [π οΈ **Day Three**](https://peakd.com/hive-139531/@sagarkothari88/from-navbar-mess-to-react-router-success-fixing-layouts-routing-and-aioha-in-one-go): Fixed Layouts, Routing & AIOHA π₯
- [π§ **Day Four**](https://peakd.com/hive-139531/@sagarkothari88/day-4-reactjs-adventures--distriator-progress-usestate-useeffect-and-env-chaos): useState, useEffect, and .env headaches π
- [π§Ή **Day Five**](https://peakd.com/hive-139531/@sagarkothari88/level-up-your-imports-using-path-aliases-for-cleaner-react--typescript-projects): Path Aliases to clean up relative path spaghetti π
- [π‘ **Day Six**](https://peakd.com/hive-139531/@sagarkothari88/day-6-learning-reactjs-calling-apis-and-creating-context-providers-or-distriator-feature-update): Create Context Provider & call API
- [π **Day Seven**](#): Show a toast message
---
## π Whatβs New in Day 8?
In this episode, I went full-on security ninja. π₯· Here's what I implemented:
- π Login API with @aioha & distriator combo
- π¦ Storing user data *securely* in `localStorage` using AES encryption & base64 encoding
- β
Upsert logic for managing multiple Hive logins
---
## π§ **Data Modeling Like a Boss**
π Folder: `src/types/LoginApiRequestResponse.tsx`
```
export interface LoginRequestDTO {
challenge: string;
proof: string;
pubkey: string;
username: string;
}
export interface LoginSuccessResponseDTO {
token: string;
type: string;
}
export interface LocalStorageUserDTO {
token: string;
type: string;
challenge: string;
proof: string;
pubkey: string;
username: string;
}
export interface LoginErrorResponseDTO {
error: string;
}
```
---
## πΎ **Encrypting User Data in LocalStorage**

π File: `src/utils/LocalStorageUtils.tsx`
Letβs bring in the secret scrolls of utility magic! π§
### π Base64 Helper Spells
```
function toBase64(str: string): string {
return typeof window !== "undefined"
? window.btoa(unescape(encodeURIComponent(str)))
: Buffer.from(str, "utf-8").toString("base64");
}
function fromBase64(b64: string): string {
return typeof window !== "undefined"
? decodeURIComponent(escape(window.atob(b64)))
: Buffer.from(b64, "base64").toString("utf-8");
}
```
---
### π₯ Retrieve Users from LocalStorage (aka Decrypt Scroll)
```
export function getLoggedInUsers(): LocalStorageUserDTO[] {
const apiKey = import.meta.env.VITE_LOCAL_KEY;
const data = localStorage.getItem("logged-in-users") || "";
const decrypteText = CryptoJS.AES.decrypt(data, apiKey).toString(
CryptoJS.enc.Utf8
);
if (!data) return [];
try {
const jsonStr = fromBase64(decrypteText);
return JSON.parse(jsonStr);
} catch (e) {
console.error("Failed to decode/parse localStorage ", e);
return [];
}
}
```
π Steps:
- Get π key from `.env`
- Decrypt β decode β parse β return!
---
### π Store/Update New Logged-In Users (Avengers-like registry π¦Έ)
```
export function setLoggedInUsers(users: LocalStorageUserDTO[]): void {
try {
const apiKey = import.meta.env.VITE_DEKEY;
const jsonStr = JSON.stringify(users);
const base64Str = toBase64(jsonStr);
const encryptedText = CryptoJS.AES.encrypt(base64Str, apiKey).toString();
localStorage.setItem("logged-in-users", encryptedText);
} catch (e) {
console.error("Failed to encode/set localStorage ", e);
}
}
```
---
### π Save/Update User Info (like a React-powered CRM π)
```
export function saveOrUpdateUser(newUser: LocalStorageUserDTO): void {
const users = getLoggedInUsers();
const idx = users.findIndex((user) => user.username === newUser.username);
if (idx !== -1) {
users[idx] = newUser;
} else {
users.push(newUser);
}
setLoggedInUsers(users);
}
```
π
ββοΈ No remove logic yet β coming with future logout feature. Stay tuned!
---
## π **Calling the Login API β FTW!**
π File: `src/api/LoginApi.tsx`
Hereβs how the server handshake looks:
```
export const loginApi = async (challenge, proof, pubkey, username): Promise<null | string> => {
try {
const rqst: LoginRequestDTO = { challenge, proof, pubkey, username };
const response = await fetch("https://beta-api.distriator.com/login", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(rqst),
});
if (!response.ok) {
const errorBody: LoginErrorResponseDTO = await response.json();
console.error("Login failed:", errorBody);
return `Error - ${errorBody.error}`;
}
const success: LoginSuccessResponseDTO = await response.json();
const updatedUser: LocalStorageUserDTO = { ...rqst, token: success.token, type: success.type };
saveOrUpdateUser(updatedUser);
return null;
} catch (error) {
console.error("Network error:", error);
return `Network error - ${error.message}`;
}
};
```
π₯ Boom! Now you're securely logged in and your data is sealed like Wakanda tech! π‘οΈ
---
## π€ AIOHA + Distriator Login Combo πͺ
π File: `src/components/HiveUserAvatarButton.tsx`
We've glued it all together using `@aioha/react-ui`. π§©
```
<AiohaModal
displayed={modalDisplayed}
loginOptions={{
msg: proof,
keyType: KeyTypes.Posting,
}}
onLogin={performLogin}
onClose={setModalDisplayed}
/>
```
And hereβs the magic `performLogin` function:
```
async function performLogin(result: LoginResult) {
if (result.success) {
const loginResult = await loginApi(result.result, proof, result.publicKey || "none", result.username);
if (loginResult == null) {
console.log("π Logged in successfully!");
} else {
console.error(`Login failed: ${loginResult}`);
}
} else {
console.error(result.error || "Something went wrong");
}
}
```
---
## π― Wrapping Up
This was a deep dive into login flows, secure data handling & functional integration of blockchain login workflows! π§©
That's it for now, folks! Thank you so much for reading my post. π
More power to the **Hive Blockchain** π
More power to all our community members π
Until next time, Happy Coding! π»β¨
---
### π Final Note
- I asked AI to help optimize this post to make it more readable and viewer-friendly.
- Here is the link where you can find both original content & improvements made by AI
- https://www.perplexity.ai/search/e4abc965-4e52-442b-b001-ab6942995dff
---
### π My Contributions to β¦οΈ Hive Ecosystem
| Contribution | To | Hive | Ecosystem |
|--------------|----|------|-----------|
| Hive Witness Node | Hive API Node (in progress) | 3Speak Video Encoder Node Operator (highest number of nodes) | 3Speak Mobile App Developer |
| 3Speak Podcast App Developer | 3Speak Shorts App Developer | 3Speak Support & Maintenance Team | [Distriator Developer](https://distriator.com/) |
| [CheckinWithXYZ](https://checkinwith.xyz/) | [Hive Inbox](https://hive-inbox.the-hive-mobile.app/) | [HiFind](https://hifind.the-hive-mobile.app/) | [Hive Donate App](https://donate.the-hive-mobile.app/) |
| Contributed to HiveAuth Mobile App | Ecency β 3Speak Integration | Ecency β InLeo Integration | Ecency β Actifit Integration |
| [Hive Stats App](https://stats.the-hive-mobile.app/) | [Vote for Witness App](https://witness.the-hive-mobile.app/) | [HiveFlutterKit](https://hiveflutterkit.sagarkothari88.one/docs/intro) | [New 3Speak App](https://3speak.sagarkothari88.one/#/?tab=home) |
---
### π Support Back
β€οΈ **Appreciate my work? Consider supporting @threespeak & @sagarkothari88!** β€οΈ
| Vote | For | Witness |
|------|-----|---------|
| [](https://witness.the-hive-mobile.app/#/witnesses/@sagarkothari88) | [sagarkothari88](https://witness.the-hive-mobile.app/#/witnesses/@sagarkothari88) | @sagarkothari88 |
| [](https://witness.the-hive-mobile.app/#/witnesses/@threespeak) | [threespeak](https://witness.the-hive-mobile.app/#/witnesses/@threespeak) | @threespeak |
---