@kiauth/web-sdk 1.0.0 → 1.1.0
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/README.md +165 -34
- package/dist/kiauth-sdk.js +82 -3
- package/dist/kiauth-sdk.js.map +1 -1
- package/dist/kiauth-sdk.min.js +81 -2
- package/dist/kiauth-sdk.min.js.map +1 -1
- package/dist/types/KiauthSDK.d.ts +9 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/normalizeUser.d.ts +24 -0
- package/dist/types/types.d.ts +51 -2
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
# @kiauth/web-sdk
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
Svelte, or plain HTML). Renders a button, runs the QR approval flow, and hands your
|
|
5
|
-
frontend a one-time authorization `code`. Your **server** exchanges that code for the
|
|
6
|
-
verified user.
|
|
3
|
+
Three things, for any website (React, Vue, Angular, Svelte, or plain HTML):
|
|
7
4
|
|
|
8
|
-
|
|
5
|
+
1. **Verified Human** — know the person signing up is a real, unique, Aadhaar-verified human. Not a bot, not a duplicate account.
|
|
6
|
+
2. **Signup / Login** — passwordless "Login with Kiauth". The user approves on their phone with a biometric. No password database.
|
|
7
|
+
3. **Session management + cross-app knowledge** — the user sees every app they are signed into and can revoke any of them; your server is told when they do. You can also surface logins that never went through Kiauth at all.
|
|
8
|
+
|
|
9
|
+
> Backend: `https://api.kiauth.com` — pass it as `baseUrl`.
|
|
9
10
|
|
|
10
11
|
## Install
|
|
11
12
|
|
|
12
13
|
**Option A — script tag (no build tooling):**
|
|
13
14
|
```html
|
|
14
|
-
<script src="/
|
|
15
|
+
<script src="https://unpkg.com/@kiauth/web-sdk@1.1.0/dist/kiauth-sdk.min.js"></script>
|
|
15
16
|
<script>
|
|
16
17
|
const kiauth = new KiauthSDK({
|
|
17
18
|
clientId: 'YOUR_CLIENT_ID',
|
|
@@ -19,10 +20,14 @@ verified user.
|
|
|
19
20
|
environment: 'production',
|
|
20
21
|
baseUrl: 'https://api.kiauth.com'
|
|
21
22
|
});
|
|
23
|
+
|
|
22
24
|
kiauth.renderButton('#login-container', {
|
|
23
25
|
text: 'Login with Kiauth',
|
|
26
|
+
// onSuccess receives the VERIFIED USER — the SDK completes the code exchange
|
|
27
|
+
// for you. It does not hand you a raw `code`.
|
|
24
28
|
onSuccess: (user) => {
|
|
25
|
-
|
|
29
|
+
if (!KiauthSDK.isProductionTrustworthy(user)) return; // see §1
|
|
30
|
+
createSessionFor(user.userToken);
|
|
26
31
|
},
|
|
27
32
|
onError: (err) => console.error(err.message)
|
|
28
33
|
});
|
|
@@ -30,12 +35,12 @@ verified user.
|
|
|
30
35
|
<div id="login-container"></div>
|
|
31
36
|
```
|
|
32
37
|
|
|
33
|
-
**Option B —
|
|
38
|
+
**Option B — npm:**
|
|
34
39
|
```bash
|
|
35
|
-
npm install
|
|
40
|
+
npm install @kiauth/web-sdk
|
|
36
41
|
```
|
|
37
42
|
```js
|
|
38
|
-
import { KiauthSDK } from '@kiauth/web-sdk';
|
|
43
|
+
import { KiauthSDK, isProductionTrustworthy } from '@kiauth/web-sdk';
|
|
39
44
|
|
|
40
45
|
const kiauth = new KiauthSDK({
|
|
41
46
|
clientId: process.env.KIAUTH_CLIENT_ID,
|
|
@@ -45,6 +50,135 @@ const kiauth = new KiauthSDK({
|
|
|
45
50
|
});
|
|
46
51
|
```
|
|
47
52
|
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## 1. Verified Human
|
|
56
|
+
|
|
57
|
+
`onSuccess(user)` hands you a **graded** identity claim. Never trust the bare boolean.
|
|
58
|
+
|
|
59
|
+
```js
|
|
60
|
+
kiauth.renderButton('#login-container', {
|
|
61
|
+
onSuccess: (user) => {
|
|
62
|
+
// ❌ Not enough — `kiauth_verified` is ALSO true for Kiauth's test identities.
|
|
63
|
+
// if (user.kiauth_verified) { ... }
|
|
64
|
+
|
|
65
|
+
// ✅ The check a production app should make:
|
|
66
|
+
if (!isProductionTrustworthy(user)) {
|
|
67
|
+
return showError('We could not verify your identity.');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// user.userToken — stable + pairwise. The SAME human always maps to this token
|
|
71
|
+
// at YOUR app, and to a different one at every other app.
|
|
72
|
+
// Key your account record on it.
|
|
73
|
+
// user.uniqueness — 'one_account_per_human'. One real human holds at most one
|
|
74
|
+
// account at your app — not even by deleting their Kiauth
|
|
75
|
+
// account and re-registering.
|
|
76
|
+
createAccount({ kiauthUserToken: user.userToken, name: user.name, email: user.email });
|
|
77
|
+
},
|
|
78
|
+
onError: (err) => console.error(err.message)
|
|
79
|
+
});
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
`isProductionTrustworthy(user)` is exactly
|
|
83
|
+
`user.kiauth_verified && user.assurance_level === 'aadhaar' && !user.is_test_identity`.
|
|
84
|
+
It is also available as `KiauthSDK.isProductionTrustworthy` for script-tag users.
|
|
85
|
+
|
|
86
|
+
### The `KiauthUser` object
|
|
87
|
+
|
|
88
|
+
| Field | Type | Meaning |
|
|
89
|
+
|---|---|---|
|
|
90
|
+
| `kiauth_verified` | boolean | Kiauth ran an identity check and it passed. **Also true for test identities.** |
|
|
91
|
+
| `verified` | boolean | Alias of `kiauth_verified`. Both are always present. |
|
|
92
|
+
| `identity_verified` | boolean | The durable fact that this human completed Aadhaar verification. Never decays. |
|
|
93
|
+
| `assurance_level` | `'aadhaar' \| 'human' \| 'test' \| 'none'` | How strong the check was. |
|
|
94
|
+
| `is_test_identity` | boolean | **TRUE for a fabricated identity from Kiauth's test mode. Reject in production.** |
|
|
95
|
+
| `uniqueness` | `'one_account_per_human'` | Kiauth's guarantee. |
|
|
96
|
+
| `userToken` | string | Stable, pairwise per (human, your app). Key your account on it. |
|
|
97
|
+
| `method` | `'aadhaar_okyc' \| 'peer' \| 'test_identity' \| 'none'` | |
|
|
98
|
+
| `verified_at` | string \| null | ISO timestamp of the identity check. |
|
|
99
|
+
| `verification_expires_at` | string \| null | When Kiauth will ask them to renew. |
|
|
100
|
+
| `assurance_age_days` | number \| null | How old the check is, in whole days. |
|
|
101
|
+
| `assurance_fresh` | boolean | Whether it is inside Kiauth's current freshness window. |
|
|
102
|
+
|
|
103
|
+
**Re-verification is Kiauth's job, not yours.** Kiauth periodically re-checks each
|
|
104
|
+
human; that cadence is a Kiauth↔user relationship. You are never forced to pay for a
|
|
105
|
+
fresh check because our clock ran out. If your own risk rules genuinely need a recent
|
|
106
|
+
check, use the verification API's `maxAssuranceAgeDays`. Otherwise read
|
|
107
|
+
`identity_verified` and let us worry about it.
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## 2. Signup / Login
|
|
112
|
+
|
|
113
|
+
`renderButton()` runs the whole flow: shows a QR (desktop) or opens the Kiauth app
|
|
114
|
+
(mobile), waits for the biometric approval, exchanges the one-time code, and calls
|
|
115
|
+
`onSuccess(user)`.
|
|
116
|
+
|
|
117
|
+
```js
|
|
118
|
+
kiauth.renderButton('#login-container', { onSuccess, onError });
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Or drive it yourself, without the button:
|
|
122
|
+
|
|
123
|
+
```js
|
|
124
|
+
await kiauth.startLogin({ onSuccess, onError, onCancel });
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Where the code exchange happens
|
|
128
|
+
|
|
129
|
+
- **Without `clientSecret`** (recommended for browsers) — the SDK uses **PKCE** and
|
|
130
|
+
exchanges the code with no secret.
|
|
131
|
+
- **With `clientSecret` in config** — the SDK exchanges directly. Convenient for
|
|
132
|
+
server-side or sandbox use. **Never ship a client secret to a browser.**
|
|
133
|
+
|
|
134
|
+
Verify an access token later, from your server:
|
|
135
|
+
```js
|
|
136
|
+
const { valid, user } = await kiauth.verifyToken(accessToken);
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
> Prefer a redirect-based integration with your existing auth library (Auth.js, etc.)?
|
|
140
|
+
> Kiauth is also a standard **OpenID Connect** provider — see the dev-portal docs.
|
|
141
|
+
> The embedded button (this SDK) gives full white-label control; OIDC is the
|
|
142
|
+
> lowest-friction drop-in.
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## 3. Session management + cross-app knowledge
|
|
147
|
+
|
|
148
|
+
The user sees every app they are signed into, inside the Kiauth app, and can revoke
|
|
149
|
+
any of them. When they do, Kiauth sends your server a **`SESSION_REVOKED`** webhook so
|
|
150
|
+
you can end your own session too — two-way logout.
|
|
151
|
+
|
|
152
|
+
You can also surface logins that **never went through Kiauth**, so the user's dashboard
|
|
153
|
+
is complete rather than partial.
|
|
154
|
+
|
|
155
|
+
> These methods require your `clientSecret` and **throw if called from a browser**.
|
|
156
|
+
> Run them on your server.
|
|
157
|
+
|
|
158
|
+
```js
|
|
159
|
+
// A user signed in with Google / password / OTP — put it on their Kiauth dashboard.
|
|
160
|
+
await kiauth.reportSession({
|
|
161
|
+
kiauthUserToken: user.userToken,
|
|
162
|
+
externalSessionId: yourSessionId,
|
|
163
|
+
authMethod: 'GOOGLE_OAUTH', // EMAIL_PASSWORD | FACEBOOK | PHONE_OTP | APPLE | PASSKEY | UNKNOWN
|
|
164
|
+
deviceName: 'Chrome on MacBook',
|
|
165
|
+
deviceType: 'DESKTOP'
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
// Don't know their userToken? Report against a verified email instead.
|
|
169
|
+
await kiauth.reportSessionByEmail({ email, externalSessionId, authMethod: 'EMAIL_PASSWORD' });
|
|
170
|
+
|
|
171
|
+
// Backfill many at once.
|
|
172
|
+
await kiauth.reportBatchSessions(user.userToken, [ /* ... */ ]);
|
|
173
|
+
|
|
174
|
+
// The user logged out of YOUR app — remove it from their dashboard.
|
|
175
|
+
await kiauth.logout({ externalSessionId: yourSessionId });
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Webhooks to handle: `SESSION_CREATED`, `SESSION_REVOKED`, `PERMISSION_REVOKED`.
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
48
182
|
## Config
|
|
49
183
|
|
|
50
184
|
| Field | Required | Notes |
|
|
@@ -53,6 +187,7 @@ const kiauth = new KiauthSDK({
|
|
|
53
187
|
| `scopes` | yes | Subset of `name`, `email`, `dob`, `gender`, `phone` |
|
|
54
188
|
| `environment` | yes | `'production'` or `'sandbox'` |
|
|
55
189
|
| `baseUrl` | recommended | Backend URL. Overrides the env default. `/api/v1` is appended automatically. |
|
|
190
|
+
| `clientSecret` | no | **Server / sandbox only.** Omit in browsers — the SDK uses PKCE. |
|
|
56
191
|
| `redirectUri` | optional | Where to return after approval |
|
|
57
192
|
|
|
58
193
|
## Button customization (white-label)
|
|
@@ -83,27 +218,23 @@ kiauth.renderButton('#login-container', {
|
|
|
83
218
|
});
|
|
84
219
|
```
|
|
85
220
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
const { user } = await res.json(); // { name, email, verified, userToken }
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
Use `user.userToken` (a stable per-user id) to create your own app session.
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## Changelog
|
|
224
|
+
|
|
225
|
+
### 1.1.0
|
|
226
|
+
- **Fixed:** `user.kiauth_verified` was `undefined` after a login. The backend's login
|
|
227
|
+
exchange emitted `verified` while this SDK's type declared `kiauth_verified`, so an
|
|
228
|
+
app checking the documented field silently treated every verified human as
|
|
229
|
+
unverified. Both spellings are now emitted and normalized.
|
|
230
|
+
- **Fixed:** the README claimed `onSuccess` hands you a `code` to exchange on your
|
|
231
|
+
server. It hands you the verified **user** — the SDK completes the exchange.
|
|
232
|
+
- **Added:** graded identity claims — `assurance_level`, `is_test_identity`,
|
|
233
|
+
`identity_verified`, `assurance_age_days`, `assurance_fresh`, `uniqueness`.
|
|
234
|
+
- **Added:** `isProductionTrustworthy(user)` (also `KiauthSDK.isProductionTrustworthy`).
|
|
235
|
+
- **Hardened:** every response is normalized and **fails closed**. A missing or
|
|
236
|
+
unrecognised field grades as unverified rather than reaching your `if` as
|
|
237
|
+
`undefined`.
|
|
238
|
+
|
|
239
|
+
### 1.0.0
|
|
240
|
+
- Initial release.
|
package/dist/kiauth-sdk.js
CHANGED
|
@@ -1,3 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalizes whatever the backend returned into a complete `KiauthUser`.
|
|
3
|
+
*
|
|
4
|
+
* Why this exists: `/auth/token` and `/auth/verify` historically disagreed on the
|
|
5
|
+
* key name — the login exchange said `verified`, introspection said
|
|
6
|
+
* `kiauth_verified`, and this SDK's type declared only the latter. Any app that
|
|
7
|
+
* wrote `if (user.kiauth_verified)` after a login was reading `undefined` and
|
|
8
|
+
* treating a verified human as unverified.
|
|
9
|
+
*
|
|
10
|
+
* The backend now emits both spellings. This function keeps the SDK correct against
|
|
11
|
+
* older backends too, and guarantees every field is defined rather than silently
|
|
12
|
+
* absent — an `undefined` in a trust decision is worse than a `false`.
|
|
13
|
+
*
|
|
14
|
+
* FAIL CLOSED: anything missing or unrecognised grades as unverified.
|
|
15
|
+
*/
|
|
16
|
+
function normalizeUser(raw) {
|
|
17
|
+
const src = raw ?? {};
|
|
18
|
+
// Accept either spelling; require an explicit `true`, never a truthy accident.
|
|
19
|
+
const verified = src.kiauth_verified === true || src.verified === true;
|
|
20
|
+
const assurance = src.assurance_level === 'aadhaar' ||
|
|
21
|
+
src.assurance_level === 'human' ||
|
|
22
|
+
src.assurance_level === 'test' ||
|
|
23
|
+
src.assurance_level === 'none'
|
|
24
|
+
? src.assurance_level
|
|
25
|
+
: // An older backend sent no grading. A bare `verified: true` cannot be
|
|
26
|
+
// distinguished from a test identity, so do not claim `aadhaar`.
|
|
27
|
+
verified
|
|
28
|
+
? 'human'
|
|
29
|
+
: 'none';
|
|
30
|
+
const method = src.method === 'aadhaar_okyc' || src.method === 'test_identity' || src.method === 'peer' || src.method === 'none'
|
|
31
|
+
? src.method
|
|
32
|
+
: 'none';
|
|
33
|
+
const expiresAt = src.verification_expires_at ?? src.verificationExpiry ?? null;
|
|
34
|
+
return {
|
|
35
|
+
name: src.name ?? src.displayName ?? null,
|
|
36
|
+
email: src.email ?? null,
|
|
37
|
+
kiauth_verified: verified,
|
|
38
|
+
verified,
|
|
39
|
+
// Absent on an older backend: fall back to `verified` rather than inventing a
|
|
40
|
+
// durable fact we were not told about.
|
|
41
|
+
identity_verified: src.identity_verified === true || verified,
|
|
42
|
+
assurance_level: assurance,
|
|
43
|
+
method,
|
|
44
|
+
verified_at: src.verified_at ?? null,
|
|
45
|
+
verification_expires_at: expiresAt,
|
|
46
|
+
verificationExpiry: expiresAt,
|
|
47
|
+
assurance_age_days: typeof src.assurance_age_days === 'number' ? src.assurance_age_days : null,
|
|
48
|
+
assurance_fresh: src.assurance_fresh === true,
|
|
49
|
+
// Fail closed: if the backend did not say, assume nothing. A missing flag must
|
|
50
|
+
// not read as "definitely a real human".
|
|
51
|
+
is_test_identity: src.is_test_identity === true,
|
|
52
|
+
uniqueness: 'one_account_per_human',
|
|
53
|
+
userToken: src.userToken ?? ''
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* The check a production app should make. True only for a real, Aadhaar-verified,
|
|
58
|
+
* non-fabricated human.
|
|
59
|
+
*
|
|
60
|
+
* if (!isProductionTrustworthy(user)) return reject();
|
|
61
|
+
*/
|
|
62
|
+
function isProductionTrustworthy(user) {
|
|
63
|
+
return user.kiauth_verified && user.assurance_level === 'aadhaar' && !user.is_test_identity;
|
|
64
|
+
}
|
|
65
|
+
|
|
1
66
|
class KiauthApi {
|
|
2
67
|
constructor(config) {
|
|
3
68
|
this.baseUrl = KiauthApi.resolveBaseUrl(config);
|
|
@@ -99,7 +164,10 @@ class KiauthApi {
|
|
|
99
164
|
const err = await res.json().catch(() => ({}));
|
|
100
165
|
throw { code: err.code || 'TOKEN_EXCHANGE_FAILED', message: err.message || 'Failed to exchange authorization code.' };
|
|
101
166
|
}
|
|
102
|
-
|
|
167
|
+
const body = await res.json();
|
|
168
|
+
// Never hand the caller a raw response: normalizeUser guarantees every field is
|
|
169
|
+
// defined, so `user.kiauth_verified` can never be a silent `undefined`.
|
|
170
|
+
return { ...body, user: normalizeUser(body.user) };
|
|
103
171
|
}
|
|
104
172
|
async verifyToken(token) {
|
|
105
173
|
const res = await fetch(`${this.baseUrl}/auth/verify`, {
|
|
@@ -113,7 +181,10 @@ class KiauthApi {
|
|
|
113
181
|
if (!res.ok) {
|
|
114
182
|
return { valid: false };
|
|
115
183
|
}
|
|
116
|
-
|
|
184
|
+
const body = await res.json();
|
|
185
|
+
if (!body.valid)
|
|
186
|
+
return { valid: false };
|
|
187
|
+
return { valid: true, user: normalizeUser(body.user) };
|
|
117
188
|
}
|
|
118
189
|
async reportSession(clientId, clientSecret, options) {
|
|
119
190
|
const creds = btoa(`${clientId}:${clientSecret}`);
|
|
@@ -3679,6 +3750,14 @@ class KiauthSDK {
|
|
|
3679
3750
|
this.modal.close();
|
|
3680
3751
|
}
|
|
3681
3752
|
}
|
|
3753
|
+
/**
|
|
3754
|
+
* The check a production app should make on every login.
|
|
3755
|
+
*
|
|
3756
|
+
* `user.kiauth_verified` alone is NOT enough — it is also true for Kiauth's test
|
|
3757
|
+
* identities. Exposed as a static so script-tag (UMD) users can reach it too:
|
|
3758
|
+
* if (!KiauthSDK.isProductionTrustworthy(user)) return;
|
|
3759
|
+
*/
|
|
3760
|
+
KiauthSDK.isProductionTrustworthy = isProductionTrustworthy;
|
|
3682
3761
|
|
|
3683
|
-
export { KiauthSDK };
|
|
3762
|
+
export { KiauthSDK, isProductionTrustworthy, normalizeUser };
|
|
3684
3763
|
//# sourceMappingURL=kiauth-sdk.js.map
|