@proveanything/smartlinks 1.14.15 → 1.14.17

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.
@@ -1,4 +1,4 @@
1
- import type { AuthLoginResponse, AppleLoginOptions, PhoneSendCodeResponse, PhoneVerifyResponse, PasswordResetRequestResponse, VerifyResetTokenResponse, PasswordResetCompleteResponse, EmailVerificationActionResponse, EmailVerifyTokenResponse, AuthKitConfig, MagicLinkSendResponse, MagicLinkVerifyResponse, UserProfile, UpdateProfileResponse, ProfileUpdateData, SuccessResponse, SendWhatsAppRequest, SendWhatsAppResponse, ExchangeWhatsAppSessionResponse, VerifyWhatsAppResponse, WhatsAppStatusResponse, SendSmsVerifyRequest, SendSmsVerifyResponse, VerifySmsResponse, UpsertContactRequest, UpsertContactResponse } from "../types/authKit";
1
+ import type { AuthLoginResponse, AppleLoginOptions, RefreshResponse, LogoutResponse, PhoneSendCodeResponse, PhoneVerifyResponse, PasswordResetRequestResponse, VerifyResetTokenResponse, PasswordResetCompleteResponse, EmailVerificationActionResponse, EmailVerifyTokenResponse, AuthKitConfig, MagicLinkSendResponse, MagicLinkVerifyResponse, UserProfile, UpdateProfileResponse, ProfileUpdateData, SuccessResponse, SendWhatsAppRequest, SendWhatsAppResponse, ExchangeWhatsAppSessionResponse, VerifyWhatsAppResponse, WhatsAppStatusResponse, SendSmsVerifyRequest, SendSmsVerifyResponse, VerifySmsResponse, UpsertContactRequest, UpsertContactResponse } from "../types/authKit";
2
2
  /**
3
3
  * Namespace containing helper functions for the new AuthKit API.
4
4
  * Legacy collection-based authKit helpers retained (marked as *Legacy*).
@@ -35,6 +35,35 @@ export declare namespace authKit {
35
35
  * @see AppleLoginOptions
36
36
  */
37
37
  function appleLogin(clientId: string, identityToken: string, opts?: AppleLoginOptions): Promise<AuthLoginResponse>;
38
+ /**
39
+ * Exchange a refresh token for a fresh access token (public — the refresh token IS
40
+ * the credential). **Native sessions only**; refresh tokens are issued only when the
41
+ * host opted in via `initializeApi({ platform: 'native' })`.
42
+ *
43
+ * On success the new access token is stored automatically (`setBearerToken`). The
44
+ * returned `refreshToken` is **rotated** — the caller must persist it and discard the
45
+ * old one before refreshing again.
46
+ *
47
+ * ⚠️ **Single-use, no retry, serialize calls.** This method issues exactly one request
48
+ * and never retries: replaying a consumed refresh token triggers
49
+ * `REFRESH_TOKEN_REUSE_DETECTED` (the whole session family is revoked). The caller is
50
+ * responsible for ensuring only one refresh is in flight at a time (e.g. across tabs or
51
+ * resume events).
52
+ *
53
+ * Errors (thrown as `SmartlinksApiError`, read via `err.errorCode`):
54
+ * `MISSING_REFRESH_TOKEN` (400), `INVALID_REFRESH_TOKEN` (401),
55
+ * `REFRESH_TOKEN_REUSE_DETECTED` (401) — the last two mean a hard logout.
56
+ *
57
+ * @see RefreshErrorCode
58
+ */
59
+ function refreshToken(clientId: string, refreshToken: string): Promise<RefreshResponse>;
60
+ /**
61
+ * Revoke a refresh token's entire family server-side (that device's whole rotation
62
+ * chain) and clear the in-memory bearer token. Idempotent — always resolves to
63
+ * `{ success: true }`, never revealing whether the token existed. Call on explicit
64
+ * sign-out. Persisted tokens in the host's own storage must be cleared separately.
65
+ */
66
+ function logout(clientId: string, refreshToken: string): Promise<LogoutResponse>;
38
67
  /** Send a magic link email to the user (public). */
39
68
  function sendMagicLink(clientId: string, data: {
40
69
  email: string;
@@ -67,6 +96,11 @@ export declare namespace authKit {
67
96
  clientName?: string;
68
97
  }): Promise<PasswordResetRequestResponse>;
69
98
  function verifyResetToken(clientId: string, token: string): Promise<VerifyResetTokenResponse>;
99
+ /**
100
+ * Complete a password reset / invite acceptance. On invite acceptance under
101
+ * `verify-auto-login` the server returns a session — adopt it so the caller is logged
102
+ * straight in (plain resets return no token and leave the bearer untouched).
103
+ */
70
104
  function completePasswordReset(clientId: string, token: string, newPassword: string): Promise<PasswordResetCompleteResponse>;
71
105
  function sendEmailVerification(clientId: string, data: {
72
106
  userId: string;
@@ -74,6 +108,7 @@ export declare namespace authKit {
74
108
  redirectUrl?: string;
75
109
  clientName?: string;
76
110
  }): Promise<EmailVerificationActionResponse>;
111
+ /** Verify an email token; under `verify-auto-login` the server returns a session — adopt it. */
77
112
  function verifyEmail(clientId: string, token: string): Promise<EmailVerifyTokenResponse>;
78
113
  function resendEmailVerification(clientId: string, data: {
79
114
  userId: string;
@@ -70,6 +70,52 @@ export var authKit;
70
70
  return res;
71
71
  }
72
72
  authKit.appleLogin = appleLogin;
73
+ /* ===================================
74
+ * Native session refresh (public)
75
+ * =================================== */
76
+ /**
77
+ * Exchange a refresh token for a fresh access token (public — the refresh token IS
78
+ * the credential). **Native sessions only**; refresh tokens are issued only when the
79
+ * host opted in via `initializeApi({ platform: 'native' })`.
80
+ *
81
+ * On success the new access token is stored automatically (`setBearerToken`). The
82
+ * returned `refreshToken` is **rotated** — the caller must persist it and discard the
83
+ * old one before refreshing again.
84
+ *
85
+ * ⚠️ **Single-use, no retry, serialize calls.** This method issues exactly one request
86
+ * and never retries: replaying a consumed refresh token triggers
87
+ * `REFRESH_TOKEN_REUSE_DETECTED` (the whole session family is revoked). The caller is
88
+ * responsible for ensuring only one refresh is in flight at a time (e.g. across tabs or
89
+ * resume events).
90
+ *
91
+ * Errors (thrown as `SmartlinksApiError`, read via `err.errorCode`):
92
+ * `MISSING_REFRESH_TOKEN` (400), `INVALID_REFRESH_TOKEN` (401),
93
+ * `REFRESH_TOKEN_REUSE_DETECTED` (401) — the last two mean a hard logout.
94
+ *
95
+ * @see RefreshErrorCode
96
+ */
97
+ async function refreshToken(clientId, refreshToken) {
98
+ const res = await post(`/authkit/${encodeURIComponent(clientId)}/auth/refresh`, { refreshToken });
99
+ if (res.token) {
100
+ setBearerToken(res.token);
101
+ invalidateCache();
102
+ }
103
+ return res;
104
+ }
105
+ authKit.refreshToken = refreshToken;
106
+ /**
107
+ * Revoke a refresh token's entire family server-side (that device's whole rotation
108
+ * chain) and clear the in-memory bearer token. Idempotent — always resolves to
109
+ * `{ success: true }`, never revealing whether the token existed. Call on explicit
110
+ * sign-out. Persisted tokens in the host's own storage must be cleared separately.
111
+ */
112
+ async function logout(clientId, refreshToken) {
113
+ const res = await post(`/authkit/${encodeURIComponent(clientId)}/auth/logout`, { refreshToken });
114
+ setBearerToken(undefined);
115
+ invalidateCache();
116
+ return res;
117
+ }
118
+ authKit.logout = logout;
73
119
  /** Send a magic link email to the user (public). */
74
120
  async function sendMagicLink(clientId, data) {
75
121
  return post(`/authkit/${encodeURIComponent(clientId)}/auth/magic-link/send`, data);
@@ -151,8 +197,18 @@ export var authKit;
151
197
  return post(`/authkit/${encodeURIComponent(clientId)}/auth/verify-reset-token`, { token });
152
198
  }
153
199
  authKit.verifyResetToken = verifyResetToken;
200
+ /**
201
+ * Complete a password reset / invite acceptance. On invite acceptance under
202
+ * `verify-auto-login` the server returns a session — adopt it so the caller is logged
203
+ * straight in (plain resets return no token and leave the bearer untouched).
204
+ */
154
205
  async function completePasswordReset(clientId, token, newPassword) {
155
- return post(`/authkit/${encodeURIComponent(clientId)}/auth/complete-reset`, { token, newPassword });
206
+ const res = await post(`/authkit/${encodeURIComponent(clientId)}/auth/complete-reset`, { token, newPassword });
207
+ if (res.token) {
208
+ setBearerToken(res.token);
209
+ invalidateCache();
210
+ }
211
+ return res;
156
212
  }
157
213
  authKit.completePasswordReset = completePasswordReset;
158
214
  /* ===================================
@@ -162,8 +218,14 @@ export var authKit;
162
218
  return post(`/authkit/${encodeURIComponent(clientId)}/auth/send-verification`, data);
163
219
  }
164
220
  authKit.sendEmailVerification = sendEmailVerification;
221
+ /** Verify an email token; under `verify-auto-login` the server returns a session — adopt it. */
165
222
  async function verifyEmail(clientId, token) {
166
- return post(`/authkit/${encodeURIComponent(clientId)}/auth/verify-email`, { token });
223
+ const res = await post(`/authkit/${encodeURIComponent(clientId)}/auth/verify-email`, { token });
224
+ if (res.token) {
225
+ setBearerToken(res.token);
226
+ invalidateCache();
227
+ }
228
+ return res;
167
229
  }
168
230
  authKit.verifyEmail = verifyEmail;
169
231
  async function resendEmailVerification(clientId, data) {
@@ -1,6 +1,6 @@
1
1
  # Smartlinks API Summary
2
2
 
3
- Version: 1.14.15 | Generated: 2026-05-31T15:31:06.561Z
3
+ Version: 1.14.17 | Generated: 2026-06-02T16:17:46.100Z
4
4
 
5
5
  This is a concise summary of all available API functions and types.
6
6
 
@@ -152,12 +152,11 @@ Return whether proxy mode is currently enabled.
152
152
  proxyMode?: boolean
153
153
  ngrokSkipBrowserWarning?: boolean
154
154
  extraHeaders?: Record<string, string>
155
- iframeAutoResize?: boolean // default true when in iframe
156
- logger?: Logger // optional console-like or function to enable verbose logging
157
155
  /**
158
- * When true, the bearer token is automatically saved to localStorage after login
159
- * and restored on the next page load. Eliminates the need to manually persist
160
- * the token across refreshes. Clear it by calling setBearerToken(undefined) → `void`
156
+ * Declares the host platform. Set to `'native'` on native/Capacitor hosts to opt into
157
+ * AuthKit refresh tokens the SDK then sends `X-Client-Platform: native` on every
158
+ * request, so login endpoints return `refreshToken`/`refreshTokenExpiresAt` and a
159
+ * short-lived access token. Omit (or `'web'`) → `void`
161
160
  Call this once (e.g. at app startup) to configure baseURL/auth.
162
161
 
163
162
  **setNgrokSkipBrowserWarning**(flag: boolean) → `void`
@@ -2963,6 +2962,33 @@ interface AuthLoginResponse {
2963
2962
  * or null when the server could not decode it. Currently only populated by the Apple
2964
2963
  * login endpoint.
2965
2964
  expiresAt?: number | null
2965
+ * Opaque, single-use refresh token. **Native clients only** — present only when the
2966
+ * request opted in via `initializeApi({ platform: 'native' })` (or the
2967
+ * `X-Client-Platform: native` header). For native logins, `token` above is the
2968
+ * short-lived access token; pair it with this refresh token. Undefined for web.
2969
+ refreshToken?: string
2970
+ * Absolute expiry of the refresh-token family, in **milliseconds since epoch**.
2971
+ * Fixed at login — it does **not** move when the token is rotated. Native only.
2972
+ refreshTokenExpiresAt?: number
2973
+ }
2974
+ ```
2975
+
2976
+ **RefreshResponse** (interface)
2977
+ ```typescript
2978
+ interface RefreshResponse {
2979
+ token: string
2980
+ refreshToken: string
2981
+ refreshTokenExpiresAt: number
2982
+ expiresAt: number
2983
+ user: AuthKitUser
2984
+ accountData?: Record<string, any>
2985
+ }
2986
+ ```
2987
+
2988
+ **LogoutResponse** (interface)
2989
+ ```typescript
2990
+ interface LogoutResponse {
2991
+ success: true
2966
2992
  }
2967
2993
  ```
2968
2994
 
@@ -3027,6 +3053,13 @@ interface VerifyResetTokenResponse {
3027
3053
  interface PasswordResetCompleteResponse {
3028
3054
  success: boolean
3029
3055
  message: string
3056
+ token?: string
3057
+ user?: AuthKitUser
3058
+ accountData?: Record<string, any> | null
3059
+ emailVerificationMode?: 'immediate' | 'verify-auto-login' | 'verify-manual-login'
3060
+ refreshToken?: string
3061
+ refreshTokenExpiresAt?: number
3062
+ expiresAt?: number
3030
3063
  }
3031
3064
  ```
3032
3065
 
@@ -3218,6 +3251,8 @@ interface AuthKitConfig {
3218
3251
  }
3219
3252
  ```
3220
3253
 
3254
+ **RefreshErrorCode** = ``
3255
+
3221
3256
  **AuthKitErrorCode** = ``
3222
3257
 
3223
3258
  **VerifyStatus** = `'pending' | 'verified' | 'failed' | 'expired' | 'unknown'`
@@ -8297,6 +8332,12 @@ Google OAuth login via server-side authorization code (public).
8297
8332
  **appleLogin**(clientId: string, identityToken: string, opts?: AppleLoginOptions) → `Promise<AuthLoginResponse>`
8298
8333
  Sign in with Apple via an Apple identity token (public). Mirrors {@link googleLogin}. On success the returned bearer token is stored automatically and the cache is invalidated. Notable error codes (thrown as `SmartlinksApiError`, read via `err.errorCode`): - `MISSING_APPLE_TOKEN` (400), `APPLE_AUTH_NOT_CONFIGURED` (400), `INVALID_APPLE_TOKEN` (401), `APPLE_AUTH_FAILED` (500) - `ACCOUNT_EXISTS_UNVERIFIED` (409) — an unverified account already owns this email; the server refuses to silently link. `err.details.requiresEmailVerification` is `true`. Recoverable: the user should sign in with their password (or reset it), then link Apple from settings. **The same 409 can now come back from {@link googleLogin}** under the shared verified-to-verified linking policy.
8299
8334
 
8335
+ **refreshToken**(clientId: string, refreshToken: string) → `Promise<RefreshResponse>`
8336
+ Exchange a refresh token for a fresh access token (public — the refresh token IS the credential). **Native sessions only**; refresh tokens are issued only when the host opted in via `initializeApi({ platform: 'native' })`. On success the new access token is stored automatically (`setBearerToken`). The returned `refreshToken` is **rotated** — the caller must persist it and discard the old one before refreshing again. ⚠️ **Single-use, no retry, serialize calls.** This method issues exactly one request and never retries: replaying a consumed refresh token triggers `REFRESH_TOKEN_REUSE_DETECTED` (the whole session family is revoked). The caller is responsible for ensuring only one refresh is in flight at a time (e.g. across tabs or resume events). Errors (thrown as `SmartlinksApiError`, read via `err.errorCode`): `MISSING_REFRESH_TOKEN` (400), `INVALID_REFRESH_TOKEN` (401), `REFRESH_TOKEN_REUSE_DETECTED` (401) — the last two mean a hard logout.
8337
+
8338
+ **logout**(clientId: string, refreshToken: string) → `Promise<LogoutResponse>`
8339
+ Revoke a refresh token's entire family server-side (that device's whole rotation chain) and clear the in-memory bearer token. Idempotent — always resolves to `{ success: true }`, never revealing whether the token existed. Call on explicit sign-out. Persisted tokens in the host's own storage must be cleared separately.
8340
+
8300
8341
  **sendMagicLink**(clientId: string, data: { email: string; redirectUrl: string; accountData?: Record<string, any> }) → `Promise<MagicLinkSendResponse>`
8301
8342
  Send a magic link email to the user (public).
8302
8343
 
@@ -8337,19 +8378,19 @@ Upsert contact identity after lightweight verification (public).
8337
8378
  Upsert contact identity after lightweight verification (public).
8338
8379
 
8339
8380
  **completePasswordReset**(clientId: string, token: string, newPassword: string) → `Promise<PasswordResetCompleteResponse>`
8340
- Upsert contact identity after lightweight verification (public).
8381
+ Complete a password reset / invite acceptance. On invite acceptance under `verify-auto-login` the server returns a session — adopt it so the caller is logged straight in (plain resets return no token and leave the bearer untouched).
8341
8382
 
8342
8383
  **sendEmailVerification**(clientId: string, data: { userId: string; email: string; redirectUrl?: string; clientName?: string }) → `Promise<EmailVerificationActionResponse>`
8343
- Upsert contact identity after lightweight verification (public).
8384
+ Complete a password reset / invite acceptance. On invite acceptance under `verify-auto-login` the server returns a session — adopt it so the caller is logged straight in (plain resets return no token and leave the bearer untouched).
8344
8385
 
8345
8386
  **verifyEmail**(clientId: string, token: string) → `Promise<EmailVerifyTokenResponse>`
8346
- Upsert contact identity after lightweight verification (public).
8387
+ Verify an email token; under `verify-auto-login` the server returns a session — adopt it.
8347
8388
 
8348
8389
  **resendEmailVerification**(clientId: string, data: { userId: string; email: string; redirectUrl?: string; clientName?: string }) → `Promise<EmailVerificationActionResponse>`
8349
- Upsert contact identity after lightweight verification (public).
8390
+ Verify an email token; under `verify-auto-login` the server returns a session — adopt it.
8350
8391
 
8351
8392
  **getProfile**(clientId: string) → `Promise<UserProfile>`
8352
- Upsert contact identity after lightweight verification (public).
8393
+ Verify an email token; under `verify-auto-login` the server returns a session — adopt it.
8353
8394
 
8354
8395
  **updateProfile**(clientId: string, data: ProfileUpdateData) → `Promise<UpdateProfileResponse>`
8355
8396
  Update the authenticated user's profile and replace the bearer token when refreshed claims are returned.
@@ -258,6 +258,50 @@ try {
258
258
  > ⚠️ This is a behaviour change for `googleLogin`, which previously merged silently in
259
259
  > this case. Handle the 409 for both methods.
260
260
 
261
+ ### Refresh tokens (native sessions)
262
+
263
+ Native/Capacitor hosts can hold long-lived sessions via refresh tokens. Opt in **once**
264
+ at startup so every request carries `X-Client-Platform: native`:
265
+
266
+ ```ts
267
+ initializeApi({ baseURL, platform: 'native' });
268
+ ```
269
+
270
+ With the opt-in active, the login endpoints (`login`, `register` in immediate mode,
271
+ `googleLogin`, `appleLogin`, `verifyPhoneCode`, `verifyMagicLink`,
272
+ `exchangeWhatsAppSession`) additionally return `refreshToken`, `refreshTokenExpiresAt`
273
+ (absolute, fixed, ms epoch), and a **short-lived** access `token`. Web clients are
274
+ unaffected and receive the unchanged response.
275
+
276
+ ```ts
277
+ // Later — exchange the refresh token for a fresh access token.
278
+ const r = await authKit.refreshToken(clientId, storedRefreshToken);
279
+ // r.refreshToken is ROTATED — persist it and discard the old one BEFORE the next call.
280
+ // The SDK already swapped in r.token as the active bearer for you.
281
+ persist(r.refreshToken, r.refreshTokenExpiresAt);
282
+
283
+ // On explicit sign-out — revokes the whole device family server-side + clears the bearer.
284
+ await authKit.logout(clientId, storedRefreshToken);
285
+ clearPersistedTokens();
286
+ ```
287
+
288
+ **Rotation rules the host MUST respect:**
289
+
290
+ 1. **Single-use + rotation.** Every `refreshToken()` returns a new token. Persist it and
291
+ overwrite the old one *before* the next call.
292
+ 2. **Serialize refreshes.** Fire a single in-flight refresh and queue callers behind it —
293
+ the SDK does **not** serialize for you, and racing two refreshes spends the same token.
294
+ 3. **Reuse = family death.** Replaying a consumed token throws
295
+ `SmartlinksApiError` with `errorCode === 'REFRESH_TOKEN_REUSE_DETECTED'`; treat as a
296
+ hard logout (clear storage, force re-login). `INVALID_REFRESH_TOKEN` (expired/revoked)
297
+ is handled the same way.
298
+ 4. **Absolute expiry is fixed.** `refreshTokenExpiresAt` never moves on rotation; once it
299
+ passes (default 90 days) the user must log in again.
300
+
301
+ > Resume-refresh scheduling and transparent refresh-on-401 are the responsibility of the
302
+ > host/auth-ui layer, not this SDK — the SDK only exposes the `refreshToken()` / `logout()`
303
+ > primitives and the `platform` opt-in.
304
+
261
305
  ---
262
306
 
263
307
  ## Profile management
package/dist/http.d.ts CHANGED
@@ -14,6 +14,14 @@ export declare function initializeApi(options: {
14
14
  proxyMode?: boolean;
15
15
  ngrokSkipBrowserWarning?: boolean;
16
16
  extraHeaders?: Record<string, string>;
17
+ /**
18
+ * Declares the host platform. Set to `'native'` on native/Capacitor hosts to opt into
19
+ * AuthKit refresh tokens — the SDK then sends `X-Client-Platform: native` on every
20
+ * request, so login endpoints return `refreshToken`/`refreshTokenExpiresAt` and a
21
+ * short-lived access token. Omit (or `'web'`) for standard web behaviour. Preserved
22
+ * across re-initialization when not supplied.
23
+ */
24
+ platform?: 'native' | 'web';
17
25
  iframeAutoResize?: boolean;
18
26
  logger?: Logger;
19
27
  /**
package/dist/http.js CHANGED
@@ -33,6 +33,12 @@ let bearerToken = undefined;
33
33
  let proxyMode = false;
34
34
  let ngrokSkipBrowserWarning = false;
35
35
  let extraHeadersGlobal = {};
36
+ /**
37
+ * Client platform opt-in. When set to 'native', every request carries the
38
+ * `X-Client-Platform: native` header, which tells AuthKit login endpoints to
39
+ * issue refresh tokens and short-lived access tokens. Undefined → web behaviour.
40
+ */
41
+ let clientPlatform = undefined;
36
42
  /** Whether initializeApi has been successfully called at least once. */
37
43
  let initialized = false;
38
44
  /** Safely returns the current browser hostname, or an empty string in non-browser / Node environments. */
@@ -395,6 +401,11 @@ export function initializeApi(options) {
395
401
  ? !!options.ngrokSkipBrowserWarning
396
402
  : inferredNgrok;
397
403
  extraHeadersGlobal = options.extraHeaders ? Object.assign({}, options.extraHeaders) : {};
404
+ // Platform opt-in (native refresh tokens). Preserve an earlier value when a
405
+ // re-init call omits it, so long-lived native sessions aren't downgraded by a
406
+ // later bootstrap that forgot to pass platform.
407
+ if (options.platform !== undefined)
408
+ clientPlatform = options.platform;
398
409
  // Auto-enable iframe resize unless explicitly disabled
399
410
  if (iframe.isIframe() && options.iframeAutoResize !== false) {
400
411
  iframe.enableAutoIframeResize();
@@ -1084,6 +1095,8 @@ export async function request(path) {
1084
1095
  headers["X-API-Key"] = apiKey;
1085
1096
  if (bearerToken)
1086
1097
  headers["AUTHORIZATION"] = `Bearer ${bearerToken}`;
1098
+ if (clientPlatform)
1099
+ headers["X-Client-Platform"] = clientPlatform;
1087
1100
  if (ngrokSkipBrowserWarning)
1088
1101
  headers["ngrok-skip-browser-warning"] = "true";
1089
1102
  const _getDomain = getSourceDomain();
@@ -1157,6 +1170,8 @@ export async function post(path, body, extraHeaders) {
1157
1170
  headers["X-API-Key"] = apiKey;
1158
1171
  if (bearerToken)
1159
1172
  headers["AUTHORIZATION"] = `Bearer ${bearerToken}`;
1173
+ if (clientPlatform)
1174
+ headers["X-Client-Platform"] = clientPlatform;
1160
1175
  if (ngrokSkipBrowserWarning)
1161
1176
  headers["ngrok-skip-browser-warning"] = "true";
1162
1177
  const _postDomain = getSourceDomain();
@@ -1214,6 +1229,8 @@ export async function put(path, body, extraHeaders) {
1214
1229
  headers["X-API-Key"] = apiKey;
1215
1230
  if (bearerToken)
1216
1231
  headers["AUTHORIZATION"] = `Bearer ${bearerToken}`;
1232
+ if (clientPlatform)
1233
+ headers["X-Client-Platform"] = clientPlatform;
1217
1234
  if (ngrokSkipBrowserWarning)
1218
1235
  headers["ngrok-skip-browser-warning"] = "true";
1219
1236
  const _putDomain = getSourceDomain();
@@ -1271,6 +1288,8 @@ export async function patch(path, body, extraHeaders) {
1271
1288
  headers["X-API-Key"] = apiKey;
1272
1289
  if (bearerToken)
1273
1290
  headers["AUTHORIZATION"] = `Bearer ${bearerToken}`;
1291
+ if (clientPlatform)
1292
+ headers["X-Client-Platform"] = clientPlatform;
1274
1293
  if (ngrokSkipBrowserWarning)
1275
1294
  headers["ngrok-skip-browser-warning"] = "true";
1276
1295
  const _patchDomain = getSourceDomain();
@@ -1375,7 +1394,7 @@ export async function requestWithOptions(path, options) {
1375
1394
  }
1376
1395
  }
1377
1396
  const _rwoDomain = getSourceDomain();
1378
- const headers = Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ "Content-Type": "application/json" }, (apiKey ? { "X-API-Key": apiKey } : {})), (bearerToken ? { "AUTHORIZATION": `Bearer ${bearerToken}` } : {})), (ngrokSkipBrowserWarning ? { "ngrok-skip-browser-warning": "true" } : {})), (_rwoDomain ? { "X-Source-Domain": _rwoDomain } : {})), extraHeaders);
1397
+ const headers = Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ "Content-Type": "application/json" }, (apiKey ? { "X-API-Key": apiKey } : {})), (bearerToken ? { "AUTHORIZATION": `Bearer ${bearerToken}` } : {})), (clientPlatform ? { "X-Client-Platform": clientPlatform } : {})), (ngrokSkipBrowserWarning ? { "ngrok-skip-browser-warning": "true" } : {})), (_rwoDomain ? { "X-Source-Domain": _rwoDomain } : {})), extraHeaders);
1379
1398
  // Merge global custom headers (do not override existing keys from options.headers)
1380
1399
  for (const [k, v] of Object.entries(extraHeadersGlobal))
1381
1400
  if (!(k in headers))
@@ -1479,6 +1498,8 @@ export async function del(path, extraHeaders) {
1479
1498
  headers["X-API-Key"] = apiKey;
1480
1499
  if (bearerToken)
1481
1500
  headers["AUTHORIZATION"] = `Bearer ${bearerToken}`;
1501
+ if (clientPlatform)
1502
+ headers["X-Client-Platform"] = clientPlatform;
1482
1503
  if (ngrokSkipBrowserWarning)
1483
1504
  headers["ngrok-skip-browser-warning"] = "true";
1484
1505
  const _delDomain = getSourceDomain();
@@ -1519,6 +1540,8 @@ export function getApiHeaders() {
1519
1540
  headers["X-API-Key"] = apiKey;
1520
1541
  if (bearerToken)
1521
1542
  headers["AUTHORIZATION"] = `Bearer ${bearerToken}`;
1543
+ if (clientPlatform)
1544
+ headers["X-Client-Platform"] = clientPlatform;
1522
1545
  if (ngrokSkipBrowserWarning)
1523
1546
  headers["ngrok-skip-browser-warning"] = "true";
1524
1547
  const sourceDomain = getSourceDomain();
package/dist/index.d.ts CHANGED
@@ -25,4 +25,4 @@ AdminMobileHostContext, AdminMobileComponentManifest, AdminMobileBundleManifest,
25
25
  MobileAdminBundleManifest, } from './mobile-admin/types';
26
26
  export { HostCapabilityUnavailableError, HostPermissionDeniedError, HostTimeoutError, } from './mobile-admin/errors';
27
27
  export type { NativeCapability, NativeFacade, ShareFacade, ClipboardFacade, HapticImpactStyle, HapticNotificationStyle, HapticsFacade, NetworkStatus, NetworkFacade, DeviceInfo, DeviceFacade, StorageFacade, QrScanOptions, QrFacade, AuthFacade, NfcReadResult, NfcFacade, RfidScanOptions, RfidFacade, EventsFacade, WebSourceMode, WebSourceConfig, WebSourceFacade, } from './native/types';
28
- export type { AuthKitUser, UserProfile, ProfileUpdateData, UpdateProfileResponse, SuccessResponse, AuthLoginResponse, AppleLoginOptions, AuthKitErrorCode, MagicLinkSendResponse, MagicLinkVerifyResponse, PhoneSendCodeResponse, PhoneVerifyResponse, PasswordResetRequestResponse, VerifyResetTokenResponse, PasswordResetCompleteResponse, EmailVerificationActionResponse, EmailVerifyTokenResponse, VerifyStatus, WhatsAppReplyCta, WhatsAppReplyOptions, WhatsAppContactData, SendWhatsAppRequest, SendWhatsAppResponse, ExchangeWhatsAppSessionResponse, VerifyWhatsAppResponse, WhatsAppStatusResponse, SendSmsVerifyRequest, SendSmsVerifyResponse, VerifySmsResponse, UpsertContactRequest, UpsertContactResponse, AuthKitBrandingConfig, AuthKitConfig, } from './types/authKit';
28
+ export type { AuthKitUser, UserProfile, ProfileUpdateData, UpdateProfileResponse, SuccessResponse, AuthLoginResponse, AppleLoginOptions, AuthKitErrorCode, RefreshResponse, LogoutResponse, RefreshErrorCode, MagicLinkSendResponse, MagicLinkVerifyResponse, PhoneSendCodeResponse, PhoneVerifyResponse, PasswordResetRequestResponse, VerifyResetTokenResponse, PasswordResetCompleteResponse, EmailVerificationActionResponse, EmailVerifyTokenResponse, VerifyStatus, WhatsAppReplyCta, WhatsAppReplyOptions, WhatsAppContactData, SendWhatsAppRequest, SendWhatsAppResponse, ExchangeWhatsAppSessionResponse, VerifyWhatsAppResponse, WhatsAppStatusResponse, SendSmsVerifyRequest, SendSmsVerifyResponse, VerifySmsResponse, UpsertContactRequest, UpsertContactResponse, AuthKitBrandingConfig, AuthKitConfig, } from './types/authKit';
package/dist/openapi.yaml CHANGED
@@ -8071,7 +8071,7 @@ paths:
8071
8071
  get:
8072
8072
  tags:
8073
8073
  - authKit
8074
- summary: authKit.getProfile
8074
+ summary: "Verify an email token; under `verify-auto-login` the server returns a session — adopt it."
8075
8075
  operationId: authKit_getProfile
8076
8076
  security: []
8077
8077
  parameters:
@@ -8213,7 +8213,7 @@ paths:
8213
8213
  post:
8214
8214
  tags:
8215
8215
  - authKit
8216
- summary: authKit.completePasswordReset
8216
+ summary: Complete a password reset / invite acceptance.
8217
8217
  operationId: authKit_completePasswordReset
8218
8218
  security: []
8219
8219
  parameters:
@@ -8313,6 +8313,32 @@ paths:
8313
8313
  description: Unauthorized
8314
8314
  404:
8315
8315
  description: Not found
8316
+ /authkit/{clientId}/auth/logout:
8317
+ post:
8318
+ tags:
8319
+ - authKit
8320
+ summary: "Revoke a refresh token's entire family server-side (that device's whole rotation chain) and clear the in-memory bearer token."
8321
+ operationId: authKit_logout
8322
+ security: []
8323
+ parameters:
8324
+ - name: clientId
8325
+ in: path
8326
+ required: true
8327
+ schema:
8328
+ type: string
8329
+ responses:
8330
+ 200:
8331
+ description: Success
8332
+ content:
8333
+ application/json:
8334
+ schema:
8335
+ $ref: "#/components/schemas/LogoutResponse"
8336
+ 400:
8337
+ description: Bad request
8338
+ 401:
8339
+ description: Unauthorized
8340
+ 404:
8341
+ description: Not found
8316
8342
  /authkit/{clientId}/auth/magic-link/send:
8317
8343
  post:
8318
8344
  tags:
@@ -8424,6 +8450,32 @@ paths:
8424
8450
  description: Unauthorized
8425
8451
  404:
8426
8452
  description: Not found
8453
+ /authkit/{clientId}/auth/refresh:
8454
+ post:
8455
+ tags:
8456
+ - authKit
8457
+ summary: authKit.refreshToken
8458
+ operationId: authKit_refreshToken
8459
+ security: []
8460
+ parameters:
8461
+ - name: clientId
8462
+ in: path
8463
+ required: true
8464
+ schema:
8465
+ type: string
8466
+ responses:
8467
+ 200:
8468
+ description: Success
8469
+ content:
8470
+ application/json:
8471
+ schema:
8472
+ $ref: "#/components/schemas/RefreshResponse"
8473
+ 400:
8474
+ description: Bad request
8475
+ 401:
8476
+ description: Unauthorized
8477
+ 404:
8478
+ description: Not found
8427
8479
  /authkit/{clientId}/auth/register:
8428
8480
  post:
8429
8481
  tags:
@@ -8527,7 +8579,7 @@ paths:
8527
8579
  post:
8528
8580
  tags:
8529
8581
  - authKit
8530
- summary: authKit.sendEmailVerification
8582
+ summary: Complete a password reset / invite acceptance.
8531
8583
  operationId: authKit_sendEmailVerification
8532
8584
  security: []
8533
8585
  parameters:
@@ -8618,7 +8670,7 @@ paths:
8618
8670
  post:
8619
8671
  tags:
8620
8672
  - authKit
8621
- summary: authKit.verifyEmail
8673
+ summary: "Verify an email token; under `verify-auto-login` the server returns a session — adopt it."
8622
8674
  operationId: authKit_verifyEmail
8623
8675
  security: []
8624
8676
  parameters:
@@ -17965,8 +18017,42 @@ components:
17965
18017
  type: boolean
17966
18018
  expiresAt:
17967
18019
  type: number
18020
+ refreshToken:
18021
+ type: string
18022
+ refreshTokenExpiresAt:
18023
+ type: number
18024
+ required:
18025
+ - user
18026
+ RefreshResponse:
18027
+ type: object
18028
+ properties:
18029
+ token:
18030
+ type: string
18031
+ refreshToken:
18032
+ type: string
18033
+ refreshTokenExpiresAt:
18034
+ type: number
18035
+ expiresAt:
18036
+ type: number
18037
+ user:
18038
+ $ref: "#/components/schemas/AuthKitUser"
18039
+ accountData:
18040
+ type: object
18041
+ additionalProperties: true
17968
18042
  required:
18043
+ - token
18044
+ - refreshToken
18045
+ - refreshTokenExpiresAt
18046
+ - expiresAt
17969
18047
  - user
18048
+ LogoutResponse:
18049
+ type: object
18050
+ properties:
18051
+ success:
18052
+ type: object
18053
+ additionalProperties: true
18054
+ required:
18055
+ - success
17970
18056
  AppleLoginOptions:
17971
18057
  type: object
17972
18058
  properties:
@@ -18040,6 +18126,25 @@ components:
18040
18126
  type: boolean
18041
18127
  message:
18042
18128
  type: string
18129
+ token:
18130
+ type: string
18131
+ user:
18132
+ $ref: "#/components/schemas/AuthKitUser"
18133
+ accountData:
18134
+ type: object
18135
+ additionalProperties: true
18136
+ emailVerificationMode:
18137
+ type: string
18138
+ enum:
18139
+ - immediate
18140
+ - verify-auto-login
18141
+ - verify-manual-login
18142
+ refreshToken:
18143
+ type: string
18144
+ refreshTokenExpiresAt:
18145
+ type: number
18146
+ expiresAt:
18147
+ type: number
18043
18148
  required:
18044
18149
  - success
18045
18150
  - message
@@ -48,7 +48,49 @@ export interface AuthLoginResponse {
48
48
  * login endpoint.
49
49
  */
50
50
  expiresAt?: number | null;
51
+ /**
52
+ * Opaque, single-use refresh token. **Native clients only** — present only when the
53
+ * request opted in via `initializeApi({ platform: 'native' })` (or the
54
+ * `X-Client-Platform: native` header). For native logins, `token` above is the
55
+ * short-lived access token; pair it with this refresh token. Undefined for web.
56
+ */
57
+ refreshToken?: string;
58
+ /**
59
+ * Absolute expiry of the refresh-token family, in **milliseconds since epoch**.
60
+ * Fixed at login — it does **not** move when the token is rotated. Native only.
61
+ */
62
+ refreshTokenExpiresAt?: number;
51
63
  }
64
+ /**
65
+ * Response from {@link authKit.refreshToken}. The `refreshToken` is **rotated** on every
66
+ * call — persist the new value and discard the old one before refreshing again.
67
+ */
68
+ export interface RefreshResponse {
69
+ /** New short-lived access token (a `SL.` bearer JWT). */
70
+ token: string;
71
+ /** Rotated refresh token — replace the stored value with this. */
72
+ refreshToken: string;
73
+ /** Absolute family expiry (ms epoch). Unchanged across rotations. */
74
+ refreshTokenExpiresAt: number;
75
+ /** New access-token expiry (ms epoch). */
76
+ expiresAt: number;
77
+ user: AuthKitUser;
78
+ accountData?: Record<string, any>;
79
+ }
80
+ /** Response from {@link authKit.logout}. Idempotent — always `{ success: true }`. */
81
+ export interface LogoutResponse {
82
+ success: true;
83
+ }
84
+ /**
85
+ * Server-defined error codes for the refresh/logout flow, surfaced via
86
+ * `SmartlinksApiError.errorCode`.
87
+ *
88
+ * - `MISSING_REFRESH_TOKEN` (400) — programming error; no token sent.
89
+ * - `INVALID_REFRESH_TOKEN` (401) — unknown / expired / revoked / wrong client → `logout()` + route to login.
90
+ * - `REFRESH_TOKEN_REUSE_DETECTED` (401) — a consumed token was replayed; the **entire session
91
+ * family was revoked server-side**. Hard logout: clear storage, force re-login.
92
+ */
93
+ export type RefreshErrorCode = 'MISSING_REFRESH_TOKEN' | 'INVALID_REFRESH_TOKEN' | 'REFRESH_TOKEN_REUSE_DETECTED';
52
94
  /**
53
95
  * Options for {@link authKit.appleLogin}. All fields are optional — only the
54
96
  * `identityToken` (passed as a positional argument) is required by the server.
@@ -102,9 +144,36 @@ export interface VerifyResetTokenResponse {
102
144
  expiresAt?: number;
103
145
  message?: string;
104
146
  }
147
+ /**
148
+ * Response from {@link authKit.completePasswordReset}.
149
+ *
150
+ * For a **plain password reset**, or an invite accepted under `verify-manual-login`,
151
+ * the server returns only `success` + `message` and the user is sent to the login screen.
152
+ *
153
+ * For **invite acceptance under `verify-auto-login`** (the default), the server also
154
+ * completes email verification and returns a full session — `token`, `user`, and
155
+ * `accountData` — so the user can be logged straight in, mirroring
156
+ * {@link EmailVerifyTokenResponse}. The native-only fields are populated when the request
157
+ * opted in via `initializeApi({ platform: 'native' })` (the `X-Client-Platform: native`
158
+ * header). Every session field is therefore optional.
159
+ */
105
160
  export interface PasswordResetCompleteResponse {
106
161
  success: boolean;
107
162
  message: string;
163
+ /** Session token (a `SL.` bearer JWT). Present only on invite auto-login. Adopt it and skip the login screen. */
164
+ token?: string;
165
+ /** The authenticated user. Present only when a `token` is returned. `emailVerified` is `true` once completion verifies the invite. */
166
+ user?: AuthKitUser;
167
+ /** The `clients[clientId]` account-data slice, or `null`. Present only when a `token` is returned. */
168
+ accountData?: Record<string, any> | null;
169
+ /** Configured verification mode echoed back; auto-login only occurs for `verify-auto-login`. */
170
+ emailVerificationMode?: 'immediate' | 'verify-auto-login' | 'verify-manual-login';
171
+ /** Opaque, single-use refresh token. **Native clients only** — pairs with the short-lived `token`. */
172
+ refreshToken?: string;
173
+ /** Absolute expiry of the refresh-token family, in **ms since epoch**. Native only. */
174
+ refreshTokenExpiresAt?: number;
175
+ /** Access-token expiry, in **ms since epoch**. Native only. */
176
+ expiresAt?: number;
108
177
  }
109
178
  export interface EmailVerificationActionResponse {
110
179
  success: boolean;
@@ -1,6 +1,6 @@
1
1
  # Smartlinks API Summary
2
2
 
3
- Version: 1.14.15 | Generated: 2026-05-31T15:31:06.561Z
3
+ Version: 1.14.17 | Generated: 2026-06-02T16:17:46.100Z
4
4
 
5
5
  This is a concise summary of all available API functions and types.
6
6
 
@@ -152,12 +152,11 @@ Return whether proxy mode is currently enabled.
152
152
  proxyMode?: boolean
153
153
  ngrokSkipBrowserWarning?: boolean
154
154
  extraHeaders?: Record<string, string>
155
- iframeAutoResize?: boolean // default true when in iframe
156
- logger?: Logger // optional console-like or function to enable verbose logging
157
155
  /**
158
- * When true, the bearer token is automatically saved to localStorage after login
159
- * and restored on the next page load. Eliminates the need to manually persist
160
- * the token across refreshes. Clear it by calling setBearerToken(undefined) → `void`
156
+ * Declares the host platform. Set to `'native'` on native/Capacitor hosts to opt into
157
+ * AuthKit refresh tokens the SDK then sends `X-Client-Platform: native` on every
158
+ * request, so login endpoints return `refreshToken`/`refreshTokenExpiresAt` and a
159
+ * short-lived access token. Omit (or `'web'`) → `void`
161
160
  Call this once (e.g. at app startup) to configure baseURL/auth.
162
161
 
163
162
  **setNgrokSkipBrowserWarning**(flag: boolean) → `void`
@@ -2963,6 +2962,33 @@ interface AuthLoginResponse {
2963
2962
  * or null when the server could not decode it. Currently only populated by the Apple
2964
2963
  * login endpoint.
2965
2964
  expiresAt?: number | null
2965
+ * Opaque, single-use refresh token. **Native clients only** — present only when the
2966
+ * request opted in via `initializeApi({ platform: 'native' })` (or the
2967
+ * `X-Client-Platform: native` header). For native logins, `token` above is the
2968
+ * short-lived access token; pair it with this refresh token. Undefined for web.
2969
+ refreshToken?: string
2970
+ * Absolute expiry of the refresh-token family, in **milliseconds since epoch**.
2971
+ * Fixed at login — it does **not** move when the token is rotated. Native only.
2972
+ refreshTokenExpiresAt?: number
2973
+ }
2974
+ ```
2975
+
2976
+ **RefreshResponse** (interface)
2977
+ ```typescript
2978
+ interface RefreshResponse {
2979
+ token: string
2980
+ refreshToken: string
2981
+ refreshTokenExpiresAt: number
2982
+ expiresAt: number
2983
+ user: AuthKitUser
2984
+ accountData?: Record<string, any>
2985
+ }
2986
+ ```
2987
+
2988
+ **LogoutResponse** (interface)
2989
+ ```typescript
2990
+ interface LogoutResponse {
2991
+ success: true
2966
2992
  }
2967
2993
  ```
2968
2994
 
@@ -3027,6 +3053,13 @@ interface VerifyResetTokenResponse {
3027
3053
  interface PasswordResetCompleteResponse {
3028
3054
  success: boolean
3029
3055
  message: string
3056
+ token?: string
3057
+ user?: AuthKitUser
3058
+ accountData?: Record<string, any> | null
3059
+ emailVerificationMode?: 'immediate' | 'verify-auto-login' | 'verify-manual-login'
3060
+ refreshToken?: string
3061
+ refreshTokenExpiresAt?: number
3062
+ expiresAt?: number
3030
3063
  }
3031
3064
  ```
3032
3065
 
@@ -3218,6 +3251,8 @@ interface AuthKitConfig {
3218
3251
  }
3219
3252
  ```
3220
3253
 
3254
+ **RefreshErrorCode** = ``
3255
+
3221
3256
  **AuthKitErrorCode** = ``
3222
3257
 
3223
3258
  **VerifyStatus** = `'pending' | 'verified' | 'failed' | 'expired' | 'unknown'`
@@ -8297,6 +8332,12 @@ Google OAuth login via server-side authorization code (public).
8297
8332
  **appleLogin**(clientId: string, identityToken: string, opts?: AppleLoginOptions) → `Promise<AuthLoginResponse>`
8298
8333
  Sign in with Apple via an Apple identity token (public). Mirrors {@link googleLogin}. On success the returned bearer token is stored automatically and the cache is invalidated. Notable error codes (thrown as `SmartlinksApiError`, read via `err.errorCode`): - `MISSING_APPLE_TOKEN` (400), `APPLE_AUTH_NOT_CONFIGURED` (400), `INVALID_APPLE_TOKEN` (401), `APPLE_AUTH_FAILED` (500) - `ACCOUNT_EXISTS_UNVERIFIED` (409) — an unverified account already owns this email; the server refuses to silently link. `err.details.requiresEmailVerification` is `true`. Recoverable: the user should sign in with their password (or reset it), then link Apple from settings. **The same 409 can now come back from {@link googleLogin}** under the shared verified-to-verified linking policy.
8299
8334
 
8335
+ **refreshToken**(clientId: string, refreshToken: string) → `Promise<RefreshResponse>`
8336
+ Exchange a refresh token for a fresh access token (public — the refresh token IS the credential). **Native sessions only**; refresh tokens are issued only when the host opted in via `initializeApi({ platform: 'native' })`. On success the new access token is stored automatically (`setBearerToken`). The returned `refreshToken` is **rotated** — the caller must persist it and discard the old one before refreshing again. ⚠️ **Single-use, no retry, serialize calls.** This method issues exactly one request and never retries: replaying a consumed refresh token triggers `REFRESH_TOKEN_REUSE_DETECTED` (the whole session family is revoked). The caller is responsible for ensuring only one refresh is in flight at a time (e.g. across tabs or resume events). Errors (thrown as `SmartlinksApiError`, read via `err.errorCode`): `MISSING_REFRESH_TOKEN` (400), `INVALID_REFRESH_TOKEN` (401), `REFRESH_TOKEN_REUSE_DETECTED` (401) — the last two mean a hard logout.
8337
+
8338
+ **logout**(clientId: string, refreshToken: string) → `Promise<LogoutResponse>`
8339
+ Revoke a refresh token's entire family server-side (that device's whole rotation chain) and clear the in-memory bearer token. Idempotent — always resolves to `{ success: true }`, never revealing whether the token existed. Call on explicit sign-out. Persisted tokens in the host's own storage must be cleared separately.
8340
+
8300
8341
  **sendMagicLink**(clientId: string, data: { email: string; redirectUrl: string; accountData?: Record<string, any> }) → `Promise<MagicLinkSendResponse>`
8301
8342
  Send a magic link email to the user (public).
8302
8343
 
@@ -8337,19 +8378,19 @@ Upsert contact identity after lightweight verification (public).
8337
8378
  Upsert contact identity after lightweight verification (public).
8338
8379
 
8339
8380
  **completePasswordReset**(clientId: string, token: string, newPassword: string) → `Promise<PasswordResetCompleteResponse>`
8340
- Upsert contact identity after lightweight verification (public).
8381
+ Complete a password reset / invite acceptance. On invite acceptance under `verify-auto-login` the server returns a session — adopt it so the caller is logged straight in (plain resets return no token and leave the bearer untouched).
8341
8382
 
8342
8383
  **sendEmailVerification**(clientId: string, data: { userId: string; email: string; redirectUrl?: string; clientName?: string }) → `Promise<EmailVerificationActionResponse>`
8343
- Upsert contact identity after lightweight verification (public).
8384
+ Complete a password reset / invite acceptance. On invite acceptance under `verify-auto-login` the server returns a session — adopt it so the caller is logged straight in (plain resets return no token and leave the bearer untouched).
8344
8385
 
8345
8386
  **verifyEmail**(clientId: string, token: string) → `Promise<EmailVerifyTokenResponse>`
8346
- Upsert contact identity after lightweight verification (public).
8387
+ Verify an email token; under `verify-auto-login` the server returns a session — adopt it.
8347
8388
 
8348
8389
  **resendEmailVerification**(clientId: string, data: { userId: string; email: string; redirectUrl?: string; clientName?: string }) → `Promise<EmailVerificationActionResponse>`
8349
- Upsert contact identity after lightweight verification (public).
8390
+ Verify an email token; under `verify-auto-login` the server returns a session — adopt it.
8350
8391
 
8351
8392
  **getProfile**(clientId: string) → `Promise<UserProfile>`
8352
- Upsert contact identity after lightweight verification (public).
8393
+ Verify an email token; under `verify-auto-login` the server returns a session — adopt it.
8353
8394
 
8354
8395
  **updateProfile**(clientId: string, data: ProfileUpdateData) → `Promise<UpdateProfileResponse>`
8355
8396
  Update the authenticated user's profile and replace the bearer token when refreshed claims are returned.
package/docs/auth-kit.md CHANGED
@@ -258,6 +258,50 @@ try {
258
258
  > ⚠️ This is a behaviour change for `googleLogin`, which previously merged silently in
259
259
  > this case. Handle the 409 for both methods.
260
260
 
261
+ ### Refresh tokens (native sessions)
262
+
263
+ Native/Capacitor hosts can hold long-lived sessions via refresh tokens. Opt in **once**
264
+ at startup so every request carries `X-Client-Platform: native`:
265
+
266
+ ```ts
267
+ initializeApi({ baseURL, platform: 'native' });
268
+ ```
269
+
270
+ With the opt-in active, the login endpoints (`login`, `register` in immediate mode,
271
+ `googleLogin`, `appleLogin`, `verifyPhoneCode`, `verifyMagicLink`,
272
+ `exchangeWhatsAppSession`) additionally return `refreshToken`, `refreshTokenExpiresAt`
273
+ (absolute, fixed, ms epoch), and a **short-lived** access `token`. Web clients are
274
+ unaffected and receive the unchanged response.
275
+
276
+ ```ts
277
+ // Later — exchange the refresh token for a fresh access token.
278
+ const r = await authKit.refreshToken(clientId, storedRefreshToken);
279
+ // r.refreshToken is ROTATED — persist it and discard the old one BEFORE the next call.
280
+ // The SDK already swapped in r.token as the active bearer for you.
281
+ persist(r.refreshToken, r.refreshTokenExpiresAt);
282
+
283
+ // On explicit sign-out — revokes the whole device family server-side + clears the bearer.
284
+ await authKit.logout(clientId, storedRefreshToken);
285
+ clearPersistedTokens();
286
+ ```
287
+
288
+ **Rotation rules the host MUST respect:**
289
+
290
+ 1. **Single-use + rotation.** Every `refreshToken()` returns a new token. Persist it and
291
+ overwrite the old one *before* the next call.
292
+ 2. **Serialize refreshes.** Fire a single in-flight refresh and queue callers behind it —
293
+ the SDK does **not** serialize for you, and racing two refreshes spends the same token.
294
+ 3. **Reuse = family death.** Replaying a consumed token throws
295
+ `SmartlinksApiError` with `errorCode === 'REFRESH_TOKEN_REUSE_DETECTED'`; treat as a
296
+ hard logout (clear storage, force re-login). `INVALID_REFRESH_TOKEN` (expired/revoked)
297
+ is handled the same way.
298
+ 4. **Absolute expiry is fixed.** `refreshTokenExpiresAt` never moves on rotation; once it
299
+ passes (default 90 days) the user must log in again.
300
+
301
+ > Resume-refresh scheduling and transparent refresh-on-401 are the responsibility of the
302
+ > host/auth-ui layer, not this SDK — the SDK only exposes the `refreshToken()` / `logout()`
303
+ > primitives and the `platform` opt-in.
304
+
261
305
  ---
262
306
 
263
307
  ## Profile management
package/openapi.yaml CHANGED
@@ -8071,7 +8071,7 @@ paths:
8071
8071
  get:
8072
8072
  tags:
8073
8073
  - authKit
8074
- summary: authKit.getProfile
8074
+ summary: "Verify an email token; under `verify-auto-login` the server returns a session — adopt it."
8075
8075
  operationId: authKit_getProfile
8076
8076
  security: []
8077
8077
  parameters:
@@ -8213,7 +8213,7 @@ paths:
8213
8213
  post:
8214
8214
  tags:
8215
8215
  - authKit
8216
- summary: authKit.completePasswordReset
8216
+ summary: Complete a password reset / invite acceptance.
8217
8217
  operationId: authKit_completePasswordReset
8218
8218
  security: []
8219
8219
  parameters:
@@ -8313,6 +8313,32 @@ paths:
8313
8313
  description: Unauthorized
8314
8314
  404:
8315
8315
  description: Not found
8316
+ /authkit/{clientId}/auth/logout:
8317
+ post:
8318
+ tags:
8319
+ - authKit
8320
+ summary: "Revoke a refresh token's entire family server-side (that device's whole rotation chain) and clear the in-memory bearer token."
8321
+ operationId: authKit_logout
8322
+ security: []
8323
+ parameters:
8324
+ - name: clientId
8325
+ in: path
8326
+ required: true
8327
+ schema:
8328
+ type: string
8329
+ responses:
8330
+ 200:
8331
+ description: Success
8332
+ content:
8333
+ application/json:
8334
+ schema:
8335
+ $ref: "#/components/schemas/LogoutResponse"
8336
+ 400:
8337
+ description: Bad request
8338
+ 401:
8339
+ description: Unauthorized
8340
+ 404:
8341
+ description: Not found
8316
8342
  /authkit/{clientId}/auth/magic-link/send:
8317
8343
  post:
8318
8344
  tags:
@@ -8424,6 +8450,32 @@ paths:
8424
8450
  description: Unauthorized
8425
8451
  404:
8426
8452
  description: Not found
8453
+ /authkit/{clientId}/auth/refresh:
8454
+ post:
8455
+ tags:
8456
+ - authKit
8457
+ summary: authKit.refreshToken
8458
+ operationId: authKit_refreshToken
8459
+ security: []
8460
+ parameters:
8461
+ - name: clientId
8462
+ in: path
8463
+ required: true
8464
+ schema:
8465
+ type: string
8466
+ responses:
8467
+ 200:
8468
+ description: Success
8469
+ content:
8470
+ application/json:
8471
+ schema:
8472
+ $ref: "#/components/schemas/RefreshResponse"
8473
+ 400:
8474
+ description: Bad request
8475
+ 401:
8476
+ description: Unauthorized
8477
+ 404:
8478
+ description: Not found
8427
8479
  /authkit/{clientId}/auth/register:
8428
8480
  post:
8429
8481
  tags:
@@ -8527,7 +8579,7 @@ paths:
8527
8579
  post:
8528
8580
  tags:
8529
8581
  - authKit
8530
- summary: authKit.sendEmailVerification
8582
+ summary: Complete a password reset / invite acceptance.
8531
8583
  operationId: authKit_sendEmailVerification
8532
8584
  security: []
8533
8585
  parameters:
@@ -8618,7 +8670,7 @@ paths:
8618
8670
  post:
8619
8671
  tags:
8620
8672
  - authKit
8621
- summary: authKit.verifyEmail
8673
+ summary: "Verify an email token; under `verify-auto-login` the server returns a session — adopt it."
8622
8674
  operationId: authKit_verifyEmail
8623
8675
  security: []
8624
8676
  parameters:
@@ -17965,8 +18017,42 @@ components:
17965
18017
  type: boolean
17966
18018
  expiresAt:
17967
18019
  type: number
18020
+ refreshToken:
18021
+ type: string
18022
+ refreshTokenExpiresAt:
18023
+ type: number
18024
+ required:
18025
+ - user
18026
+ RefreshResponse:
18027
+ type: object
18028
+ properties:
18029
+ token:
18030
+ type: string
18031
+ refreshToken:
18032
+ type: string
18033
+ refreshTokenExpiresAt:
18034
+ type: number
18035
+ expiresAt:
18036
+ type: number
18037
+ user:
18038
+ $ref: "#/components/schemas/AuthKitUser"
18039
+ accountData:
18040
+ type: object
18041
+ additionalProperties: true
17968
18042
  required:
18043
+ - token
18044
+ - refreshToken
18045
+ - refreshTokenExpiresAt
18046
+ - expiresAt
17969
18047
  - user
18048
+ LogoutResponse:
18049
+ type: object
18050
+ properties:
18051
+ success:
18052
+ type: object
18053
+ additionalProperties: true
18054
+ required:
18055
+ - success
17970
18056
  AppleLoginOptions:
17971
18057
  type: object
17972
18058
  properties:
@@ -18040,6 +18126,25 @@ components:
18040
18126
  type: boolean
18041
18127
  message:
18042
18128
  type: string
18129
+ token:
18130
+ type: string
18131
+ user:
18132
+ $ref: "#/components/schemas/AuthKitUser"
18133
+ accountData:
18134
+ type: object
18135
+ additionalProperties: true
18136
+ emailVerificationMode:
18137
+ type: string
18138
+ enum:
18139
+ - immediate
18140
+ - verify-auto-login
18141
+ - verify-manual-login
18142
+ refreshToken:
18143
+ type: string
18144
+ refreshTokenExpiresAt:
18145
+ type: number
18146
+ expiresAt:
18147
+ type: number
18043
18148
  required:
18044
18149
  - success
18045
18150
  - message
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@proveanything/smartlinks",
3
- "version": "1.14.15",
3
+ "version": "1.14.17",
4
4
  "description": "Official JavaScript/TypeScript SDK for the Smartlinks API",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",