Skip to content
This repository was archived by the owner on Apr 8, 2025. It is now read-only.

Commit c0dfb83

Browse files
committed
Merge branch 'feature/bslt-132' into develop
2 parents fea3f95 + 2b3a3be commit c0dfb83

File tree

11 files changed

+492
-109
lines changed

11 files changed

+492
-109
lines changed

.npmignore

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@
77

88
# === Archives and build-related files ===
99
*.tgz
10-
bun.lockb
11-
pnpm-lock.yaml
1210

1311
# === Bundler configuration files ===
1412
bundler.ts
@@ -26,7 +24,7 @@ jest.config.json
2624
node_modules/
2725
package-lock.json
2826
yarn.lock
29-
bun.lockb
27+
bun.lock
3028
pnpm-lock.yaml
3129

3230
# === Project-specific folders ===

README.md

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,16 @@
55
- [📦 Basalt-auth](#-basalt-auth)
66
- [📌 Table of contents](#-table-of-contents)
77
- [📝 Description](#-description)
8-
- [🌟 Documentation](#-documentation)
8+
- [🌟 Documentation](#-Documentation)
99
- [🔧 Installation](#-installation)
1010
- [⚖️ License](#️-license)
1111
- [📧 Contact](#-contact)
1212

1313
## 📝 Description
1414

15-
`Basalt-auth` is a library designed to create a secure and user-friendly authentication system.
15+
> ONLY FOR BUN
16+
17+
**Basalt-auth** is a library designed to create a secure and user-friendly authentication system.
1618
It is inspired by [JSON Web Tokens (JWT)](https://jwt.io/), with some differences.
1719

1820
One of the main differences of `Basalt-auth` is the key management: instead of using a global secret key, it generates a unique key pair for each token, thereby increasing the security of the individual session.
@@ -28,14 +30,8 @@ Only the public key and the token are stored, ensuring that interactions remain
2830

2931
## 🔧 Installation
3032

31-
NPM:
32-
```bash
33-
npm i @basalt-lab/basalt-auth
34-
```
35-
36-
PNPM:
3733
```bash
38-
pnpm i @basalt-lab/basalt-auth
34+
bun add @basalt-lab/basalt-auth
3935
```
4036

4137
## ⚖️ License

bun.lock

Lines changed: 367 additions & 0 deletions
Large diffs are not rendered by default.

bun.lockb

-64.2 KB
Binary file not shown.

bundler.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ const devDependencies = 'devDependencies' in pkg ? Object.keys(pkg.devDependenci
77
const peerDependencies = 'peerDependencies' in pkg ? Object.keys(pkg.peerDependencies ?? {}) : [];
88

99
await Bun.build({
10-
target: 'node',
10+
target: 'bun',
1111
external: [...dependencies, ...devDependencies, ...peerDependencies],
1212
root: './source',
1313
entrypoints: [

package.json

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
{
22
"name": "@basalt-lab/basalt-auth",
33
"version": "1.9.4",
4-
"description": "Authentication module for Basalt Framework",
4+
"description": "A sample authentication library each token has key pair (public and private)",
55
"keywords": [
6+
"bun",
67
"basalt-auth",
78
"basalt",
89
"auth",
@@ -39,18 +40,18 @@
3940
"lint": "eslint ./source"
4041
},
4142
"devDependencies": {
42-
"@eslint/js": "^9.17.0",
43-
"@stylistic/eslint-plugin": "^2.12.1",
44-
"@types/bun": "^1.1.15",
45-
"@types/node": "^22.10.5",
43+
"@eslint/js": "^9.18.0",
44+
"@stylistic/eslint-plugin": "^2.13.0",
45+
"@types/bun": "^1.2.0",
4646
"bun-plugin-dts": "^0.3.0",
47+
"eslint": "^9.18.0",
4748
"eslint-plugin-tsdoc": "^0.4.0",
48-
"eslint": "^9.17.0",
49-
"typescript-eslint": "^8.19.1",
50-
"typedoc": "^0.27.6"
49+
"globals": "^15.14.0",
50+
"typedoc": "^0.27.6",
51+
"typescript-eslint": "^8.21.0"
5152
},
5253
"peerDependencies": {
53-
"typescript": "^5.7.2"
54+
"typescript": "^5.7.3"
5455
},
5556
"changelog": {
5657
"types": {

source/core/basaltToken.ts

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { randomUUID, sign as sig, verify as ver } from 'crypto';
1+
import { randomUUIDv7 } from 'bun';
2+
import { sign as sig, verify as ver } from 'crypto';
23

34
import { BasaltError } from '#/error/basaltError';
45
import { GLOBAL_KEY_ERROR } from '#/error/key/globalKeyError';
@@ -90,8 +91,8 @@ function _buildSignature(header: string, payload: string, privateKey: string, pa
9091
*
9192
* @param token - The authentication token.
9293
*
93-
* @throws ({@link BasaltError}) If the token structure is invalid. ({@link GLOBAL_KEY_ERROR}.TOKEN_INVALID_STRUCTURE)
94-
* @throws ({@link BasaltError}) If the token header is invalid. ({@link GLOBAL_KEY_ERROR}.TOKEN_INVALID_HEADER)
94+
* @throws ({@link BasaltError}) If the token structure is invalid. ({@link GLOBAL_KEY_ERROR.TOKEN_INVALID_STRUCTURE})
95+
* @throws ({@link BasaltError}) If the token header is invalid. ({@link GLOBAL_KEY_ERROR.TOKEN_INVALID_HEADER})
9596
*
9697
* @returns The parsed header of the token. ({@link BasaltTokenHeader})
9798
*/
@@ -117,7 +118,7 @@ function getHeader(token: string): BasaltTokenHeader {
117118
*
118119
* @param token - The authentication token.
119120
*
120-
* @throws ({@link BasaltError}) If the token structure is invalid. ({@link GLOBAL_KEY_ERROR}.TOKEN_INVALID_STRUCTURE)
121+
* @throws ({@link BasaltError}) If the token structure is invalid. ({@link GLOBAL_KEY_ERROR.TOKEN_INVALID_STRUCTURE})
121122
*
122123
* @returns The UUID of the token.
123124
*/
@@ -130,7 +131,7 @@ function getTokenUuid(token: string): string {
130131
*
131132
* @param token - The authentication token.
132133
*
133-
* @throws ({@link BasaltError}) If the token structure is invalid. ({@link GLOBAL_KEY_ERROR}.TOKEN_INVALID_STRUCTURE)
134+
* @throws ({@link BasaltError}) If the token structure is invalid. ({@link GLOBAL_KEY_ERROR.TOKEN_INVALID_STRUCTURE})
134135
*
135136
* @returns The expiration date of the token.
136137
*/
@@ -143,7 +144,7 @@ function getExpirationDate(token: string): Date {
143144
*
144145
* @param token - The authentication token.
145146
*
146-
* @throws ({@link BasaltError}) If the token structure is invalid. ({@link GLOBAL_KEY_ERROR}.TOKEN_INVALID_STRUCTURE)
147+
* @throws ({@link BasaltError}) If the token structure is invalid. ({@link GLOBAL_KEY_ERROR.TOKEN_INVALID_STRUCTURE})
147148
*
148149
* @returns The intended audience of the token.
149150
*/
@@ -156,7 +157,7 @@ function getAudience(token: string): string {
156157
*
157158
* @param token - The authentication token.
158159
*
159-
* @throws ({@link BasaltError} If the token structure is invalid. ({@link GLOBAL_KEY_ERROR}.TOKEN_INVALID_STRUCTURE)
160+
* @throws ({@link BasaltError} If the token structure is invalid. ({@link GLOBAL_KEY_ERROR.TOKEN_INVALID_STRUCTURE})
160161
*
161162
* @returns The issuer of the token.
162163
*/
@@ -171,8 +172,8 @@ function getIssuer(token: string): string {
171172
*
172173
* @param token - The authentication token.
173174
*
174-
* @throws ({@link BasaltError}) If the token structure is invalid. ({@link GLOBAL_KEY_ERROR}.TOKEN_INVALID_STRUCTURE)
175-
* @throws ({@link BasaltError}) If the token payload is invalid. ({@link GLOBAL_KEY_ERROR}.TOKEN_INVALID_PAYLOAD)
175+
* @throws ({@link BasaltError}) If the token structure is invalid. ({@link GLOBAL_KEY_ERROR.TOKEN_INVALID_STRUCTURE})
176+
* @throws ({@link BasaltError}) If the token payload is invalid. ({@link GLOBAL_KEY_ERROR.TOKEN_INVALID_PAYLOAD})
176177
*
177178
* @returns The parsed payload of the token. ({@link T})
178179
*/
@@ -198,7 +199,7 @@ function getPayload<T extends object>(token: string): T {
198199
*
199200
* @param token - The authentication token.
200201
*
201-
* @throws ({@link BasaltError}) If the token structure is invalid. ({@link GLOBAL_KEY_ERROR}.TOKEN_INVALID_STRUCTURE)
202+
* @throws ({@link BasaltError}) If the token structure is invalid. ({@link GLOBAL_KEY_ERROR.TOKEN_INVALID_STRUCTURE})
202203
*
203204
* @returns True if the token has expired, false otherwise.
204205
*/
@@ -237,7 +238,7 @@ function sign<T extends object>(
237238
issuer = 'Basalt-Issuer',
238239
audience = 'Basalt-Audience'
239240
): BasaltTokenSignResult {
240-
const tokenUUid: string = randomUUID();
241+
const tokenUUid: string = randomUUIDv7();
241242
const keyPair: KeyPairED25519 = generateKeyPairED25519();
242243

243244
const headerStringify: string = _buildHeader(tokenUUid, expirationMs, issuer, audience);
@@ -257,9 +258,9 @@ function sign<T extends object>(
257258
* @param token - The authentication token to verify.
258259
* @param publicKey - The public key corresponding to the private key used to sign the token.
259260
*
260-
* @throws ({@link BasaltError}) If the token structure is invalid. ({@link GLOBAL_KEY_ERROR}.TOKEN_INVALID_STRUCTURE)
261-
* @throws ({@link BasaltError}) If the token has expired. ({@link GLOBAL_KEY_ERROR}.TOKEN_IS_EXPIRED)
262-
* @throws ({@link BasaltError}) If the token signature is invalid. ({@link GLOBAL_KEY_ERROR}.TOKEN_SIGNATURE_INVALID)
261+
* @throws ({@link BasaltError}) If the token structure is invalid. ({@link GLOBAL_KEY_ERROR.TOKEN_INVALID_STRUCTURE})
262+
* @throws ({@link BasaltError}) If the token has expired. ({@link GLOBAL_KEY_ERROR.TOKEN_IS_EXPIRED})
263+
* @throws ({@link BasaltError}) If the token signature is invalid. ({@link GLOBAL_KEY_ERROR.TOKEN_SIGNATURE_INVALID})
263264
*/
264265
function verify(token: string, publicKey: string): void {
265266
if (isExpired(token))

source/error/basaltError.ts

Lines changed: 56 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { randomUUID } from 'crypto';
1+
import { randomUUIDv7 } from 'bun';
22

33
/**
44
* Represents the options for the Basalt error.
@@ -7,7 +7,7 @@ export interface BasaltErrorOptions<T = unknown> {
77
/**
88
* The error key.
99
*/
10-
key?: [string, number] | undefined;
10+
key?: readonly [string, number] | undefined;
1111
/**
1212
* The cause of the error.
1313
*/
@@ -17,7 +17,7 @@ export interface BasaltErrorOptions<T = unknown> {
1717
/**
1818
* Basalt error class that extends the ({@link Error}) class and provides additional properties. (uuidError, date, code, fileName, line, column)
1919
*
20-
* @typeparam T - The type of the cause of the error.
20+
* @typeParam T - The type of the cause of the error.
2121
*
2222
* @example
2323
* The following example demonstrates how to throw a new instance of the Basalt error.
@@ -27,29 +27,33 @@ export interface BasaltErrorOptions<T = unknown> {
2727
* } catch (error) {
2828
* console.log(error instanceof BasaltError); // true
2929
* console.log(error instanceof Error); // true
30-
* // u can access to uuidError, date, code, fileName, line, column, message, name, stack, cause
30+
* // u can access to uuidError, date, code, fileName, line, column, message, name, stack, cause, toJSON
3131
* }
3232
* ```
3333
* @example
3434
* The following example demonstrates how to create a new instance of the Basalt error with provided type for the cause.
3535
* ```typescript
36+
* // { foo: 'bar' } is the type of the cause;
3637
* const basaltError: BasaltError<{ foo: 'bar' }> = new BasaltError({
3738
* key: 'error.unknown',
3839
* cause: {
3940
* foo: 'bar',
4041
* },
4142
* });
42-
* console.log(basaltError.cause); // { foo: 'bar' } if you make ctrl + space after cause. you will see the properties of the cause
43+
* console.log(basaltError.cause); // { foo: 'bar' }
4344
* ```
4445
*/
45-
export class BasaltError<T = unknown> extends Error {
46+
export class BasaltError<const T = unknown> extends Error {
47+
/**
48+
* The cause of the error. (if available)
49+
*/
4650
public override readonly cause: T | undefined;
4751

4852
/**
4953
* The unique identifier of the error.
5054
* This identifier is used to track the error in the logs.
5155
*/
52-
private readonly _uuidError: string = randomUUID();
56+
private readonly _uuid: string = randomUUIDv7();
5357

5458
/**
5559
* The date when the error was created.
@@ -86,57 +90,83 @@ export class BasaltError<T = unknown> extends Error {
8690
super.name = 'BasaltError';
8791
this.cause = basaltErrorOptions?.cause;
8892
this._code = basaltErrorOptions?.key?.[1] || 500;
89-
if (Error.captureStackTrace) {
90-
Error.captureStackTrace(this, this.constructor);
91-
const stackLine = this.stack?.split('\n')[1]?.trim();
92-
const match = stackLine?.match(/:(\d+):(\d+)\)$/);
93-
this._fileName = stackLine?.split('(')[1]?.split(':')[0] || '';
94-
if (match) {
95-
this._line = match[1] ? parseInt(match[1], 10) : 0;
96-
this._column = match[2] ? parseInt(match[2], 10) : 0;
97-
}
93+
const stackLine = this.stack?.split('\n')[1]?.trim();
94+
const match = stackLine?.match(/\(?(.+):(\d+):(\d+)\)?$/);
95+
if (match) {
96+
this._fileName = match[1] || '';
97+
this._line = parseInt(match[2], 10) || 0;
98+
this._column = parseInt(match[3], 10) || 0;
9899
}
99100
}
100101

101102
/**
102-
* Gets the unique identifier of the error.
103+
* The unique identifier of the error.
103104
*/
104-
public get uuidError(): string {
105-
return this._uuidError;
105+
public get uuid(): string {
106+
return this._uuid;
106107
}
107108

108109
/**
109-
* Gets the date when the error was created.
110+
* The date when the error was created.
110111
*/
111112
public get date(): Date {
112113
return this._date;
113114
}
114115

115116
/**
116-
* Gets the fileName where the error occurred (if available).
117+
* The error code. (HTTP status code)
118+
*/
119+
public get code(): number {
120+
return this._code;
121+
}
122+
123+
/**
124+
* The fileName where the error occurred (if available).
117125
*/
118126
public get fileName(): string {
119127
return this._fileName;
120128
}
121129

122130
/**
123-
* Gets the line number where the error occurred (if available).
131+
* The line number where the error occurred (if available).
124132
*/
125133
public get line(): number {
126134
return this._line;
127135
}
128136

129137
/**
130-
* Gets the column number where the error occurred (if available).
138+
* The column number where the error occurred (if available).
131139
*/
132140
public get column(): number {
133141
return this._column;
134142
}
135143

136144
/**
137-
* Gets the error code.
145+
* Converts the error object to a JSON object.
146+
*
147+
* @returns The error object as a JSON object.
138148
*/
139-
public get code(): number {
140-
return this._code;
149+
public toJSON(): {
150+
name: string;
151+
uuid: string;
152+
date: Date;
153+
message: string;
154+
code: number;
155+
cause: T | undefined;
156+
fileName: string;
157+
line: number;
158+
column: number;
159+
} {
160+
return {
161+
name: this.name,
162+
uuid: this._uuid,
163+
date: this._date,
164+
message: this.message,
165+
code: this._code,
166+
cause: this.cause,
167+
fileName: this._fileName,
168+
line: this._line,
169+
column: this._column
170+
};
141171
}
142172
}

source/error/key/globalKeyError.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/**
22
* Global error key is a list of errors in the global context.
33
*/
4-
export const GLOBAL_KEY_ERROR: Record<string, [string, number]> = {
4+
export const GLOBAL_KEY_ERROR = {
55
TOKEN_INVALID_STRUCTURE: ['error.basalt-auth.token_invalid_structure', 401],
66
TOKEN_INVALID_HEADER: ['error.basalt-auth.token_invalid_header', 401],
77
TOKEN_INVALID_PAYLOAD: ['error.basalt-auth.token_invalid_payload', 401],

source/tools/keyGenerator.util.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { generateKeyPairSync, randomUUID, type KeyPairSyncResult } from 'crypto';
1+
import { generateKeyPairSync, type KeyPairSyncResult } from 'crypto';
2+
import { randomUUIDv7 } from 'bun';
23

34
import type { KeyPairED25519 } from '#/types/data/keyPairED25519';
45

@@ -8,7 +9,7 @@ import type { KeyPairED25519 } from '#/types/data/keyPairED25519';
89
* @returns The generated key pair with the passphrase. ({@link KeyPairED25519})
910
*/
1011
export function generateKeyPairED25519(): KeyPairED25519 {
11-
const passphrase: string = randomUUID();
12+
const passphrase: string = randomUUIDv7();
1213
const keyPair: KeyPairSyncResult<string, string> = generateKeyPairSync(
1314
'ed25519',
1415
{

0 commit comments

Comments
 (0)