ππ» νμκ°μ
λ° λ‘κ·ΈμΈ κΈ°λ₯μ μμν©λλ€.
We are launching the sign-up and login functions.
ππ» publick key κ²μ¦λ‘μ§μ μΆκ°νμ΅λλ€.(κΈ°μ‘΄μ κ²μ¦ μμ΄ ν¨μ€)
Added public key verification logic (previously, it proceeded without verification).
ππ» Userν
μ΄λΈ ꡬ쑰λ₯Ό λ³κ²½ νμ΅λλ€.
The User table structure has been modified.
ππ» μ 체 μ½λλ κΉνλΈμμ νμΈ ν μ μμ΅λλ€.
You can find the full code on GitHub.
https://github.com/gideonslife01/flm-nextjs
π μ 체 νλ‘μ νΈ κ΅¬μ‘° / Overall Project Structure
myapp project/
βββ app/ (Next.js App Router)
β βββ .well-known/webfinger/route.ts -> webfinger
β βββ api/follow/route.ts -> Follow API(temporary)
β βββ api/announce/route.ts -> Boost(Announcement)
β βββ api/like/route.ts. -> Like API
β βββ api/posts/route.ts -> Writing API
β βββ api/timeline/route.ts -> Timeline API
β βββ users/[username]/
β β βββ statuses/[id]/route.ts -> Indivisual Post
β β βββ route.ts -> Acotr Information
β β βββ followers/route.ts -> Followers List
β β βββ following/route.ts -> Following List
β β βββ inbox/route.ts -> Inbox
β β βββ outbox/route.ts -> outbox
β βββ usersui/[username]/
β β βββ page.tsx -> Timeline UI
β β βββ _components/themes/
β β βββ themeex/ThemeexTheme.tsx -> Example Theme
β β βββ pinafore/PinaforeTheme.tsx -> Theme 1
β β βββ mastodon/MastodonTheme.tsx -> Theme 2
β β βββ minimal/MinimaltTheme.tsx -> Theme 3
β βββ layout.tsx, page.tsx, globals.css
β βββ favicon.ico
βββ lib/
β βββ theme.tsx -> Theme Provider
β βββ watchThemes.ts -> Check real-time theme changes
β βββ auth.ts -> Authentication, User Management
β βββ ap.ts -> Follow,Undo,Create,Likes,Announce
β βββ db.ts -> DB connection
βββ data/
β βββ keys/userIDs/ -> private.pem, public.pem(New)
β βββ keys/ -> private.pem, public.pem(legacy)
βββ data.sqlite -> Database(1/3)
βββ data.sqlite-wal -> Database(2/3)
βββ data.sqlite-shm -> Database(3/3)
βββ Caddyfile -> https
βββ instrumentation.ts -> Background Server
βββ package.json
π νλ‘μ νΈ μμ / Porject Start
π λͺ¨λ μ€μΉ / Module install
— λΉλ°λ²νΈ μνΈν / Password encryption
npm install bcrypt jsonwebtoken
npm install -D @types/bcrypt @types/jsonwebtoken
π DB λ§μ΄κ·Έλ μ΄μ / DB Migration
βοΈ κΈ°μ‘΄ Usersν
μ΄λΈμμ User1μ μ μ§νκ³ μ 보λ₯Ό μ
λ°μ΄νΈν©λλ€.
Retain User1 in the existing Users table and update its information.
βοΈ Users ν μ΄λΈ / User Table
— email,password_hash,email_verified,vaerification_token νλλ₯Ό μΆκ°ν©λλ€.
Add the email, password_hash, email_verified, and verification_token fields.
CREATE TABLE users (
username TEXT PRIMARY KEY,
display_name TEXT DEFAULT '',
summary TEXT DEFAULT '',
private_key TEXT,
public_key TEXT,
email TEXT UNIQUE,
password_hash TEXT,
email_verified INTEGER DEFAULT 0,
verification_token TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
βοΈ SQLITE
— ALTERλͺ
λ Ήμ΄λ‘λ UNIQUEλ₯Ό μΆκ°ν μ μμ΄μ ν
μ΄λΈμ μλ‘λ§λλλ€.
Since a UNIQUE constraint cannot be added using the ALTER command, I am creating a new table.
# - ν
μ΄λΈ μ κ· μμ± / Create New Table -
sqlite3 data.sqlite
# - κΈ°μ‘΄ ν
μ΄λΈ μμ (UNIQUEλ ALTERλ‘ μ
λ ₯ λΆκ°λ₯) -
# Delete existing table (UNIQUE constraint cannot be added via ALTER)
DROP TABLE IF EXISTS users;
# - ν
μ΄λΈ μμ± / Create Table --
CREATE TABLE users (
username TEXT PRIMARY KEY,
display_name TEXT DEFAULT '',
summary TEXT DEFAULT '',
private_key TEXT,
public_key TEXT,
email TEXT UNIQUE,
password_hash TEXT,
email_verified INTEGER DEFAULT 0,
verification_token TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
# - μ¬μ©μ μ
λ ₯(ν¨μ€μλ 1111) --
# User input (password 1111
INSERT INTO users (username, display_name, summary, email, password_hash, email_verified)
VALUES ('user1','user1','My Fediverse account on aloy-horizon','freelifemakers@gmail.com','$2b$10$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQoeG6Lruj3vjPGga31lW',1);
SELECT * FROM users;
— Private ,Public ν€ μμ± λ° DBμ
λ°μ΄νΈ(λλ κΈ°μ‘΄ ν€ νμ©)
Generate private and public keys and update the database (or utilize existing keys)
# ν€ μμ± / Key Generation
cd ~/myapp31
node -e "
const crypto = require('crypto');
const fs = require('fs');
const { privateKey, publicKey } = crypto.generateKeyPairSync('rsa', {
modulusLength: 2048,
publicKeyEncoding: {type: 'spki', format: 'pem'},
privateKeyEncoding: {type: 'pkcs8', format: 'pem'}
});
fs.writeFileSync('pri.pem', privateKey);
fs.writeFileSync('pub.pem', publicKey);
console.log('μμ±λ¨!');
console.log('private length:', privateKey.length);
console.log('public length:', publicKey.length);
"
# cat λͺ
λ Ήμ΄λ‘ ν€ νμΈν DBμ μ
λ°μ΄νΈ νκΈ°
Check the key using the `cat` command and update the database.
# DB Update / DB Update
UPDATE users SET
private_key = '-----BEGIN PRIVATE KEY-----',
public_key = '-----BEGIN PUBLIC KEY-----'
WHERE username = 'user1';
SELECT username, length(private_key) as pri_len, length(public_key) as pub_len FROM users;
— μ¬μ©μ λλ ν 리 λ§λ€κ³ ν€ νμΌ λ³΅μ¬(db.tsλ‘μ§μ΄λ λ§μΆκΈ° μν΄μ)
Create the user directory and copy the key file (to align with the db.ts logic).
# κΈ°μ‘΄νμΌμ μ§
# Keep existing files
# /myapp31/data/keys/
user1 % ls
private.pem public.pem
# μ κ·νμΌ
# New File
#/myapp31/data/keys/user1
user1 % ls
private.pem public.pem
1) νμκ°μ μ ν€λ data/keys/userid/private_key.pem or public_key.pem μΌλ‘ μ μ₯ λ¨.
π μ½λμΆκ° / Add code
βοΈ νμκ°μ
μ²λ¦¬λ λ‘κ·ΈμΈκ΄λ ¨ ν¨μμ
λλ€.
These are functions related to user registration and login processing.
— lib/auth.ts
... ...
export async function hashPassword(password: string) {
... ...
}
export async function verifyPassword(password: string, hash: string) {
... ...
}
export function createToken(username: string) {
... ...
}
export function verifyToken(token: string) {
... ...
}
export function getUser(username: string) {
... ...
}
export function getUserByEmail(email: string) {
... ...
}
// β
myapp31 - μ κ· μ μ μμ± (ν€λ κ°μ΄ μμ±!) / Create a new user (with keys!)
export function createUser(username: string, email: string, passwordHash: string, displayName: string = '') {
... ...
}
// β
myapp31 -κΈ°μ‘΄ DB -> νμΌλ‘ λ§μ΄κ·Έλ μ΄μ
! / Migrate existing DB keys to files!
export function migrateKeysToFiles() {
... ...
}
... ...
π μ½λ μμ / Code Modification
βοΈ lib/db.ts
// lib/db.ts
// keys ν΄λ μμ±! / Create keys folder!
const keysDir = path.join(process.cwd(), 'data/keys');
if (!fs.existsSync(keysDir)) {
fs.mkdirSync(keysDir, { recursive: true });
console.log('β
keys ν΄λ μμ±/ keys folder created:', keysDir);
}
... ...
db.exec(`
CREATE TABLE IF NOT EXISTS users (
username TEXT PRIMARY KEY,
display_name TEXT DEFAULT '',
summary TEXT DEFAULT '',
private_key TEXT,
public_key TEXT,
email TEXT UNIQUE,
password_hash TEXT,
email_verified INTEGER DEFAULT 0,
verification_token TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP);
`);
βοΈ publick key κ²μ¦λ‘μ§ λΆλΆ μμ
Modified the public key verification logic.
— lib/ap.ts
1) Publick Key κ²μ¦λ‘μ§ μΆκ°
Added public key verification logic.
2)getPublicKey, verifyHttpSignature, fetchActorPublicKey ν¨μ μΆκ°
Added getPublicKey, verifyHttpSignature, and fetchActorPublicKey functions.
function getPublicKey(username: string): string {
... ...
}
... ...
export async function verifyHttpSignature(
req: Request,
publicKeyPem: string
): Promise<boolean> { ... ...}
export async function fetchActorPublicKey(actorUrl: string): Promise<string> { ... ...
}
βοΈ λ΄ μλ²λ‘ νλ‘μ° μμ²μ Public Keyλ‘ κ²μ¦
Verify using the public key when a follow request is sent to my server.
— app/api/inbox/route.ts
export async function POST(req: Request, { params }: { params: Promise<{ username: string }> }) {
const { username } = await params;
let body: any;
let rawBody = '';
try {
// rawBodyλ₯Ό λ¨Όμ μ½κΈ° / Read rawBody first
rawBody = await req.text();
try {
body = JSON.parse(rawBody);
} catch {
console.log('β οΈ JSON νμ± μ€ν¨! λΉ λ°λ? / Failed to parse JSON! Empty body?', rawBody.slice(0,200));
return new Response('', { status: 202 });
}
// const body = await req.json();
console.log(`π© [${username}] INBOX:`, body.type, body.actor, body.id);
... ...
}
... ...
}
1) νλ‘μ°,μΈνλ‘μ°,κΈ λ°κΈ°,μ’μμ,λΆμ€νΈ λͺ¨λ publick keyλ‘ κ²μ¦
Follow, undo, receive posts, like, and boost(announce) actions are all verified using public keys.
2)DELETE μμ²μλ public key κ²μ¦ μν¨.
Public key verification is not performed for DELETE requests.
— DELETEμμ²μ λ³ΈμΈμ κ³μ μ΄ μμ λκ±Έ λ€λ₯Έμλ²μ μ리λ κΈ°λ₯μ.
The DELETE request serves to notify other servers that your account has been deleted.
— κ³μ μ΄ μμ λμΌλ―λ‘ public keyκ° μμ μ μμ.
The public key may be missing because the account has been deleted.
π ν μ€νΈ / Test
βοΈ λ§μ€ν λ μλ²μμ aloy-horizon-duckdns.orgλ₯Ό μΈνλ‘μ°νκ³ λ€μ νλ‘μ° νκΈ°

βοΈ ν€ κ²μ¦ λ‘κ·Έ νμΈνκΈ° / Checking Key Verification Logs

μν볡μ 8μ₯ 32μ / John 8:32
“κ·Έλ¦¬κ³ λν¬λ μ§λ¦¬λ₯Ό μκ² λ κ²μ΄λ©°, μ§λ¦¬κ° λν¬λ₯Ό μμ λ‘κ² ν κ²μ΄λ€.”
“Then you will know the truth ,and the truth will set you free”