@kawanua/license-sdk 1.0.1 → 1.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.
@@ -0,0 +1,26 @@
1
+ name: Publish Package
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - 'v*'
7
+
8
+ permissions:
9
+ id-token: write # Required for OIDC
10
+ contents: read
11
+
12
+ jobs:
13
+ publish:
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - uses: actions/checkout@v6
17
+
18
+ - uses: actions/setup-node@v6
19
+ with:
20
+ node-version: '24'
21
+ registry-url: 'https://registry.npmjs.org'
22
+ package-manager-cache: false # never use caching in release builds
23
+ - run: npm ci
24
+ - run: npm run build --if-present
25
+ - run: npm test
26
+ - run: npm publish # Or: npm stage publish
package/README.md CHANGED
@@ -50,6 +50,64 @@ KawanuaLicense.init({
50
50
  });
51
51
  ```
52
52
 
53
+ #### React
54
+
55
+ ```jsx
56
+ import { useEffect, useState } from 'react';
57
+
58
+ function App() {
59
+ const [license, setLicense] = useState(null);
60
+
61
+ useEffect(() => {
62
+ KawanuaLicense.init({
63
+ token: window.__LICENSE_TOKEN__, // inject dari server-side rendering
64
+ onReady: (state) => setLicense(state),
65
+ });
66
+ return () => KawanuaLicense.destroy();
67
+ }, []);
68
+
69
+ if (!license) return <p>Memverifikasi lisensi...</p>;
70
+
71
+ return (
72
+ <div>
73
+ <h1>Dashboard</h1>
74
+ {KawanuaLicense.can('api_access') && <ApiSection />}
75
+ <p>Plan: {license.plan}</p>
76
+ </div>
77
+ );
78
+ }
79
+ ```
80
+
81
+ #### Vue 3
82
+
83
+ ```js
84
+ import { onMounted, onUnmounted } from 'vue';
85
+
86
+ onMounted(() => {
87
+ KawanuaLicense.init({
88
+ token: import.meta.env.VITE_LICENSE_TOKEN,
89
+ onReady: (state) => { license.value = state; },
90
+ });
91
+ });
92
+ onUnmounted(() => KawanuaLicense.destroy());
93
+ ```
94
+
95
+ #### Vanilla JS
96
+
97
+ ```html
98
+ <script src="https://cdn.kawanua.id/sdk/license.min.js"></script>
99
+ <script>
100
+ KawanuaLicense.init({
101
+ token: document.querySelector('meta[name="license-token"]').content,
102
+ onReady: (state) => {
103
+ if (!KawanuaLicense.can('white_label')) {
104
+ document.getElementById('footer-branding').style.display = 'block';
105
+ }
106
+ },
107
+ });
108
+ </script>
109
+ ```
110
+
53
111
  ### Mengecek Akses Fitur
54
112
 
55
113
  Setelah inisialisasi berhasil, kamu bisa mengecek apakah sebuah fitur diperbolehkan:
@@ -80,4 +138,4 @@ SDK secara otomatis mengatur batasan fitur berdasarkan plan:
80
138
  ISC
81
139
 
82
140
  ---
83
- *Dibuat oleh [Kawanua Indo Digital](https://kawanua.id)*
141
+ *Dibuat oleh [Kawanua Indo Digital](https://labs.kawanua.co)*
package/index.js CHANGED
@@ -29,22 +29,10 @@
29
29
 
30
30
  // ─── Konstanta ──────────────────────────────────────────────────
31
31
 
32
- /**
33
- * Public key RS256 dari server lisensi Kawanua.
34
- * Klien hanya bisa VERIFY — tidak bisa forge token baru.
35
- * Ganti dengan public key aktual setelah generate keypair di server.
36
- */
37
- const KAWANUA_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
38
- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2a2rwplBQLzHPZe5TNJF
39
- ... (ganti dengan public key RS256 aktual dari server kamu) ...
40
- -----END PUBLIC KEY-----`;
41
-
42
- /**
43
- * URL endpoint JWKS publik — alternatif lebih dinamis dari hardcode PEM.
44
- * SDK akan fetch public key dari sini jika PEM tidak di-set.
45
- * Endpoint ini harus tersedia di server Kawanua.
46
- */
47
- const JWKS_URL = "https://lisensi.kawanua.workers.dev/.well-known/jwks.json";
32
+ // Public key RS256 dari server lisensi Kawanua (Base64-encoded PEM).
33
+ // Klien hanya bisa VERIFY tidak bisa forge token baru.
34
+ const KAWANUA_PUBLIC_KEY_B64 =
35
+ "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUEzTFgrbDdKbS9VQ25nZ1VmcVhXMAoxazN2M2FYR0RRdEF3Z2NyeVFad1lUY2x5SzdQdFdxMG10ejArUDRyTW0veFBhdmlaQlRaYUt4aVdScWVTQUhUCk4wVDdVSFRSbnJjaElwY1JaeExiTXlzWHJ6eS9NcjZ3cTF1aW5FUjJ1TGNjRE9Hc2swR0Q4QU1TWU54Q1NsZ24KcFREN200R0g0TE1DTzNPU2dXYm9Ua0VWdkp2TXN5ckZ2TTlTT2N1UG9NTmJyTGk2SDVnOTR2OU9UWkRvTzhZeQo0RXUwQjRaM3luVS9iRzY2TFZmWGNTYlNBaGU4TWZ6NDZ2UUxzMXJJNUpqZElhajAxWDdsamw1cWdWcklCRHhkCjhneXUxK1FZcXZjYTdzaTRUVkZzOTlYV0xlUVpBWWh2UC9kT3gvdWxvNE1CL1doQU9NRWNwai9kaGVHYk5ieTcKZ3dJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg=="; // <- isi dengan base64(PUBLIC_KEY_PEM)
48
36
 
49
37
  /** Feature map per plan — single source of truth */
50
38
  const PLAN_FEATURES = {
@@ -112,15 +100,17 @@ MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2a2rwplBQLzHPZe5TNJF
112
100
  }
113
101
 
114
102
  /**
115
- * Import public key RS256 dari PEM string ke CryptoKey.
116
- * Menggunakan Web Crypto API (tersedia di semua browser modern).
103
+ * Import public key RS256 dari base64-encoded PEM ke CryptoKey.
117
104
  */
118
- async function _importPublicKey(pem) {
105
+ async function _importPublicKey(b64) {
106
+ const pem = atob(b64);
119
107
  const pemBody = pem
120
108
  .replace(/-----BEGIN PUBLIC KEY-----/, "")
121
109
  .replace(/-----END PUBLIC KEY-----/, "")
122
110
  .replace(/\s+/g, "");
111
+
123
112
  const der = Uint8Array.from(atob(pemBody), (c) => c.charCodeAt(0));
113
+
124
114
  return crypto.subtle.importKey(
125
115
  "spki",
126
116
  der.buffer,
@@ -130,24 +120,6 @@ MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2a2rwplBQLzHPZe5TNJF
130
120
  );
131
121
  }
132
122
 
133
- /**
134
- * Fetch public key dari JWKS endpoint.
135
- * Fallback jika PEM belum di-hardcode (mode dinamis).
136
- */
137
- async function _fetchPublicKeyFromJWKS(kid) {
138
- const res = await fetch(JWKS_URL, { cache: "force-cache" });
139
- const jwks = await res.json();
140
- const jwk = kid ? jwks.keys.find((k) => k.kid === kid) : jwks.keys[0];
141
- if (!jwk) throw new Error("No matching key found in JWKS");
142
- return crypto.subtle.importKey(
143
- "jwk",
144
- jwk,
145
- { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" },
146
- false,
147
- ["verify"],
148
- );
149
- }
150
-
151
123
  /**
152
124
  * Verifikasi signature JWT dengan RS256.
153
125
  * Returns true jika valid, false jika tidak.
@@ -197,14 +169,14 @@ MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2a2rwplBQLzHPZe5TNJF
197
169
  );
198
170
  }
199
171
 
200
- // Import public key (prioritas: hardcode PEM → JWKS endpoint)
201
- let publicKey;
202
- const hasPem = KAWANUA_PUBLIC_KEY_PEM.includes("..."); // placeholder check
203
- if (!hasPem) {
204
- publicKey = await _importPublicKey(KAWANUA_PUBLIC_KEY_PEM);
205
- } else {
206
- publicKey = await _fetchPublicKeyFromJWKS(header.kid);
172
+ // Fail-fast jika public key belum dikonfigurasi.
173
+ if (!KAWANUA_PUBLIC_KEY_B64) {
174
+ throw new Error(
175
+ "[KawanuaLicense] KAWANUA_PUBLIC_KEY_B64 belum dikonfigurasi. " +
176
+ "Hubungi Kawanua untuk mendapatkan public key SDK kamu.",
177
+ );
207
178
  }
179
+ const publicKey = await _importPublicKey(KAWANUA_PUBLIC_KEY_B64);
208
180
 
209
181
  // Verify signature
210
182
  const isValid = await _verifySignature(token, publicKey);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kawanua/license-sdk",
3
- "version": "1.0.1",
3
+ "version": "1.1.1",
4
4
  "description": "Drop-in SDK untuk klien yang menggunakan lisensi Kawanua.",
5
5
  "keywords": [
6
6
  "kawanua",