@proveanything/smartlinks 1.14.14 → 1.14.16
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/dist/api/authKit.d.ts +48 -1
- package/dist/api/authKit.js +73 -0
- package/dist/docs/API_SUMMARY.md +66 -6
- package/dist/docs/auth-kit.md +87 -0
- package/dist/http.d.ts +8 -0
- package/dist/http.js +24 -1
- package/dist/index.d.ts +1 -1
- package/dist/openapi.yaml +132 -0
- package/dist/types/authKit.d.ts +82 -0
- package/docs/API_SUMMARY.md +66 -6
- package/docs/auth-kit.md +87 -0
- package/openapi.yaml +132 -0
- package/package.json +1 -1
package/dist/api/authKit.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AuthLoginResponse, 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*).
|
|
@@ -17,6 +17,53 @@ export declare namespace authKit {
|
|
|
17
17
|
function googleLogin(clientId: string, idToken: string): Promise<AuthLoginResponse>;
|
|
18
18
|
/** Google OAuth login via server-side authorization code (public). */
|
|
19
19
|
function googleCodeLogin(clientId: string, code: string, redirectUri: string): Promise<AuthLoginResponse>;
|
|
20
|
+
/**
|
|
21
|
+
* Sign in with Apple via an Apple identity token (public).
|
|
22
|
+
*
|
|
23
|
+
* Mirrors {@link googleLogin}. On success the returned bearer token is stored
|
|
24
|
+
* automatically and the cache is invalidated.
|
|
25
|
+
*
|
|
26
|
+
* Notable error codes (thrown as `SmartlinksApiError`, read via `err.errorCode`):
|
|
27
|
+
* - `MISSING_APPLE_TOKEN` (400), `APPLE_AUTH_NOT_CONFIGURED` (400),
|
|
28
|
+
* `INVALID_APPLE_TOKEN` (401), `APPLE_AUTH_FAILED` (500)
|
|
29
|
+
* - `ACCOUNT_EXISTS_UNVERIFIED` (409) — an unverified account already owns this
|
|
30
|
+
* email; the server refuses to silently link. `err.details.requiresEmailVerification`
|
|
31
|
+
* is `true`. Recoverable: the user should sign in with their password (or reset it),
|
|
32
|
+
* then link Apple from settings. **The same 409 can now come back from
|
|
33
|
+
* {@link googleLogin}** under the shared verified-to-verified linking policy.
|
|
34
|
+
*
|
|
35
|
+
* @see AppleLoginOptions
|
|
36
|
+
*/
|
|
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>;
|
|
20
67
|
/** Send a magic link email to the user (public). */
|
|
21
68
|
function sendMagicLink(clientId: string, data: {
|
|
22
69
|
email: string;
|
package/dist/api/authKit.js
CHANGED
|
@@ -43,6 +43,79 @@ export var authKit;
|
|
|
43
43
|
return res;
|
|
44
44
|
}
|
|
45
45
|
authKit.googleCodeLogin = googleCodeLogin;
|
|
46
|
+
/**
|
|
47
|
+
* Sign in with Apple via an Apple identity token (public).
|
|
48
|
+
*
|
|
49
|
+
* Mirrors {@link googleLogin}. On success the returned bearer token is stored
|
|
50
|
+
* automatically and the cache is invalidated.
|
|
51
|
+
*
|
|
52
|
+
* Notable error codes (thrown as `SmartlinksApiError`, read via `err.errorCode`):
|
|
53
|
+
* - `MISSING_APPLE_TOKEN` (400), `APPLE_AUTH_NOT_CONFIGURED` (400),
|
|
54
|
+
* `INVALID_APPLE_TOKEN` (401), `APPLE_AUTH_FAILED` (500)
|
|
55
|
+
* - `ACCOUNT_EXISTS_UNVERIFIED` (409) — an unverified account already owns this
|
|
56
|
+
* email; the server refuses to silently link. `err.details.requiresEmailVerification`
|
|
57
|
+
* is `true`. Recoverable: the user should sign in with their password (or reset it),
|
|
58
|
+
* then link Apple from settings. **The same 409 can now come back from
|
|
59
|
+
* {@link googleLogin}** under the shared verified-to-verified linking policy.
|
|
60
|
+
*
|
|
61
|
+
* @see AppleLoginOptions
|
|
62
|
+
*/
|
|
63
|
+
async function appleLogin(clientId, identityToken, opts) {
|
|
64
|
+
const body = Object.assign({ identityToken }, opts);
|
|
65
|
+
const res = await post(`/authkit/${encodeURIComponent(clientId)}/auth/apple`, body);
|
|
66
|
+
if (res.token) {
|
|
67
|
+
setBearerToken(res.token);
|
|
68
|
+
invalidateCache();
|
|
69
|
+
}
|
|
70
|
+
return res;
|
|
71
|
+
}
|
|
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;
|
|
46
119
|
/** Send a magic link email to the user (public). */
|
|
47
120
|
async function sendMagicLink(clientId, data) {
|
|
48
121
|
return post(`/authkit/${encodeURIComponent(clientId)}/auth/magic-link/send`, data);
|
package/dist/docs/API_SUMMARY.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Smartlinks API Summary
|
|
2
2
|
|
|
3
|
-
Version: 1.14.
|
|
3
|
+
Version: 1.14.16 | Generated: 2026-06-01T09:33:51.336Z
|
|
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
|
-
*
|
|
159
|
-
*
|
|
160
|
-
*
|
|
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`
|
|
@@ -2956,6 +2955,54 @@ interface AuthLoginResponse {
|
|
|
2956
2955
|
requiresEmailVerification?: boolean // True if email verification is required but not yet completed
|
|
2957
2956
|
emailVerificationDeadline?: number // Unix timestamp - for 'immediate' mode grace period deadline
|
|
2958
2957
|
accountLocked?: boolean // True if account is locked due to expired verification deadline
|
|
2958
|
+
* True when this login created a brand-new account. Currently only populated by
|
|
2959
|
+
* the Apple login endpoint; left undefined by the other AuthKit login endpoints.
|
|
2960
|
+
isNewUser?: boolean
|
|
2961
|
+
* Session token expiry, in **milliseconds since epoch** (not seconds, not a duration),
|
|
2962
|
+
* or null when the server could not decode it. Currently only populated by the Apple
|
|
2963
|
+
* login endpoint.
|
|
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
|
|
2992
|
+
}
|
|
2993
|
+
```
|
|
2994
|
+
|
|
2995
|
+
**AppleLoginOptions** (interface)
|
|
2996
|
+
```typescript
|
|
2997
|
+
interface AppleLoginOptions {
|
|
2998
|
+
authorizationCode?: string
|
|
2999
|
+
* The **raw** nonce the client generated, if nonce binding was used. The server
|
|
3000
|
+
* accepts either `token.nonce === nonce` (native) or `token.nonce === sha256hex(nonce)` (web).
|
|
3001
|
+
nonce?: string
|
|
3002
|
+
* Name/email from Apple's **first** authorization callback only — Apple never returns
|
|
3003
|
+
* these again, and never inside the token. Forwarded so the server can persist the
|
|
3004
|
+
* display name on first account creation. Treated as untrusted (never used for identity).
|
|
3005
|
+
userInfo?: { email?: string; name?: string }
|
|
2959
3006
|
}
|
|
2960
3007
|
```
|
|
2961
3008
|
|
|
@@ -3197,6 +3244,10 @@ interface AuthKitConfig {
|
|
|
3197
3244
|
}
|
|
3198
3245
|
```
|
|
3199
3246
|
|
|
3247
|
+
**RefreshErrorCode** = ``
|
|
3248
|
+
|
|
3249
|
+
**AuthKitErrorCode** = ``
|
|
3250
|
+
|
|
3200
3251
|
**VerifyStatus** = `'pending' | 'verified' | 'failed' | 'expired' | 'unknown'`
|
|
3201
3252
|
|
|
3202
3253
|
### batch
|
|
@@ -8271,6 +8322,15 @@ Google OAuth login via ID token (public).
|
|
|
8271
8322
|
**googleCodeLogin**(clientId: string, code: string, redirectUri: string) → `Promise<AuthLoginResponse>`
|
|
8272
8323
|
Google OAuth login via server-side authorization code (public).
|
|
8273
8324
|
|
|
8325
|
+
**appleLogin**(clientId: string, identityToken: string, opts?: AppleLoginOptions) → `Promise<AuthLoginResponse>`
|
|
8326
|
+
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.
|
|
8327
|
+
|
|
8328
|
+
**refreshToken**(clientId: string, refreshToken: string) → `Promise<RefreshResponse>`
|
|
8329
|
+
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.
|
|
8330
|
+
|
|
8331
|
+
**logout**(clientId: string, refreshToken: string) → `Promise<LogoutResponse>`
|
|
8332
|
+
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.
|
|
8333
|
+
|
|
8274
8334
|
**sendMagicLink**(clientId: string, data: { email: string; redirectUrl: string; accountData?: Record<string, any> }) → `Promise<MagicLinkSendResponse>`
|
|
8275
8335
|
Send a magic link email to the user (public).
|
|
8276
8336
|
|
package/dist/docs/auth-kit.md
CHANGED
|
@@ -215,6 +215,93 @@ When `contactData.name` or explicit name parts were supplied on the original `se
|
|
|
215
215
|
const session = await authKit.googleLogin(clientId, googleIdToken);
|
|
216
216
|
```
|
|
217
217
|
|
|
218
|
+
### Sign in with Apple
|
|
219
|
+
|
|
220
|
+
Pass the Apple **identity token** (a JWT). On iOS/native it's
|
|
221
|
+
`ASAuthorizationAppleIDCredential.identityToken` (UTF-8 decoded); on web it's
|
|
222
|
+
`response.authorization.id_token` from Apple JS.
|
|
223
|
+
|
|
224
|
+
```ts
|
|
225
|
+
const session = await authKit.appleLogin(clientId, appleIdentityToken, {
|
|
226
|
+
// All optional:
|
|
227
|
+
nonce, // raw nonce, if you used nonce binding
|
|
228
|
+
userInfo: { name, email }, // first authorization callback ONLY — Apple never resends it
|
|
229
|
+
});
|
|
230
|
+
// session.isNewUser and session.expiresAt (ms epoch) are populated by this endpoint.
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
Apple returns the user's name/email **only on the very first authorization, ever**, and
|
|
234
|
+
never inside the token. Capture it from that first callback and forward it via `userInfo`
|
|
235
|
+
so the server can seed the display name — it's treated as untrusted and never used for identity.
|
|
236
|
+
|
|
237
|
+
Apple login requires the client's AuthKit config to list allowed audiences in
|
|
238
|
+
`appleClientIds`; until then the endpoint returns `400 APPLE_AUTH_NOT_CONFIGURED`.
|
|
239
|
+
|
|
240
|
+
#### Verified-to-verified account linking (affects Google too)
|
|
241
|
+
|
|
242
|
+
Both `appleLogin` and `googleLogin` now refuse to silently merge a federated login into a
|
|
243
|
+
pre-existing account whose email is **unverified**. Instead they throw
|
|
244
|
+
`SmartlinksApiError` with `errorCode === 'ACCOUNT_EXISTS_UNVERIFIED'` (409) and
|
|
245
|
+
`err.details?.requiresEmailVerification === true`. Treat this as recoverable, not fatal:
|
|
246
|
+
|
|
247
|
+
```ts
|
|
248
|
+
try {
|
|
249
|
+
const session = await authKit.appleLogin(clientId, appleIdentityToken);
|
|
250
|
+
} catch (err) {
|
|
251
|
+
if (err instanceof SmartlinksApiError && err.errorCode === 'ACCOUNT_EXISTS_UNVERIFIED') {
|
|
252
|
+
// "An account with this email exists but isn't verified. Sign in with your
|
|
253
|
+
// password (or reset it), then link Apple/Google from settings."
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
> ⚠️ This is a behaviour change for `googleLogin`, which previously merged silently in
|
|
259
|
+
> this case. Handle the 409 for both methods.
|
|
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
|
+
|
|
218
305
|
---
|
|
219
306
|
|
|
220
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, 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
|
@@ -8177,6 +8177,38 @@ paths:
|
|
|
8177
8177
|
description: Unauthorized
|
|
8178
8178
|
404:
|
|
8179
8179
|
description: Not found
|
|
8180
|
+
/authkit/{clientId}/auth/apple:
|
|
8181
|
+
post:
|
|
8182
|
+
tags:
|
|
8183
|
+
- authKit
|
|
8184
|
+
summary: Sign in with Apple via an Apple identity token (public).
|
|
8185
|
+
operationId: authKit_appleLogin
|
|
8186
|
+
security: []
|
|
8187
|
+
parameters:
|
|
8188
|
+
- name: clientId
|
|
8189
|
+
in: path
|
|
8190
|
+
required: true
|
|
8191
|
+
schema:
|
|
8192
|
+
type: string
|
|
8193
|
+
responses:
|
|
8194
|
+
200:
|
|
8195
|
+
description: Success
|
|
8196
|
+
content:
|
|
8197
|
+
application/json:
|
|
8198
|
+
schema:
|
|
8199
|
+
$ref: "#/components/schemas/AuthLoginResponse"
|
|
8200
|
+
400:
|
|
8201
|
+
description: Bad request
|
|
8202
|
+
401:
|
|
8203
|
+
description: Unauthorized
|
|
8204
|
+
404:
|
|
8205
|
+
description: Not found
|
|
8206
|
+
requestBody:
|
|
8207
|
+
required: true
|
|
8208
|
+
content:
|
|
8209
|
+
application/json:
|
|
8210
|
+
schema:
|
|
8211
|
+
$ref: "#/components/schemas/AppleLoginOptions"
|
|
8180
8212
|
/authkit/{clientId}/auth/complete-reset:
|
|
8181
8213
|
post:
|
|
8182
8214
|
tags:
|
|
@@ -8281,6 +8313,32 @@ paths:
|
|
|
8281
8313
|
description: Unauthorized
|
|
8282
8314
|
404:
|
|
8283
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
|
|
8284
8342
|
/authkit/{clientId}/auth/magic-link/send:
|
|
8285
8343
|
post:
|
|
8286
8344
|
tags:
|
|
@@ -8392,6 +8450,32 @@ paths:
|
|
|
8392
8450
|
description: Unauthorized
|
|
8393
8451
|
404:
|
|
8394
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
|
|
8395
8479
|
/authkit/{clientId}/auth/register:
|
|
8396
8480
|
post:
|
|
8397
8481
|
tags:
|
|
@@ -17929,8 +18013,56 @@ components:
|
|
|
17929
18013
|
type: number
|
|
17930
18014
|
accountLocked:
|
|
17931
18015
|
type: boolean
|
|
18016
|
+
isNewUser:
|
|
18017
|
+
type: boolean
|
|
18018
|
+
expiresAt:
|
|
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
|
|
17932
18042
|
required:
|
|
18043
|
+
- token
|
|
18044
|
+
- refreshToken
|
|
18045
|
+
- refreshTokenExpiresAt
|
|
18046
|
+
- expiresAt
|
|
17933
18047
|
- user
|
|
18048
|
+
LogoutResponse:
|
|
18049
|
+
type: object
|
|
18050
|
+
properties:
|
|
18051
|
+
success:
|
|
18052
|
+
type: object
|
|
18053
|
+
additionalProperties: true
|
|
18054
|
+
required:
|
|
18055
|
+
- success
|
|
18056
|
+
AppleLoginOptions:
|
|
18057
|
+
type: object
|
|
18058
|
+
properties:
|
|
18059
|
+
authorizationCode:
|
|
18060
|
+
type: string
|
|
18061
|
+
nonce:
|
|
18062
|
+
type: string
|
|
18063
|
+
userInfo:
|
|
18064
|
+
type: object
|
|
18065
|
+
additionalProperties: true
|
|
17934
18066
|
MagicLinkSendResponse:
|
|
17935
18067
|
type: object
|
|
17936
18068
|
properties:
|
package/dist/types/authKit.d.ts
CHANGED
|
@@ -37,7 +37,89 @@ export interface AuthLoginResponse {
|
|
|
37
37
|
requiresEmailVerification?: boolean;
|
|
38
38
|
emailVerificationDeadline?: number;
|
|
39
39
|
accountLocked?: boolean;
|
|
40
|
+
/**
|
|
41
|
+
* True when this login created a brand-new account. Currently only populated by
|
|
42
|
+
* the Apple login endpoint; left undefined by the other AuthKit login endpoints.
|
|
43
|
+
*/
|
|
44
|
+
isNewUser?: boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Session token expiry, in **milliseconds since epoch** (not seconds, not a duration),
|
|
47
|
+
* or null when the server could not decode it. Currently only populated by the Apple
|
|
48
|
+
* login endpoint.
|
|
49
|
+
*/
|
|
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;
|
|
40
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';
|
|
94
|
+
/**
|
|
95
|
+
* Options for {@link authKit.appleLogin}. All fields are optional — only the
|
|
96
|
+
* `identityToken` (passed as a positional argument) is required by the server.
|
|
97
|
+
*/
|
|
98
|
+
export interface AppleLoginOptions {
|
|
99
|
+
/** Apple authorization code. Accepted but ignored by the server for now (reserved for future server-side token exchange). */
|
|
100
|
+
authorizationCode?: string;
|
|
101
|
+
/**
|
|
102
|
+
* The **raw** nonce the client generated, if nonce binding was used. The server
|
|
103
|
+
* accepts either `token.nonce === nonce` (native) or `token.nonce === sha256hex(nonce)` (web).
|
|
104
|
+
*/
|
|
105
|
+
nonce?: string;
|
|
106
|
+
/**
|
|
107
|
+
* Name/email from Apple's **first** authorization callback only — Apple never returns
|
|
108
|
+
* these again, and never inside the token. Forwarded so the server can persist the
|
|
109
|
+
* display name on first account creation. Treated as untrusted (never used for identity).
|
|
110
|
+
*/
|
|
111
|
+
userInfo?: {
|
|
112
|
+
email?: string;
|
|
113
|
+
name?: string;
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Server-defined error codes returned by AuthKit federated-login endpoints
|
|
118
|
+
* (Apple + Google). Surfaced via `SmartlinksApiError.errorCode`. The
|
|
119
|
+
* `ACCOUNT_EXISTS_UNVERIFIED` case also carries `requiresEmailVerification: true`
|
|
120
|
+
* in `SmartlinksApiError.details`.
|
|
121
|
+
*/
|
|
122
|
+
export type AuthKitErrorCode = 'MISSING_APPLE_TOKEN' | 'APPLE_AUTH_NOT_CONFIGURED' | 'INVALID_APPLE_TOKEN' | 'ACCOUNT_EXISTS_UNVERIFIED' | 'APPLE_AUTH_FAILED';
|
|
41
123
|
export interface MagicLinkSendResponse {
|
|
42
124
|
success: boolean;
|
|
43
125
|
message: string;
|
package/docs/API_SUMMARY.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Smartlinks API Summary
|
|
2
2
|
|
|
3
|
-
Version: 1.14.
|
|
3
|
+
Version: 1.14.16 | Generated: 2026-06-01T09:33:51.336Z
|
|
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
|
-
*
|
|
159
|
-
*
|
|
160
|
-
*
|
|
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`
|
|
@@ -2956,6 +2955,54 @@ interface AuthLoginResponse {
|
|
|
2956
2955
|
requiresEmailVerification?: boolean // True if email verification is required but not yet completed
|
|
2957
2956
|
emailVerificationDeadline?: number // Unix timestamp - for 'immediate' mode grace period deadline
|
|
2958
2957
|
accountLocked?: boolean // True if account is locked due to expired verification deadline
|
|
2958
|
+
* True when this login created a brand-new account. Currently only populated by
|
|
2959
|
+
* the Apple login endpoint; left undefined by the other AuthKit login endpoints.
|
|
2960
|
+
isNewUser?: boolean
|
|
2961
|
+
* Session token expiry, in **milliseconds since epoch** (not seconds, not a duration),
|
|
2962
|
+
* or null when the server could not decode it. Currently only populated by the Apple
|
|
2963
|
+
* login endpoint.
|
|
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
|
|
2992
|
+
}
|
|
2993
|
+
```
|
|
2994
|
+
|
|
2995
|
+
**AppleLoginOptions** (interface)
|
|
2996
|
+
```typescript
|
|
2997
|
+
interface AppleLoginOptions {
|
|
2998
|
+
authorizationCode?: string
|
|
2999
|
+
* The **raw** nonce the client generated, if nonce binding was used. The server
|
|
3000
|
+
* accepts either `token.nonce === nonce` (native) or `token.nonce === sha256hex(nonce)` (web).
|
|
3001
|
+
nonce?: string
|
|
3002
|
+
* Name/email from Apple's **first** authorization callback only — Apple never returns
|
|
3003
|
+
* these again, and never inside the token. Forwarded so the server can persist the
|
|
3004
|
+
* display name on first account creation. Treated as untrusted (never used for identity).
|
|
3005
|
+
userInfo?: { email?: string; name?: string }
|
|
2959
3006
|
}
|
|
2960
3007
|
```
|
|
2961
3008
|
|
|
@@ -3197,6 +3244,10 @@ interface AuthKitConfig {
|
|
|
3197
3244
|
}
|
|
3198
3245
|
```
|
|
3199
3246
|
|
|
3247
|
+
**RefreshErrorCode** = ``
|
|
3248
|
+
|
|
3249
|
+
**AuthKitErrorCode** = ``
|
|
3250
|
+
|
|
3200
3251
|
**VerifyStatus** = `'pending' | 'verified' | 'failed' | 'expired' | 'unknown'`
|
|
3201
3252
|
|
|
3202
3253
|
### batch
|
|
@@ -8271,6 +8322,15 @@ Google OAuth login via ID token (public).
|
|
|
8271
8322
|
**googleCodeLogin**(clientId: string, code: string, redirectUri: string) → `Promise<AuthLoginResponse>`
|
|
8272
8323
|
Google OAuth login via server-side authorization code (public).
|
|
8273
8324
|
|
|
8325
|
+
**appleLogin**(clientId: string, identityToken: string, opts?: AppleLoginOptions) → `Promise<AuthLoginResponse>`
|
|
8326
|
+
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.
|
|
8327
|
+
|
|
8328
|
+
**refreshToken**(clientId: string, refreshToken: string) → `Promise<RefreshResponse>`
|
|
8329
|
+
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.
|
|
8330
|
+
|
|
8331
|
+
**logout**(clientId: string, refreshToken: string) → `Promise<LogoutResponse>`
|
|
8332
|
+
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.
|
|
8333
|
+
|
|
8274
8334
|
**sendMagicLink**(clientId: string, data: { email: string; redirectUrl: string; accountData?: Record<string, any> }) → `Promise<MagicLinkSendResponse>`
|
|
8275
8335
|
Send a magic link email to the user (public).
|
|
8276
8336
|
|
package/docs/auth-kit.md
CHANGED
|
@@ -215,6 +215,93 @@ When `contactData.name` or explicit name parts were supplied on the original `se
|
|
|
215
215
|
const session = await authKit.googleLogin(clientId, googleIdToken);
|
|
216
216
|
```
|
|
217
217
|
|
|
218
|
+
### Sign in with Apple
|
|
219
|
+
|
|
220
|
+
Pass the Apple **identity token** (a JWT). On iOS/native it's
|
|
221
|
+
`ASAuthorizationAppleIDCredential.identityToken` (UTF-8 decoded); on web it's
|
|
222
|
+
`response.authorization.id_token` from Apple JS.
|
|
223
|
+
|
|
224
|
+
```ts
|
|
225
|
+
const session = await authKit.appleLogin(clientId, appleIdentityToken, {
|
|
226
|
+
// All optional:
|
|
227
|
+
nonce, // raw nonce, if you used nonce binding
|
|
228
|
+
userInfo: { name, email }, // first authorization callback ONLY — Apple never resends it
|
|
229
|
+
});
|
|
230
|
+
// session.isNewUser and session.expiresAt (ms epoch) are populated by this endpoint.
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
Apple returns the user's name/email **only on the very first authorization, ever**, and
|
|
234
|
+
never inside the token. Capture it from that first callback and forward it via `userInfo`
|
|
235
|
+
so the server can seed the display name — it's treated as untrusted and never used for identity.
|
|
236
|
+
|
|
237
|
+
Apple login requires the client's AuthKit config to list allowed audiences in
|
|
238
|
+
`appleClientIds`; until then the endpoint returns `400 APPLE_AUTH_NOT_CONFIGURED`.
|
|
239
|
+
|
|
240
|
+
#### Verified-to-verified account linking (affects Google too)
|
|
241
|
+
|
|
242
|
+
Both `appleLogin` and `googleLogin` now refuse to silently merge a federated login into a
|
|
243
|
+
pre-existing account whose email is **unverified**. Instead they throw
|
|
244
|
+
`SmartlinksApiError` with `errorCode === 'ACCOUNT_EXISTS_UNVERIFIED'` (409) and
|
|
245
|
+
`err.details?.requiresEmailVerification === true`. Treat this as recoverable, not fatal:
|
|
246
|
+
|
|
247
|
+
```ts
|
|
248
|
+
try {
|
|
249
|
+
const session = await authKit.appleLogin(clientId, appleIdentityToken);
|
|
250
|
+
} catch (err) {
|
|
251
|
+
if (err instanceof SmartlinksApiError && err.errorCode === 'ACCOUNT_EXISTS_UNVERIFIED') {
|
|
252
|
+
// "An account with this email exists but isn't verified. Sign in with your
|
|
253
|
+
// password (or reset it), then link Apple/Google from settings."
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
> ⚠️ This is a behaviour change for `googleLogin`, which previously merged silently in
|
|
259
|
+
> this case. Handle the 409 for both methods.
|
|
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
|
+
|
|
218
305
|
---
|
|
219
306
|
|
|
220
307
|
## Profile management
|
package/openapi.yaml
CHANGED
|
@@ -8177,6 +8177,38 @@ paths:
|
|
|
8177
8177
|
description: Unauthorized
|
|
8178
8178
|
404:
|
|
8179
8179
|
description: Not found
|
|
8180
|
+
/authkit/{clientId}/auth/apple:
|
|
8181
|
+
post:
|
|
8182
|
+
tags:
|
|
8183
|
+
- authKit
|
|
8184
|
+
summary: Sign in with Apple via an Apple identity token (public).
|
|
8185
|
+
operationId: authKit_appleLogin
|
|
8186
|
+
security: []
|
|
8187
|
+
parameters:
|
|
8188
|
+
- name: clientId
|
|
8189
|
+
in: path
|
|
8190
|
+
required: true
|
|
8191
|
+
schema:
|
|
8192
|
+
type: string
|
|
8193
|
+
responses:
|
|
8194
|
+
200:
|
|
8195
|
+
description: Success
|
|
8196
|
+
content:
|
|
8197
|
+
application/json:
|
|
8198
|
+
schema:
|
|
8199
|
+
$ref: "#/components/schemas/AuthLoginResponse"
|
|
8200
|
+
400:
|
|
8201
|
+
description: Bad request
|
|
8202
|
+
401:
|
|
8203
|
+
description: Unauthorized
|
|
8204
|
+
404:
|
|
8205
|
+
description: Not found
|
|
8206
|
+
requestBody:
|
|
8207
|
+
required: true
|
|
8208
|
+
content:
|
|
8209
|
+
application/json:
|
|
8210
|
+
schema:
|
|
8211
|
+
$ref: "#/components/schemas/AppleLoginOptions"
|
|
8180
8212
|
/authkit/{clientId}/auth/complete-reset:
|
|
8181
8213
|
post:
|
|
8182
8214
|
tags:
|
|
@@ -8281,6 +8313,32 @@ paths:
|
|
|
8281
8313
|
description: Unauthorized
|
|
8282
8314
|
404:
|
|
8283
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
|
|
8284
8342
|
/authkit/{clientId}/auth/magic-link/send:
|
|
8285
8343
|
post:
|
|
8286
8344
|
tags:
|
|
@@ -8392,6 +8450,32 @@ paths:
|
|
|
8392
8450
|
description: Unauthorized
|
|
8393
8451
|
404:
|
|
8394
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
|
|
8395
8479
|
/authkit/{clientId}/auth/register:
|
|
8396
8480
|
post:
|
|
8397
8481
|
tags:
|
|
@@ -17929,8 +18013,56 @@ components:
|
|
|
17929
18013
|
type: number
|
|
17930
18014
|
accountLocked:
|
|
17931
18015
|
type: boolean
|
|
18016
|
+
isNewUser:
|
|
18017
|
+
type: boolean
|
|
18018
|
+
expiresAt:
|
|
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
|
|
17932
18042
|
required:
|
|
18043
|
+
- token
|
|
18044
|
+
- refreshToken
|
|
18045
|
+
- refreshTokenExpiresAt
|
|
18046
|
+
- expiresAt
|
|
17933
18047
|
- user
|
|
18048
|
+
LogoutResponse:
|
|
18049
|
+
type: object
|
|
18050
|
+
properties:
|
|
18051
|
+
success:
|
|
18052
|
+
type: object
|
|
18053
|
+
additionalProperties: true
|
|
18054
|
+
required:
|
|
18055
|
+
- success
|
|
18056
|
+
AppleLoginOptions:
|
|
18057
|
+
type: object
|
|
18058
|
+
properties:
|
|
18059
|
+
authorizationCode:
|
|
18060
|
+
type: string
|
|
18061
|
+
nonce:
|
|
18062
|
+
type: string
|
|
18063
|
+
userInfo:
|
|
18064
|
+
type: object
|
|
18065
|
+
additionalProperties: true
|
|
17934
18066
|
MagicLinkSendResponse:
|
|
17935
18067
|
type: object
|
|
17936
18068
|
properties:
|