-
Notifications
You must be signed in to change notification settings - Fork 26
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
262 additions
and
40 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,194 @@ | ||
{ | ||
"info": { | ||
"_postman_id": "d4058f10-7e8c-418f-8f4f-e740771b6e24", | ||
"name": "Two-Factor Auth", | ||
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", | ||
"_exporter_id": "14791724" | ||
}, | ||
"item": [ | ||
{ | ||
"name": "Disable 2FA", | ||
"request": { | ||
"method": "POST", | ||
"header": [], | ||
"body": { | ||
"mode": "raw", | ||
"raw": "{\r\n \"user_id\": \"9d3a5a8a-5d53-4d13-8cde-e51b91a09f9a\"\r\n}", | ||
"options": { | ||
"raw": { | ||
"language": "json" | ||
} | ||
} | ||
}, | ||
"url": { | ||
"raw": "http:https://localhost:8000/api/auth/otp/disable", | ||
"protocol": "http", | ||
"host": [ | ||
"localhost" | ||
], | ||
"port": "8000", | ||
"path": [ | ||
"api", | ||
"auth", | ||
"otp", | ||
"disable" | ||
] | ||
} | ||
}, | ||
"response": [] | ||
}, | ||
{ | ||
"name": "Verify OTP", | ||
"request": { | ||
"method": "POST", | ||
"header": [], | ||
"body": { | ||
"mode": "raw", | ||
"raw": "{\r\n \"user_id\": \"9d3a5a8a-5d53-4d13-8cde-e51b91a09f9a\",\r\n \"token\": \"296294\"\r\n}", | ||
"options": { | ||
"raw": { | ||
"language": "json" | ||
} | ||
} | ||
}, | ||
"url": { | ||
"raw": "http:https://localhost:8000/api/auth/otp/verify", | ||
"protocol": "http", | ||
"host": [ | ||
"localhost" | ||
], | ||
"port": "8000", | ||
"path": [ | ||
"api", | ||
"auth", | ||
"otp", | ||
"verify" | ||
] | ||
} | ||
}, | ||
"response": [] | ||
}, | ||
{ | ||
"name": "Generate OTP", | ||
"request": { | ||
"method": "POST", | ||
"header": [], | ||
"body": { | ||
"mode": "raw", | ||
"raw": "{\r\n \"user_id\": \"9d3a5a8a-5d53-4d13-8cde-e51b91a09f9a\",\r\n \"email\":\"[email protected]\"\r\n}", | ||
"options": { | ||
"raw": { | ||
"language": "json" | ||
} | ||
} | ||
}, | ||
"url": { | ||
"raw": "http:https://localhost:8000/api/auth/otp/generate", | ||
"protocol": "http", | ||
"host": [ | ||
"localhost" | ||
], | ||
"port": "8000", | ||
"path": [ | ||
"api", | ||
"auth", | ||
"otp", | ||
"generate" | ||
] | ||
} | ||
}, | ||
"response": [] | ||
}, | ||
{ | ||
"name": "Validate OTP", | ||
"request": { | ||
"method": "POST", | ||
"header": [], | ||
"body": { | ||
"mode": "raw", | ||
"raw": "{\r\n \"user_id\": \"9d3a5a8a-5d53-4d13-8cde-e51b91a09f9a\",\r\n \"token\": \"235485\"\r\n}", | ||
"options": { | ||
"raw": { | ||
"language": "json" | ||
} | ||
} | ||
}, | ||
"url": { | ||
"raw": "http:https://localhost:8000/api/auth/otp/validate", | ||
"protocol": "http", | ||
"host": [ | ||
"localhost" | ||
], | ||
"port": "8000", | ||
"path": [ | ||
"api", | ||
"auth", | ||
"otp", | ||
"validate" | ||
] | ||
} | ||
}, | ||
"response": [] | ||
}, | ||
{ | ||
"name": "Register User", | ||
"request": { | ||
"method": "POST", | ||
"header": [], | ||
"body": { | ||
"mode": "raw", | ||
"raw": "{\r\n \"email\": \"[email protected]\",\r\n \"name\": \"Jane\",\r\n \"password\": \"password123\"\r\n}", | ||
"options": { | ||
"raw": { | ||
"language": "json" | ||
} | ||
} | ||
}, | ||
"url": { | ||
"raw": "http:https://localhost:8000/api/auth/register", | ||
"protocol": "http", | ||
"host": [ | ||
"localhost" | ||
], | ||
"port": "8000", | ||
"path": [ | ||
"api", | ||
"auth", | ||
"register" | ||
] | ||
} | ||
}, | ||
"response": [] | ||
}, | ||
{ | ||
"name": "Login User", | ||
"request": { | ||
"method": "POST", | ||
"header": [], | ||
"body": { | ||
"mode": "raw", | ||
"raw": "{\r\n \"email\": \"[email protected]\",\r\n \"password\": \"password123\"\r\n}", | ||
"options": { | ||
"raw": { | ||
"language": "json" | ||
} | ||
} | ||
}, | ||
"url": { | ||
"raw": "http:https://localhost:8000/api/auth/login", | ||
"protocol": "http", | ||
"host": [ | ||
"localhost" | ||
], | ||
"port": "8000", | ||
"path": [ | ||
"api", | ||
"auth", | ||
"login" | ||
] | ||
} | ||
}, | ||
"response": [] | ||
} | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,7 +2,8 @@ import crypto from "crypto"; | |
import { Prisma } from "@prisma/client"; | ||
import { Request, Response, NextFunction } from "express"; | ||
import { prisma } from "../server"; | ||
import speakeasy from "speakeasy"; | ||
import * as OTPAuth from "otpauth"; | ||
import { encode } from "hi-base32"; | ||
|
||
const RegisterUser = async ( | ||
req: Request, | ||
|
@@ -70,27 +71,48 @@ const LoginUser = async (req: Request, res: Response, next: NextFunction) => { | |
} | ||
}; | ||
|
||
const generateRandomBase32 = () => { | ||
const buffer = crypto.randomBytes(15); | ||
const base32 = encode(buffer).replace(/=/g, "").substring(0, 24); | ||
return base32; | ||
}; | ||
|
||
const GenerateOTP = async (req: Request, res: Response) => { | ||
try { | ||
const { user_id } = req.body; | ||
const { ascii, hex, base32, otpauth_url } = speakeasy.generateSecret({ | ||
|
||
const user = await prisma.user.findUnique({ where: { id: user_id } }); | ||
|
||
if (!user) { | ||
return res.status(404).json({ | ||
status: "fail", | ||
message: "No user with that email exists", | ||
}); | ||
} | ||
|
||
const base32_secret = generateRandomBase32(); | ||
|
||
let totp = new OTPAuth.TOTP({ | ||
issuer: "codevoweb.com", | ||
name: "[email protected]", | ||
length: 15, | ||
label: "CodevoWeb", | ||
algorithm: "SHA1", | ||
digits: 6, | ||
period: 15, | ||
secret: base32_secret, | ||
}); | ||
|
||
let otpauth_url = totp.toString(); | ||
|
||
await prisma.user.update({ | ||
where: { id: user_id }, | ||
data: { | ||
otp_ascii: ascii, | ||
otp_auth_url: otpauth_url, | ||
otp_base32: base32, | ||
otp_hex: hex, | ||
otp_base32: base32_secret, | ||
}, | ||
}); | ||
|
||
res.status(200).json({ | ||
base32, | ||
base32: base32_secret, | ||
otpauth_url, | ||
}); | ||
} catch (error) { | ||
|
@@ -114,13 +136,18 @@ const VerifyOTP = async (req: Request, res: Response) => { | |
}); | ||
} | ||
|
||
const verified = speakeasy.totp.verify({ | ||
let totp = new OTPAuth.TOTP({ | ||
issuer: "codevoweb.com", | ||
label: "CodevoWeb", | ||
algorithm: "SHA1", | ||
digits: 6, | ||
period: 15, | ||
secret: user.otp_base32!, | ||
encoding: "base32", | ||
token, | ||
}); | ||
|
||
if (!verified) { | ||
let delta = totp.validate({ token }); | ||
|
||
if (delta === null) { | ||
return res.status(401).json({ | ||
status: "fail", | ||
message, | ||
|
@@ -164,15 +191,18 @@ const ValidateOTP = async (req: Request, res: Response) => { | |
message, | ||
}); | ||
} | ||
|
||
const validToken = speakeasy.totp.verify({ | ||
secret: user?.otp_base32!, | ||
encoding: "base32", | ||
token, | ||
window: 1, | ||
let totp = new OTPAuth.TOTP({ | ||
issuer: "codevoweb.com", | ||
label: "CodevoWeb", | ||
algorithm: "SHA1", | ||
digits: 6, | ||
period: 15, | ||
secret: user.otp_base32!, | ||
}); | ||
|
||
if (!validToken) { | ||
let delta = totp.validate({ token, window: 1 }); | ||
|
||
if (delta === null) { | ||
return res.status(401).json({ | ||
status: "fail", | ||
message, | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file not shown.
Oops, something went wrong.