Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ PUBLIC_APP_STORE_EID_WALLET=""
PUBLIC_PLAY_STORE_EID_WALLET=""
NOTIFICATION_SHARED_SECRET=your-notification-secret-key

PUBLIC_ESIGNER_BASE_URL="http://localhost:3004"

DREAMSYNC_DATABASE_URL=postgresql://postgres:postgres@localhost:5432/dreamsync
VITE_DREAMSYNC_BASE_URL="http://localhost:8888"

Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/check-code.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,6 @@ jobs:
pnpm i

- name: Check Code
run: pnpm check
run: |
cp .env.example .env
pnpm check
48 changes: 48 additions & 0 deletions platforms/esigner-api/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{
"name": "esigner-api",
"version": "1.0.0",
"description": "eSigner Document Signing Platform API",
"main": "src/index.ts",
"scripts": {
"start": "ts-node src/index.ts",
"dev": "nodemon --exec ts-node src/index.ts",
"build": "tsc && cp -r src/web3adapter/mappings dist/web3adapter/",
"typeorm": "typeorm-ts-node-commonjs",
"migration:generate": "npm run typeorm migration:generate -- -d src/database/data-source.ts",
"migration:run": "npm run typeorm migration:run -- -d src/database/data-source.ts",
"migration:revert": "npm run typeorm migration:revert -- -d src/database/data-source.ts"
},
"dependencies": {
"axios": "^1.6.7",
"cors": "^2.8.5",
"dotenv": "^16.4.5",
"eventsource-polyfill": "^0.9.6",
"express": "^4.18.2",
"graphql-request": "^6.1.0",
"jsonwebtoken": "^9.0.2",
"multer": "^1.4.5-lts.1",
"pg": "^8.11.3",
"reflect-metadata": "^0.2.1",
"typeorm": "^0.3.24",
"uuid": "^9.0.1",
"signature-validator": "workspace:*",
"web3-adapter": "workspace:*"
},
"devDependencies": {
"@types/cors": "^2.8.17",
"@types/express": "^4.17.21",
"@types/jsonwebtoken": "^9.0.5",
"@types/multer": "^1.4.11",
"@types/node": "^20.11.24",
"@types/pg": "^8.11.2",
"@types/uuid": "^9.0.8",
"@typescript-eslint/eslint-plugin": "^7.0.1",
"@typescript-eslint/parser": "^7.0.1",
"eslint": "^8.56.0",
"nodemon": "^3.0.3",
"ts-node": "^10.9.2",
"typescript": "^5.3.3"
}
}


134 changes: 134 additions & 0 deletions platforms/esigner-api/src/controllers/AuthController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import { Request, Response } from "express";
import { v4 as uuidv4 } from "uuid";
import { UserService } from "../services/UserService";
import { EventEmitter } from "events";
import { signToken } from "../utils/jwt";
import { isVersionValid } from "../utils/version";
import { verifySignature } from "signature-validator";

const MIN_REQUIRED_VERSION = "0.4.0";

export class AuthController {
private userService: UserService;
private eventEmitter: EventEmitter;

constructor() {
this.userService = new UserService();
this.eventEmitter = new EventEmitter();
}

sseStream = async (req: Request, res: Response) => {
const { id } = req.params;

res.writeHead(200, {
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache",
Connection: "keep-alive",
"Access-Control-Allow-Origin": "*",
});

const handler = (data: any) => {
res.write(`data: ${JSON.stringify(data)}\n\n`);
};

this.eventEmitter.on(id, handler);

req.on("close", () => {
this.eventEmitter.off(id, handler);
res.end();
});

req.on("error", (error) => {
console.error("SSE Error:", error);
this.eventEmitter.off(id, handler);
res.end();
});
};

getOffer = async (req: Request, res: Response) => {
const url = new URL(
"/api/auth",
process.env.PUBLIC_ESIGNER_BASE_URL,
).toString();
const session = uuidv4();
const offer = `w3ds://auth?redirect=${url}&session=${session}&platform=esigner`;
res.json({ uri: offer });
};

login = async (req: Request, res: Response) => {
try {
const { ename, session, appVersion, signature } = req.body;

if (!ename) {
return res.status(400).json({ error: "ename is required" });
}

if (!session) {
return res.status(400).json({ error: "session is required" });
}

if (!signature) {
return res.status(400).json({ error: "signature is required" });
}

if (!appVersion || !isVersionValid(appVersion, MIN_REQUIRED_VERSION)) {
const errorMessage = {
error: true,
message: `Your eID Wallet app version is outdated. Please update to version ${MIN_REQUIRED_VERSION} or later.`,
type: "version_mismatch"
};
this.eventEmitter.emit(session, errorMessage);
return res.status(400).json({
error: "App version too old",
message: errorMessage.message
});
}

const registryBaseUrl = process.env.PUBLIC_REGISTRY_URL;
if (!registryBaseUrl) {
console.error("PUBLIC_REGISTRY_URL not configured");
return res.status(500).json({ error: "Server configuration error" });
}

const verificationResult = await verifySignature({
eName: ename,
signature: signature,
payload: session,
registryBaseUrl: registryBaseUrl,
});

if (!verificationResult.valid) {
console.error("Signature validation failed:", verificationResult.error);
return res.status(401).json({
error: "Invalid signature",
message: verificationResult.error
});
}

let user = await this.userService.findByEname(ename);

if (!user) {
throw new Error("User not found");
}

const token = signToken({ userId: user.id });

const data = {
user: {
id: user.id,
ename: user.ename,
isVerified: user.isVerified,
isPrivate: user.isPrivate,
},
token,
};
this.eventEmitter.emit(session, data);
res.status(200).json(data);
} catch (error) {
console.error("Error during login:", error);
res.status(500).json({ error: "Internal server error" });
}
};
}


Loading