-
Notifications
You must be signed in to change notification settings - Fork 0
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
0 parents
commit 61e05ce
Showing
15 changed files
with
404 additions
and
0 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,6 @@ | ||
root = true | ||
|
||
[*] | ||
end_of_line = lf | ||
indent_style = space | ||
indent_size = 2 |
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,2 @@ | ||
DATABASE_URL="mysql://user:password@localhost:3306/database_name" | ||
PRIVATE_KEY=aNywgbliberhHEre.AccEPtINRangeUTf8 |
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,3 @@ | ||
node_modules | ||
# Keep environment variables out of version control | ||
.env |
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,13 @@ | ||
# `nut` | ||
|
||
An authentication server. | ||
|
||
## How to use (standalone) | ||
|
||
- Copy `.env.example` to `.env` and fill it. | ||
- `bun start` | ||
- Reverse proxy the port, specified by `PORT` enviroment variable. `8080` by default | ||
|
||
## How to use (embedded) | ||
|
||
I should involve, no worries. |
Binary file not shown.
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,146 @@ | ||
import { PrismaClient } from "@prisma/client"; | ||
import { ErrorMessage } from "./types/shared"; | ||
import { saltcheck } from "./utils/cipher"; | ||
import { protocol, tokener, verify } from "./trusted"; | ||
|
||
const prisma = new PrismaClient(); | ||
|
||
const mgex = /^[a-zA-Z0-9_]{2,16}$/; | ||
|
||
type FutureResponse = Promise<Response>; | ||
type JSONAble = object | boolean | number | string | null | undefined; | ||
|
||
class JSONResponse extends Response { | ||
constructor(body: JSONAble, options?: ResponseInit) { | ||
super(JSON.stringify(body), { | ||
headers: { | ||
"Content-Type": "application/json", | ||
}, | ||
...options, | ||
}); | ||
} | ||
} | ||
|
||
class ErrorResponse extends JSONResponse { | ||
constructor(body: ErrorMessage, status: number, options?: ResponseInit) { | ||
super(body, { | ||
status, | ||
...options, | ||
}); | ||
} | ||
} | ||
|
||
module api { | ||
/* Auth */ | ||
/** | ||
* #### This route provide a user-token for user-related service | ||
* > Route accept `urlencoded form` body. `POST` only | ||
* | ||
* `username` is a minecraft username, lowercased | ||
* | ||
* `password` is user's password hashed in sha256 | ||
*/ | ||
export async function auth(req: Request): FutureResponse { | ||
const fd = await req.formData(); | ||
|
||
if (!fd.has("username") || !fd.has("password")) | ||
return new ErrorResponse( | ||
{ | ||
success: false, | ||
msg: "Missing body parameters", | ||
}, | ||
400 | ||
); | ||
const username = (fd.get("username") as string).toLowerCase(); | ||
if (!mgex.test(username)) | ||
return new ErrorResponse( | ||
{ | ||
success: false, | ||
msg: "Wrong username format?", | ||
}, | ||
400 | ||
); | ||
const fetch = await prisma.credential.findUnique({ | ||
where: { | ||
username: username, | ||
}, | ||
select: { | ||
password: true, | ||
realname: true, | ||
}, | ||
}); | ||
if (fetch) { | ||
const { password, realname } = fetch; | ||
if (saltcheck(fd.get("password") as string, password)) { | ||
const f = protocol.serialize(username); | ||
return new JSONResponse({ | ||
success: true, | ||
msg: "Authenticated as " + realname, | ||
token: tokener.sign(f), | ||
}); | ||
} else | ||
return new ErrorResponse( | ||
{ success: false, msg: "Password check failed!", code: "fpwd" }, | ||
401 | ||
); | ||
} else { | ||
return new ErrorResponse( | ||
{ success: false, msg: "User not found?", code: "nusr" }, | ||
404 | ||
); | ||
} | ||
} | ||
|
||
export async function auth_verify(req: Request) { | ||
const to = req.headers.get("Authorization")?.split(" "), | ||
ve = to && to[0] == "Bearer" && verify(to[1]); | ||
return ve | ||
? new JSONResponse({ | ||
success: true, | ||
msg: "Authenticated as " + ve.username, | ||
data: ve, | ||
}) | ||
: new ErrorResponse( | ||
{ | ||
success: false, | ||
msg: "Token invalid", | ||
}, | ||
401 | ||
); | ||
} | ||
} | ||
|
||
/* HTTP Flow */ | ||
|
||
class Resolver { | ||
readonly url: URL; | ||
readonly req: Request; | ||
constructor(req: Request) { | ||
this.req = req; | ||
this.url = new URL(req.url); | ||
} | ||
match(matcher: string) { | ||
const pathname = this.url.pathname; | ||
return pathname == matcher; | ||
} | ||
matchMethod(method: string, matcher: string) { | ||
return this.match(matcher) && this.req.method === method; | ||
} | ||
} | ||
|
||
Bun.serve({ | ||
port: 8080, | ||
fetch(req: Request) { | ||
return new Promise<Response>((ok, panic) => { | ||
const rs = new Resolver(req); | ||
if (rs.matchMethod("POST", "/api/auth")) | ||
api.auth(req).then(ok).catch(panic); | ||
else if (rs.match("/api/auth/verify")) | ||
api.auth_verify(req).then(ok).catch(panic); | ||
else if (rs.match("/")) ok(new Response("Home")); | ||
else ok(new Response("no?", { status: 404 })); | ||
}); | ||
}, | ||
}); | ||
|
||
export {}; |
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,24 @@ | ||
{ | ||
"name": "nut", | ||
"proto": 1, | ||
"version": "1.0.0", | ||
"description": "", | ||
"main": "index.js", | ||
"scripts": { | ||
"test": "echo \"Error: no test specified\" && exit 1" | ||
}, | ||
"keywords": [], | ||
"author": "", | ||
"license": "ISC", | ||
"devDependencies": { | ||
"@types/node": "^20.4.2", | ||
"bun-types": "^0.6.14", | ||
"prisma": "^5.0.0", | ||
"ts-node": "^10.9.1", | ||
"typescript": "^5.1.6" | ||
}, | ||
"dependencies": { | ||
"@prisma/client": "5.0.0", | ||
"serdebin": "^1.1.0" | ||
} | ||
} |
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,50 @@ | ||
// This is your Prisma schema file, | ||
// learn more about it in the docs: https://pris.ly/d/prisma-schema | ||
|
||
generator client { | ||
provider = "prisma-client-js" | ||
} | ||
|
||
datasource db { | ||
provider = "mysql" | ||
url = env("DATABASE_URL") | ||
} | ||
|
||
// Custom | ||
|
||
model Profile { | ||
id Int @id @default(autoincrement()) @db.UnsignedInt | ||
createdAt DateTime @default(now()) | ||
updatedAt DateTime @updatedAt | ||
// Profile info | ||
// Rel | ||
credential Credential @relation(fields: [cid], references: [id]) | ||
cid Int @unique @db.UnsignedInt | ||
} | ||
|
||
model Credential { | ||
id Int @id @default(autoincrement()) @db.UnsignedInt | ||
username String @unique @db.Char(16) | ||
realname String @db.Char(16) | ||
password String @default("no") | ||
ip String? | ||
lastlogin BigInt? @db.UnsignedBigInt | ||
w String @default("l") @db.Char(16) | ||
x Float @default(0) @db.Double | ||
y Float @default(0) @db.Double | ||
z Float @default(0) @db.Double | ||
v Float @default(0) @db.Double | ||
h Float @default(0) @db.Double | ||
regdate BigInt @default(0) @db.UnsignedBigInt | ||
regip String? @default("::1") @db.Char(39) | ||
email String? @db.VarChar(255) | ||
isLogged Boolean @default(false) | ||
hasSession Boolean @default(false) | ||
totp String? @db.VarChar(32) | ||
uuid String? @db.Char(32) | ||
Profile Profile? | ||
@@map(name: "am") | ||
} |
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,70 @@ | ||
/** | ||
* This interface allow allies in swarm who share the same private key | ||
* in order to verify enemy hash | ||
*/ | ||
|
||
import { build, u } from "serdebin/helper"; | ||
import { Deserialization, extract_str, makeFixed } from "serdebin"; | ||
import pkgjson from "./package.json"; | ||
import Engine from "./utils/token"; | ||
import { trimStartPad } from "./utils/bytewise"; | ||
|
||
const pk = process.env.PRIVATE_KEY, | ||
proto = pkgjson.proto; | ||
|
||
if (!pk) | ||
throw "Please run with PRIVATE_KEY enviroment variable, or add it into .env file"; | ||
const tokener = new Engine(pk); | ||
|
||
/** | ||
* To keep long number remain compact, binary is used. | ||
* | ||
* Unfortunaltely, you have to copy the size from serialize function to deserialize function. | ||
* There is no automation | ||
* | ||
* Example, current time in UNIX is 13 character, but under number bit level, it only cost 5 and 1/4 byte. | ||
*/ | ||
export module protocol { | ||
/** | ||
* swarmer have anility to grant the token. | ||
* | ||
* however the main job upon the OAuth server | ||
* @param username player's username | ||
*/ | ||
export function serialize(username: string) { | ||
return build( | ||
// bit-pad ofuscator | ||
false, | ||
// protocol version | ||
u(7, proto), | ||
// username | ||
makeFixed(extract_str(username), 16 * 8), | ||
// validUntil | ||
u(42, Date.now()) | ||
); | ||
} | ||
/** | ||
* once data is trustable, deserialize it | ||
* @param reader Data reader | ||
*/ | ||
export function deserialize(reader: Deserialization) { | ||
reader.b(); // left pad | ||
return { | ||
proto: reader.u(7), | ||
username: trimStartPad(reader.s(16)), | ||
expire: reader.u(42), | ||
}; | ||
} | ||
} | ||
|
||
/** | ||
* This quantum function may share across trusted NodeJS server | ||
* @param hash hash stored in header | ||
* @returns payloads | ||
*/ | ||
export function verify(hash: string) { | ||
const reader = tokener.verify(hash); | ||
return reader && protocol.deserialize(reader); | ||
} | ||
|
||
export { tokener }; |
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,25 @@ | ||
{ | ||
"compilerOptions": { | ||
// add Bun type definitions | ||
"types": ["bun-types"], | ||
|
||
// enable latest features | ||
"lib": ["esnext"], | ||
"module": "esnext", | ||
"target": "esnext", | ||
|
||
// if TS 5.x+ | ||
"moduleResolution": "bundler", | ||
// "allowImportingTsExtensions": true, | ||
"moduleDetection": "force", | ||
|
||
"jsx": "react-jsx", // support JSX | ||
"allowJs": true, // allow importing `.js` from `.ts` | ||
"esModuleInterop": true, // allow default imports for CommonJS modules | ||
|
||
// best practices | ||
"strict": true, | ||
"forceConsistentCasingInFileNames": true, | ||
"skipLibCheck": true | ||
} | ||
} |
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,8 @@ | ||
export type Message = { | ||
success: true; | ||
}; | ||
export type ErrorMessage = { | ||
success: false; | ||
msg: string; | ||
code?: string; | ||
}; |
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,16 @@ | ||
export function joinu8a(...arrays: Uint8Array[]) { | ||
let length = arrays.reduce((acc, arr) => acc + arr.length, 0); | ||
let mergedArray = new Uint8Array(length); | ||
let offset = 0; | ||
arrays.forEach((arr) => { | ||
mergedArray.set(arr, offset); | ||
offset += arr.length; | ||
}); | ||
return mergedArray.buffer; | ||
} | ||
|
||
export function trimStartPad(d: string) { | ||
var i = 0; | ||
while (d[i] == "\u0000" && i < d.length) i++; | ||
return d.slice(i); | ||
} |
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,8 @@ | ||
import { sha256 } from "./digest"; | ||
|
||
export function saltcheck(phPwd: string, hashd: string): boolean { | ||
// $SHA$salt$hash, where hash := sha256(sha256(password) . salt) | ||
// somehow this thing might work | ||
const [_, __, salt, salten] = hashd.split("$"); | ||
return sha256(phPwd + salt) === salten; | ||
} |
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,6 @@ | ||
export function sha256(i: string | Uint8Array | ArrayBuffer): string { | ||
// return crypto.createHash('sha256').update(str).digest('hex'); | ||
const hashr = new Bun.CryptoHasher("sha256") | ||
hashr.update(i) | ||
return hashr.digest("hex"); | ||
} |
Oops, something went wrong.