crypta-client 0.1.1

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/.prettierrc ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "singleQuote": true,
3
+ "trailingComma": "none",
4
+ "semi": false,
5
+ "tabWidth": 2
6
+ }
package/.sequelizerc ADDED
@@ -0,0 +1,8 @@
1
+ const path = require('path')
2
+
3
+ module.exports = {
4
+ config: path.resolve('config', 'config.js'),
5
+ 'models-path': path.resolve('db', 'models'),
6
+ 'seeders-path': path.resolve('db', 'seeders'),
7
+ 'migrations-path': path.resolve('db', 'migrations')
8
+ }
package/README.md ADDED
@@ -0,0 +1,226 @@
1
+ # crypta-client
2
+
3
+ [![npm version](https://img.shields.io/npm/v/crypta-client.svg)](https://www.npmjs.com/package/crypta-client)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+
6
+ A Node.js client library for interacting with the Crypta Secret Manager. Securely manage and access secrets using service account authentication with JWT.
7
+
8
+ ## Features
9
+
10
+ - 🔐 **Secure Authentication** - Service account-based authentication using JWT
11
+ - 🔄 **Automatic Token Management** - Built-in token caching and refresh
12
+ - 🚀 **Simple API** - Easy-to-use interface for secret retrieval
13
+ - ⚡ **Lightweight** - Minimal dependencies
14
+ - 🛡️ **Error Handling** - Comprehensive error handling and custom error types
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ npm install crypta-client
20
+ ```
21
+
22
+ ## Quick Start
23
+
24
+ ```javascript
25
+ const { CryptaClient } = require('crypta-client')
26
+
27
+ // Initialize the client
28
+ const client = new CryptaClient({
29
+ baseUrl: 'https://your-crypta-instance.com',
30
+ clientId: 'your-service-account-client-id',
31
+ privateKeyPem: `-----BEGIN PRIVATE KEY-----
32
+ your-private-key-here
33
+ -----END PRIVATE KEY-----`
34
+ })
35
+
36
+ // Get a secret
37
+ async function getMySecret() {
38
+ try {
39
+ const secret = await client.getSecret('my-secret-name')
40
+ console.log('Secret value:', secret.payload)
41
+ } catch (error) {
42
+ console.error('Error:', error.message)
43
+ }
44
+ }
45
+
46
+ getMySecret()
47
+ ```
48
+
49
+ ## API Reference
50
+
51
+ ### `new CryptaClient(options)`
52
+
53
+ Creates a new Crypta client instance.
54
+
55
+ #### Parameters
56
+
57
+ - `options` (Object)
58
+ - `baseUrl` (string, required) - The base URL of your Crypta instance
59
+ - `clientId` (string, required) - Your service account client ID
60
+ - `privateKeyPem` (string, required) - Your service account private key in PEM format
61
+
62
+ #### Example
63
+
64
+ ```javascript
65
+ const client = new CryptaClient({
66
+ baseUrl: 'https://crypta.example.com',
67
+ clientId: 'sa-12345',
68
+ privateKeyPem: process.env.CRYPTA_PRIVATE_KEY
69
+ })
70
+ ```
71
+
72
+ ### `client.getSecret(name)`
73
+
74
+ Retrieves the latest version of a secret.
75
+
76
+ #### Parameters
77
+
78
+ - `name` (string, required) - The name/path of the secret to retrieve
79
+
80
+ #### Returns
81
+
82
+ Returns a Promise that resolves to an object containing:
83
+
84
+ - `payload` (string) - The secret value
85
+ - Additional metadata about the secret
86
+
87
+ #### Example
88
+
89
+ ```javascript
90
+ const secret = await client.getSecret('database/password')
91
+ console.log(secret.payload) // The actual secret value
92
+ ```
93
+
94
+ #### Error Handling
95
+
96
+ ```javascript
97
+ try {
98
+ const secret = await client.getSecret('my-secret')
99
+ console.log(secret.payload)
100
+ } catch (error) {
101
+ if (error.statusCode === 404) {
102
+ console.error('Secret not found')
103
+ } else if (error.statusCode === 401) {
104
+ console.error('Authentication failed')
105
+ } else {
106
+ console.error('Error:', error.message)
107
+ }
108
+ }
109
+ ```
110
+
111
+ ## Environment Variables
112
+
113
+ It's recommended to store sensitive information in environment variables:
114
+
115
+ ```javascript
116
+ require('dotenv').config() // If using dotenv
117
+
118
+ const client = new CryptaClient({
119
+ baseUrl: process.env.CRYPTA_BASE_URL,
120
+ clientId: process.env.CRYPTA_CLIENT_ID,
121
+ privateKeyPem: process.env.CRYPTA_PRIVATE_KEY
122
+ })
123
+ ```
124
+
125
+ Example `.env` file:
126
+
127
+ ```env
128
+ CRYPTA_BASE_URL=https://crypta.example.com
129
+ CRYPTA_CLIENT_ID=sa-12345
130
+ CRYPTA_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\nMIIE...\n-----END PRIVATE KEY-----"
131
+ ```
132
+
133
+ ## Advanced Usage
134
+
135
+ ### Token Management
136
+
137
+ The client automatically manages authentication tokens, including caching and refresh. You don't need to handle tokens manually.
138
+
139
+ ### Custom Error Handling
140
+
141
+ The library provides custom error types for better error handling:
142
+
143
+ ```javascript
144
+ const { CryptaClient } = require('crypta-client')
145
+
146
+ try {
147
+ const secret = await client.getSecret('my-secret')
148
+ } catch (error) {
149
+ console.error('Status Code:', error.statusCode)
150
+ console.error('Message:', error.message)
151
+ }
152
+ ```
153
+
154
+ ### Multiple Secrets
155
+
156
+ ```javascript
157
+ async function getMultipleSecrets() {
158
+ const secretNames = ['db-password', 'api-key', 'encryption-key']
159
+
160
+ try {
161
+ const secrets = await Promise.all(
162
+ secretNames.map((name) => client.getSecret(name))
163
+ )
164
+
165
+ return secrets.reduce((acc, secret, index) => {
166
+ acc[secretNames[index]] = secret.payload
167
+ return acc
168
+ }, {})
169
+ } catch (error) {
170
+ console.error('Failed to fetch secrets:', error.message)
171
+ throw error
172
+ }
173
+ }
174
+ ```
175
+
176
+ ## Requirements
177
+
178
+ - Node.js >= 14.x
179
+ - A Crypta Secret Manager instance
180
+ - A service account with appropriate permissions
181
+
182
+ ## Dependencies
183
+
184
+ - `axios` - HTTP client
185
+ - `jsonwebtoken` - JWT token generation
186
+
187
+ ## Error Codes
188
+
189
+ | Status Code | Description |
190
+ | ----------- | ------------------------------------------- |
191
+ | 401 | Authentication failed - Invalid credentials |
192
+ | 403 | Forbidden - Insufficient permissions |
193
+ | 404 | Secret not found |
194
+ | 500 | Internal server error |
195
+
196
+ ## Contributing
197
+
198
+ Contributions are welcome! Please feel free to submit a Pull Request.
199
+
200
+ ## License
201
+
202
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
203
+
204
+ ## Support
205
+
206
+ For issues and questions:
207
+
208
+ - Open an issue on [GitHub](https://github.com/rayhanzz772/crypta-client/issues)
209
+ - Contact: [Your contact information]
210
+
211
+ ## Changelog
212
+
213
+ ### v0.1.0
214
+
215
+ - Initial release
216
+ - Service account authentication
217
+ - Secret retrieval functionality
218
+ - Automatic token management
219
+
220
+ ## Author
221
+
222
+ Rayhan
223
+
224
+ ---
225
+
226
+ **Note**: Make sure to keep your private keys secure and never commit them to version control.
package/index.js ADDED
@@ -0,0 +1,2 @@
1
+ const CryptaClient = require('./src/client')
2
+ module.exports = { CryptaClient }
package/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "crypta-client",
3
+ "version": "0.1.1",
4
+ "description": "Crypta Secret Manager client library for Node.js",
5
+ "main": "index.js",
6
+ "type": "commonjs",
7
+ "license": "MIT",
8
+ "author": "Rayhan",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "https://github.com/yourusername/crypta-client"
12
+ },
13
+ "keywords": [
14
+ "secret-manager",
15
+ "vault",
16
+ "cryptography",
17
+ "security",
18
+ "jwt",
19
+ "service-account"
20
+ ],
21
+ "dependencies": {
22
+ "axios": "^1.7.0",
23
+ "jsonwebtoken": "^9.0.2"
24
+ }
25
+ }
@@ -0,0 +1,19 @@
1
+ const jwt = require('jsonwebtoken')
2
+
3
+ function generateAssertion({ clientId, privateKeyPem, audience }) {
4
+ const now = Math.floor(Date.now() / 1000)
5
+
6
+ return jwt.sign(
7
+ {
8
+ iss: clientId,
9
+ sub: clientId,
10
+ aud: audience,
11
+ iat: now,
12
+ exp: now + 120 // 2 menit
13
+ },
14
+ privateKeyPem,
15
+ { algorithm: 'RS256' }
16
+ )
17
+ }
18
+
19
+ module.exports = { generateAssertion }
package/src/client.js ADDED
@@ -0,0 +1,46 @@
1
+ const { createHttp } = require('./http')
2
+ const { TokenManager } = require('./token')
3
+ const { CryptaError } = require('./errors')
4
+
5
+ class CryptaClient {
6
+ constructor({ baseUrl, clientId, privateKeyPem }) {
7
+ if (!baseUrl || !clientId || !privateKeyPem) {
8
+ throw new Error('baseUrl, clientId, dan privateKeyPem wajib diisi')
9
+ }
10
+
11
+ this.baseUrl = baseUrl.replace(/\/+$/, '')
12
+ this.clientId = clientId
13
+ this.privateKeyPem = privateKeyPem
14
+
15
+ this.http = createHttp(this.baseUrl)
16
+
17
+ this.tokenManager = new TokenManager({
18
+ http: this.http,
19
+ clientId: this.clientId,
20
+ privateKeyPem: this.privateKeyPem,
21
+ audience: `${this.baseUrl}/public-api/auth/token`
22
+ })
23
+ }
24
+
25
+ async getSecret(name) {
26
+ if (!name) throw new Error('secret name wajib diisi')
27
+
28
+ const token = await this.tokenManager.getToken()
29
+
30
+ try {
31
+ const res = await this.http.get(
32
+ `/v1/secrets/${name}/versions/latest:access`,
33
+ {
34
+ headers: { Authorization: `Bearer ${token}` }
35
+ }
36
+ )
37
+ return res.data.data
38
+ } catch (err) {
39
+ const status = err.response?.status
40
+ const message = err.response?.data?.message || 'Failed to get secret'
41
+ throw new CryptaError(message, status)
42
+ }
43
+ }
44
+ }
45
+
46
+ module.exports = CryptaClient
package/src/errors.js ADDED
@@ -0,0 +1,9 @@
1
+ class CryptaError extends Error {
2
+ constructor(message, status) {
3
+ super(message)
4
+ this.name = 'CryptaError'
5
+ this.status = status
6
+ }
7
+ }
8
+
9
+ module.exports = { CryptaError }
package/src/http.js ADDED
@@ -0,0 +1,10 @@
1
+ const axios = require('axios')
2
+
3
+ function createHttp(baseUrl) {
4
+ return axios.create({
5
+ baseURL: baseUrl,
6
+ timeout: 10_000
7
+ })
8
+ }
9
+
10
+ module.exports = { createHttp }
package/src/token.js ADDED
@@ -0,0 +1,40 @@
1
+ const { generateAssertion } = require('./assertion')
2
+
3
+ class TokenManager {
4
+ constructor({ http, clientId, privateKeyPem, audience }) {
5
+ this.http = http
6
+ this.clientId = clientId
7
+ this.privateKeyPem = privateKeyPem
8
+ this.audience = audience
9
+
10
+ this.accessToken = null
11
+ this.expiresAt = 0
12
+ }
13
+
14
+ isExpired() {
15
+ return !this.accessToken || Date.now() >= this.expiresAt
16
+ }
17
+
18
+ async fetchToken() {
19
+ const assertion = generateAssertion({
20
+ clientId: this.clientId,
21
+ privateKeyPem: this.privateKeyPem,
22
+ audience: this.audience
23
+ })
24
+
25
+ const res = await this.http.post('/public-api/auth/token', { assertion })
26
+
27
+ this.accessToken = res.data.access_token
28
+ // 1 menit buffer
29
+ this.expiresAt = Date.now() + 9 * 60 * 1000
30
+ }
31
+
32
+ async getToken() {
33
+ if (this.isExpired()) {
34
+ await this.fetchToken()
35
+ }
36
+ return this.accessToken
37
+ }
38
+ }
39
+
40
+ module.exports = { TokenManager }