@cocreate/authenticate 1.12.1 → 1.12.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/automated.yml +1 -1
- package/CHANGELOG.md +7 -0
- package/README.md +100 -48
- package/package.json +17 -14
- package/release.config.js +1 -1
- package/src/index.js +107 -17
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
## [1.12.2](https://github.com/CoCreate-app/CoCreate-authenticate/compare/v1.12.1...v1.12.2) (2026-07-19)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* set default branch to main ([4fcc1e9](https://github.com/CoCreate-app/CoCreate-authenticate/commit/4fcc1e9ebaf17e97641d6c44ea7c99dec96e3c63))
|
|
7
|
+
|
|
1
8
|
## [1.12.1](https://github.com/CoCreate-app/CoCreate-authenticate/compare/v1.12.0...v1.12.1) (2026-07-17)
|
|
2
9
|
|
|
3
10
|
|
package/README.md
CHANGED
|
@@ -1,83 +1,135 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @cocreate/authentication
|
|
2
2
|
|
|
3
|
-
A
|
|
3
|
+
A high-performance, native RS256 JWT session management and authentication engine. Designed as a zero-dependency ESM singleton cache layer, this engine dynamically provisions 2048-bit RSA keypairs, signs non-opaque session tokens, syncs live connection statuses back to databases via CoCreate CRUD gateways, and runs fast local public-key signature verification to guard distributed nodes against forged requests.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-

|
|
7
|
-

|
|
8
|
-

|
|
5
|
+
---
|
|
9
6
|
|
|
10
|
-
|
|
7
|
+
## Table of Contents
|
|
11
8
|
|
|
12
|
-
|
|
9
|
+
* [Features](#features)
|
|
10
|
+
* [Installation](#installation)
|
|
11
|
+
* [Usage](#usage)
|
|
12
|
+
* [How it Works](#how-it-works)
|
|
13
|
+
* [API Reference](#api-reference)
|
|
14
|
+
* [How to Contribute](#how-to-contribute)
|
|
15
|
+
* [License](#license)
|
|
13
16
|
|
|
14
|
-
|
|
17
|
+
---
|
|
15
18
|
|
|
16
|
-
##
|
|
19
|
+
## Features
|
|
17
20
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
+
* **Zero-Dependency Native RS256:** Leverages Node's internal `node:crypto` subsystem to sign and verify JSON Web Tokens (JWT) using asymmetric cryptography without relying on massive external dependencies.
|
|
22
|
+
* **Ephemeral Key-Pair Lifecycles:** Automatically provisions 2048-bit RSA key pairs, assigns isolated tracker IDs (`kid`), and handles local cache purging when lifetimes expire.
|
|
23
|
+
* **Multi-Layered Hot Cache:** Accelerates performance by keeping active key pairs and client connections within rapid-access memory structures (`Map`), dropping signature evaluation latency to a minimum.
|
|
24
|
+
* **CRUD Gateway Synchronization:** Seamlessly broadcasts active user sessions and lifecycle states down to persistent target database collections via internal protocol events (`object.update`).
|
|
25
|
+
* **Fast Signature Guard:** Isolates signature validations from state persistence layers, checking incoming signatures against active in-memory keys to drop structural forgery attempts instantly before making database round trips.
|
|
21
26
|
|
|
22
|
-
|
|
23
|
-
<script src="https://cdn.cocreate.app/authenticate/latest/CoCreate-authenticate.min.css"></script>
|
|
24
|
-
```
|
|
27
|
+
---
|
|
25
28
|
|
|
26
|
-
##
|
|
29
|
+
## Installation
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npm install @cocreate/authentication
|
|
27
33
|
|
|
28
|
-
```shell
|
|
29
|
-
$ npm i @cocreate/authenticate
|
|
30
34
|
```
|
|
31
35
|
|
|
32
|
-
|
|
36
|
+
---
|
|
33
37
|
|
|
34
|
-
|
|
35
|
-
- [Announcements](#announcements)
|
|
36
|
-
- [Roadmap](#roadmap)
|
|
37
|
-
- [How to Contribute](#how-to-contribute)
|
|
38
|
-
- [About](#about)
|
|
39
|
-
- [License](#license)
|
|
38
|
+
## Usage
|
|
40
39
|
|
|
41
|
-
|
|
40
|
+
### Token Issuance (Session Generation)
|
|
42
41
|
|
|
43
|
-
|
|
42
|
+
Generate a cryptographically signed RS256 token for a successful client connection and synchronize the state into database layers:
|
|
44
43
|
|
|
45
|
-
|
|
44
|
+
```javascript
|
|
45
|
+
import auth from '@cocreate/authentication';
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
const sessionParams = {
|
|
48
|
+
organization_id: "64b9a32e18f21bc56789abcd",
|
|
49
|
+
user_id: "64b9a35f18f21bc5e9812456",
|
|
50
|
+
clientId: "client_ws_90210_alpha",
|
|
51
|
+
host: "app.cocreate.js"
|
|
52
|
+
};
|
|
48
53
|
|
|
49
|
-
|
|
54
|
+
// Creates/reuses keys, signs the JWT, and saves the session
|
|
55
|
+
const token = auth.encodeToken(
|
|
56
|
+
sessionParams.organization_id,
|
|
57
|
+
sessionParams.user_id,
|
|
58
|
+
sessionParams.clientId,
|
|
59
|
+
sessionParams.host
|
|
60
|
+
);
|
|
50
61
|
|
|
51
|
-
|
|
62
|
+
console.log("Generated JWT:", token);
|
|
52
63
|
|
|
53
|
-
|
|
64
|
+
```
|
|
54
65
|
|
|
55
|
-
|
|
66
|
+
### Token Verification & Decoding
|
|
56
67
|
|
|
57
|
-
|
|
68
|
+
Intercept incoming request channels, extract identity records, and catch forged signatures locally:
|
|
58
69
|
|
|
59
|
-
|
|
70
|
+
```javascript
|
|
71
|
+
import auth from '@cocreate/authentication';
|
|
60
72
|
|
|
61
|
-
|
|
73
|
+
const inboundToken = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6...";
|
|
74
|
+
const context = {
|
|
75
|
+
organization_id: "64b9a32e18f21bc56789abcd",
|
|
76
|
+
clientId: "client_ws_90210_alpha",
|
|
77
|
+
host: "app.cocreate.js"
|
|
78
|
+
};
|
|
62
79
|
|
|
63
|
-
|
|
80
|
+
// Verifies integrity against local keys and falls back to structural DB records if required
|
|
81
|
+
const session = await auth.decodeToken(
|
|
82
|
+
inboundToken,
|
|
83
|
+
context.organization_id,
|
|
84
|
+
context.clientId,
|
|
85
|
+
context.host
|
|
86
|
+
);
|
|
64
87
|
|
|
65
|
-
|
|
88
|
+
if (!session.user_id) {
|
|
89
|
+
console.log("Authentication Failed: Session missing, expired, or signature forged.");
|
|
90
|
+
} else {
|
|
91
|
+
console.log(`Authenticated User: ${session.user_id}, Expires at: ${session.expires}`);
|
|
92
|
+
}
|
|
66
93
|
|
|
67
|
-
|
|
94
|
+
```
|
|
68
95
|
|
|
69
|
-
|
|
96
|
+
---
|
|
70
97
|
|
|
71
|
-
|
|
98
|
+
## How it Works
|
|
72
99
|
|
|
73
|
-
|
|
100
|
+
1. **Lifecycle Rotation & Key Selection:** When `encodeToken` runs, the engine checks its active in-memory cache map. It automatically prunes expired keys and searches for a valid, unexpired asymmetric pair. If none are found, it triggers a 2048-bit RSA generation run.
|
|
101
|
+
2. **Asymmetric Envelope Signing:** It constructs standard JSON Web Token blocks (Header with `kid` + Payload with `user_id` and timestamps), serializes them into Base64URL string footprints, and signs the unified buffer natively via an asymmetric SHA-256 algorithm.
|
|
102
|
+
3. **Persisted State Bridging:** Once the token is assembled, the engine logs the structure into local memory slots and issues asynchronous events (`object.update`) down to central arrays to keep database records aligned with client connection parameters.
|
|
103
|
+
4. **Signature Pre-Screening:** During decoding checks (`decodeToken`), the engine parses the incoming header instantly to read the key identification string (`kid`). If that key is cached locally, it executes an direct crypto check (`verifySignature`). Forged payloads are intercepted and dropped right here, skipping down-stream infrastructure operations.
|
|
104
|
+
5. **State Invalidation & Synchronization:** If local signatures check out but matching memory records are absent (e.g., when scaled out across distributed processes), it sends query lookups down to persistent storage (`read`). If the token is verified to be expired or invalid, the engine clears all local tracking points and flags the database to nullify the state.
|
|
74
105
|
|
|
75
|
-
|
|
106
|
+
---
|
|
76
107
|
|
|
77
|
-
|
|
108
|
+
## API Reference
|
|
109
|
+
|
|
110
|
+
### Default Manifest Exports
|
|
78
111
|
|
|
79
|
-
|
|
112
|
+
| Method Selector | Payload Input Structure | Returns | Role |
|
|
113
|
+
| --- | --- | --- | --- |
|
|
114
|
+
| **`createKeyPair()`** | *None* | `Object` | Generates a new secure 2048-bit RSA key pair object with automatic expiration tracking. |
|
|
115
|
+
| **`deleteKeyPair(keyPair)`** | `keyPair: Object` | `Boolean` | Explicitly removes targeted cryptographic configurations from the local tracking cache. |
|
|
116
|
+
| **`encodeToken(orgId, userId, clientId, host)`** | `String, String, String, String` | `String` | Generates a zero-dependency RS256 token, assigns local session parameters, and pushes changes to databases. |
|
|
117
|
+
| **`decodeToken(token, orgId, clientId, host)`** | `String, String, String, String` | `Object` | Decodes signatures, catches structural forgery attempts instantly, and returns verified identity states. |
|
|
118
|
+
| **`read(orgId, clientId, host)`** | `String, String, String` | `Promise<Object|null>` | Reaches out into data backends via internal CRUD pathways to retrieve active persistent session payloads. |
|
|
80
119
|
|
|
81
|
-
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## How to Contribute
|
|
123
|
+
|
|
124
|
+
We encourage contribution to our libraries (you might even score some nifty swag), please see our [CONTRIBUTING.md](https://github.com/CoCreate-app/CoCreate-authentication/blob/master/CONTRIBUTING.md) guide for details. If you encounter any bugs or wish to make feature requests, please submit an issue on our [GitHub Issues](https://github.com/CoCreate-app/CoCreate-authentication/issues) tracker. We want this library to be community-driven, and CoCreate led. We need your help to realize this goal.
|
|
125
|
+
|
|
126
|
+
For broader system configurations and API guides, please visit our [CoCreate Authentication Documentation](https://cocreatejs.com/docs/authentication).
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## License
|
|
131
|
+
|
|
132
|
+
This software is dual-licensed under the GNU Affero General Public License version 3 (AGPLv3) and a commercial license.
|
|
82
133
|
|
|
83
|
-
|
|
134
|
+
* **Open Source Use:** For open-source projects and non-commercial use, this software is available under the AGPLv3. For the full license text, see the LICENSE file.
|
|
135
|
+
* **Commercial Use:** For-profit companies and individuals intending to use this software for commercial purposes must obtain a commercial license. The commercial license is available when you sign up for an API key on our website.
|
package/package.json
CHANGED
|
@@ -1,16 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cocreate/authenticate",
|
|
3
|
-
"version": "1.12.
|
|
4
|
-
"description": "A
|
|
3
|
+
"version": "1.12.3",
|
|
4
|
+
"description": "A high-performance, zero-dependency ESM session management and authentication engine using native RS256 asymmetric cryptography, ephemeral 2048-bit RSA keypair rotation, and reactive database state synchronization.",
|
|
5
5
|
"keywords": [
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
"
|
|
6
|
+
"cocreate",
|
|
7
|
+
"cocreate-app",
|
|
8
|
+
"authentication",
|
|
9
|
+
"auth",
|
|
10
|
+
"jwt",
|
|
11
|
+
"rs256",
|
|
12
|
+
"crypto",
|
|
13
|
+
"session-management",
|
|
14
|
+
"rsa-2048",
|
|
15
|
+
"multi-tenant",
|
|
16
|
+
"token-verification",
|
|
17
|
+
"zero-dependency",
|
|
18
|
+
"esm",
|
|
19
|
+
"security"
|
|
14
20
|
],
|
|
15
21
|
"scripts": {
|
|
16
22
|
"start": "webpack --config webpack.config.js --watch",
|
|
@@ -30,8 +36,5 @@
|
|
|
30
36
|
},
|
|
31
37
|
"type": "module",
|
|
32
38
|
"main": "./src/index.js",
|
|
33
|
-
"homepage": "https://cocreate.app/docs/authenticate"
|
|
34
|
-
"dependencies": {
|
|
35
|
-
"jsonwebtoken": "^9.0.0"
|
|
36
|
-
}
|
|
39
|
+
"homepage": "https://cocreate.app/docs/authenticate"
|
|
37
40
|
}
|
package/release.config.js
CHANGED
package/src/index.js
CHANGED
|
@@ -20,29 +20,34 @@
|
|
|
20
20
|
// you must obtain a commercial license from CoCreate LLC.
|
|
21
21
|
// For details, visit <https://cocreate.app/licenses/> or contact us at sales@cocreate.app.
|
|
22
22
|
|
|
23
|
-
import crypto from 'crypto';
|
|
24
|
-
import jwt from 'jsonwebtoken';
|
|
23
|
+
import crypto from 'node:crypto';
|
|
25
24
|
import crud from '@cocreate/crud-server';
|
|
26
25
|
|
|
27
|
-
// ==========================================
|
|
28
|
-
// Module-Level Sandbox State (ESM Singleton Container)
|
|
29
|
-
// ==========================================
|
|
30
26
|
const tokenExpiration = 60; // Token expiration in minutes
|
|
31
|
-
const keyPairs = new Map();
|
|
32
|
-
const sessions = new Map();
|
|
27
|
+
const keyPairs = new Map(); // Active public/private cryptographic key pairs
|
|
28
|
+
const sessions = new Map(); // In-memory hot session storage cache
|
|
33
29
|
|
|
34
30
|
/**
|
|
35
31
|
* Generates a new RSA key pair dynamically and tracks its expiration lifecycle.
|
|
36
32
|
* @returns {Object} Newly created key pair configuration
|
|
37
33
|
*/
|
|
38
34
|
function createKeyPair() {
|
|
35
|
+
// Generate secure 2048-bit RSA keys natively using Node's built-in Crypto module
|
|
39
36
|
const { privateKey, publicKey } = crypto.generateKeyPairSync('rsa', {
|
|
40
37
|
modulusLength: 2048,
|
|
38
|
+
publicKeyEncoding: {
|
|
39
|
+
type: 'spki',
|
|
40
|
+
format: 'pem'
|
|
41
|
+
},
|
|
42
|
+
privateKeyEncoding: {
|
|
43
|
+
type: 'pkcs8',
|
|
44
|
+
format: 'pem'
|
|
45
|
+
}
|
|
41
46
|
});
|
|
42
47
|
|
|
43
|
-
const created =
|
|
48
|
+
const created = Date.now();
|
|
44
49
|
const keyPair = {
|
|
45
|
-
_id: crypto.randomUUID
|
|
50
|
+
_id: crypto.randomUUID(),
|
|
46
51
|
privateKey,
|
|
47
52
|
publicKey,
|
|
48
53
|
created,
|
|
@@ -53,6 +58,7 @@ function createKeyPair() {
|
|
|
53
58
|
return keyPair;
|
|
54
59
|
}
|
|
55
60
|
|
|
61
|
+
|
|
56
62
|
/**
|
|
57
63
|
* Deletes an expired or revoked RSA key pair from local tracking.
|
|
58
64
|
* @param {Object} keyPair - The target key pair to remove
|
|
@@ -63,15 +69,35 @@ function deleteKeyPair(keyPair) {
|
|
|
63
69
|
}
|
|
64
70
|
}
|
|
65
71
|
|
|
72
|
+
/**
|
|
73
|
+
* Helper to encode JSON payloads safely as Base64URL string representations
|
|
74
|
+
*/
|
|
75
|
+
function base64urlEncode(obj) {
|
|
76
|
+
return Buffer.from(JSON.stringify(obj)).toString('base64url');
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Helper to decode Base64URL strings safely back to JSON objects
|
|
81
|
+
*/
|
|
82
|
+
function base64urlDecode(str) {
|
|
83
|
+
try {
|
|
84
|
+
return JSON.parse(Buffer.from(str, 'base64url').toString('utf8'));
|
|
85
|
+
} catch {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
|
|
66
91
|
/**
|
|
67
92
|
* Creates and signs a new token utilizing the latest available RSA key pair.
|
|
68
|
-
*
|
|
93
|
+
* Generates a zero-dependency RS256 JWT natively.
|
|
69
94
|
*/
|
|
70
95
|
function encodeToken(organization_id, user_id, clientId, host) {
|
|
71
96
|
let keyPair = null;
|
|
72
|
-
const currentTime =
|
|
97
|
+
const currentTime = Date.now();
|
|
73
98
|
|
|
74
|
-
|
|
99
|
+
// Prune expired keypairs and locate/reuse a valid active one
|
|
100
|
+
for (const [key, value] of keyPairs) {
|
|
75
101
|
if (currentTime > value.expires) {
|
|
76
102
|
deleteKeyPair(value);
|
|
77
103
|
} else {
|
|
@@ -83,10 +109,33 @@ function encodeToken(organization_id, user_id, clientId, host) {
|
|
|
83
109
|
keyPair = createKeyPair();
|
|
84
110
|
}
|
|
85
111
|
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
112
|
+
const iat = Math.floor(currentTime / 1000);
|
|
113
|
+
const exp = iat + (tokenExpiration * 60);
|
|
114
|
+
|
|
115
|
+
// Standard JWT Segments (Header containing Key ID + Alg, and standard Payload)
|
|
116
|
+
const header = {
|
|
117
|
+
alg: 'RS256',
|
|
118
|
+
typ: 'JWT',
|
|
119
|
+
kid: keyPair._id
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const payload = {
|
|
123
|
+
user_id,
|
|
124
|
+
clientId,
|
|
125
|
+
iat,
|
|
126
|
+
exp
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const encodedHeader = base64urlEncode(header);
|
|
130
|
+
const encodedPayload = base64urlEncode(payload);
|
|
131
|
+
const signatureInput = `${encodedHeader}.${encodedPayload}`;
|
|
132
|
+
|
|
133
|
+
// Cryptographically sign the envelope natively using SHA-256 and RSA Private Key
|
|
134
|
+
const signer = crypto.createSign('RSA-SHA256');
|
|
135
|
+
signer.update(signatureInput);
|
|
136
|
+
const signature = signer.sign(keyPair.privateKey, 'base64url');
|
|
137
|
+
|
|
138
|
+
const token = `${signatureInput}.${signature}`;
|
|
90
139
|
|
|
91
140
|
const session = {
|
|
92
141
|
user_id,
|
|
@@ -114,6 +163,7 @@ function encodeToken(organization_id, user_id, clientId, host) {
|
|
|
114
163
|
return token;
|
|
115
164
|
}
|
|
116
165
|
|
|
166
|
+
|
|
117
167
|
/**
|
|
118
168
|
* Reads, parses, and validates the configuration and active state of client sessions.
|
|
119
169
|
*/
|
|
@@ -137,13 +187,53 @@ async function read(organization_id, clientId, host) {
|
|
|
137
187
|
return null;
|
|
138
188
|
}
|
|
139
189
|
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Verifies the integrity of a cryptographic token envelope natively.
|
|
193
|
+
*/
|
|
194
|
+
function verifySignature(token, publicKey) {
|
|
195
|
+
try {
|
|
196
|
+
const parts = token.split('.');
|
|
197
|
+
if (parts.length !== 3) return false;
|
|
198
|
+
|
|
199
|
+
const [header, payload, signature] = parts;
|
|
200
|
+
const verifier = crypto.createVerify('RSA-SHA256');
|
|
201
|
+
verifier.update(`${header}.${payload}`);
|
|
202
|
+
|
|
203
|
+
return verifier.verify(publicKey, signature, 'base64url');
|
|
204
|
+
} catch {
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
140
209
|
/**
|
|
141
210
|
* Verifies and decodes an incoming session token, resolving the associated user identifier.
|
|
211
|
+
* Incorporates local RSA public-key validation before hitting session layers.
|
|
142
212
|
*/
|
|
143
213
|
async function decodeToken(token, organization_id, clientId, host) {
|
|
144
214
|
if (!token) return {};
|
|
145
215
|
|
|
146
|
-
const
|
|
216
|
+
const parts = token.split('.');
|
|
217
|
+
if (parts.length !== 3) return {};
|
|
218
|
+
|
|
219
|
+
const [headerB64, payloadB64, signatureB64] = parts;
|
|
220
|
+
const header = base64urlDecode(headerB64);
|
|
221
|
+
const payload = base64urlDecode(payloadB64);
|
|
222
|
+
|
|
223
|
+
if (!header || !payload) return {};
|
|
224
|
+
|
|
225
|
+
// 1. Core Security Check: Identify active signing keys using Key ID (kid)
|
|
226
|
+
const keyPair = keyPairs.get(header.kid);
|
|
227
|
+
if (keyPair) {
|
|
228
|
+
// Reject forged signatures instantly without proceeding to database/session checks
|
|
229
|
+
const isValid = verifySignature(token, keyPair.publicKey);
|
|
230
|
+
if (!isValid) {
|
|
231
|
+
console.warn(`[SECURITY WARNING] Blocked forged token attempt on clientId: ${clientId}`);
|
|
232
|
+
return {};
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const currentTime = Date.now();
|
|
147
237
|
let session = sessions.get(clientId);
|
|
148
238
|
|
|
149
239
|
if (!session || session.token !== token) {
|