@sparkvault/sdk-mobile 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +186 -0
- package/dist/auth.d.ts +74 -0
- package/dist/auth.js +515 -0
- package/dist/auth.js.map +1 -0
- package/dist/billing.d.ts +21 -0
- package/dist/billing.js +10 -0
- package/dist/billing.js.map +1 -0
- package/dist/client.d.ts +27 -0
- package/dist/client.js +36 -0
- package/dist/client.js.map +1 -0
- package/dist/config.d.ts +39 -0
- package/dist/config.js +69 -0
- package/dist/config.js.map +1 -0
- package/dist/encoding.d.ts +7 -0
- package/dist/encoding.js +113 -0
- package/dist/encoding.js.map +1 -0
- package/dist/entropy.d.ts +18 -0
- package/dist/entropy.js +35 -0
- package/dist/entropy.js.map +1 -0
- package/dist/errors.d.ts +46 -0
- package/dist/errors.js +72 -0
- package/dist/errors.js.map +1 -0
- package/dist/folders.d.ts +15 -0
- package/dist/folders.js +54 -0
- package/dist/folders.js.map +1 -0
- package/dist/health.d.ts +19 -0
- package/dist/health.js +60 -0
- package/dist/health.js.map +1 -0
- package/dist/http.d.ts +45 -0
- package/dist/http.js +351 -0
- package/dist/http.js.map +1 -0
- package/dist/identity-dialog.d.ts +19 -0
- package/dist/identity-dialog.js +656 -0
- package/dist/identity-dialog.js.map +1 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/ingots.d.ts +99 -0
- package/dist/ingots.js +365 -0
- package/dist/ingots.js.map +1 -0
- package/dist/mutex.d.ts +6 -0
- package/dist/mutex.js +20 -0
- package/dist/mutex.js.map +1 -0
- package/dist/push-tokens.d.ts +12 -0
- package/dist/push-tokens.js +15 -0
- package/dist/push-tokens.js.map +1 -0
- package/dist/sparks.d.ts +39 -0
- package/dist/sparks.js +32 -0
- package/dist/sparks.js.map +1 -0
- package/dist/tus.d.ts +24 -0
- package/dist/tus.js +202 -0
- package/dist/tus.js.map +1 -0
- package/dist/types.d.ts +406 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/validation.d.ts +5 -0
- package/dist/validation.js +45 -0
- package/dist/validation.js.map +1 -0
- package/dist/vaults.d.ts +94 -0
- package/dist/vaults.js +106 -0
- package/dist/vaults.js.map +1 -0
- package/package.json +58 -0
- package/src/auth.ts +707 -0
- package/src/billing.ts +30 -0
- package/src/client.ts +51 -0
- package/src/config.ts +123 -0
- package/src/encoding.ts +150 -0
- package/src/entropy.ts +56 -0
- package/src/errors.ts +110 -0
- package/src/folders.ts +76 -0
- package/src/health.ts +81 -0
- package/src/http.ts +429 -0
- package/src/identity-dialog.tsx +955 -0
- package/src/index.ts +103 -0
- package/src/ingots.ts +593 -0
- package/src/mutex.ts +26 -0
- package/src/push-tokens.ts +26 -0
- package/src/sparks.ts +73 -0
- package/src/tus.ts +280 -0
- package/src/types.ts +487 -0
- package/src/validation.ts +49 -0
- package/src/vaults.ts +271 -0
package/README.md
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
# SparkVault Mobile SDK
|
|
2
|
+
|
|
3
|
+
Mobile SDK for SparkVault Identity, vault, folder, and ingot workflows.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @sparkvault/sdk-mobile
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Create a Client
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import * as FileSystem from 'expo-file-system/legacy';
|
|
15
|
+
import { createSparkVaultMobileClient } from '@sparkvault/sdk-mobile';
|
|
16
|
+
|
|
17
|
+
export const sparkVault = createSparkVaultMobileClient({
|
|
18
|
+
tokenStorage,
|
|
19
|
+
apiBaseUrl: 'https://api.sparkvault.com/v1',
|
|
20
|
+
identityBaseUrl: 'https://api.sparkvault.com/v1/apps/identity',
|
|
21
|
+
identityAccountId: 'acc_00000000000000000000000000000000',
|
|
22
|
+
userAgent: 'SparkVault/1.0.0 (Mobile)',
|
|
23
|
+
fetch: xhrFetch,
|
|
24
|
+
fileReader: {
|
|
25
|
+
readAsBase64: (fileUri, { position, length }) =>
|
|
26
|
+
FileSystem.readAsStringAsync(fileUri, {
|
|
27
|
+
encoding: 'base64',
|
|
28
|
+
position,
|
|
29
|
+
length,
|
|
30
|
+
}),
|
|
31
|
+
},
|
|
32
|
+
fileDownloader: {
|
|
33
|
+
download: async (url, fileUri, options) => {
|
|
34
|
+
const resumable = FileSystem.createDownloadResumable(
|
|
35
|
+
url,
|
|
36
|
+
fileUri,
|
|
37
|
+
{},
|
|
38
|
+
progress => options?.onProgress?.({
|
|
39
|
+
bytesWritten: progress.totalBytesWritten,
|
|
40
|
+
bytesTotal: progress.totalBytesExpectedToWrite || undefined,
|
|
41
|
+
})
|
|
42
|
+
);
|
|
43
|
+
const result = await resumable.downloadAsync();
|
|
44
|
+
return result ? { uri: result.uri, status: result.status, headers: result.headers } : null;
|
|
45
|
+
},
|
|
46
|
+
getInfo: async (fileUri) => {
|
|
47
|
+
const info = await FileSystem.getInfoAsync(fileUri);
|
|
48
|
+
return { exists: info.exists, sizeBytes: info.exists ? info.size : undefined };
|
|
49
|
+
},
|
|
50
|
+
delete: (fileUri) => FileSystem.deleteAsync(fileUri, { idempotent: true }),
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Dependency contract
|
|
56
|
+
|
|
57
|
+
The SDK depends on `tweetnacl` for Ed25519 Identity JWT verification and declares `react`/`react-native` as peer dependencies for the optional native Identity dialog. Platform behavior stays behind adapters so the SDK is portable across React Native runtimes.
|
|
58
|
+
|
|
59
|
+
| Adapter | Required | Default if omitted |
|
|
60
|
+
|---------|----------|--------------------|
|
|
61
|
+
| `tokenStorage` | Yes | Throws at client construction |
|
|
62
|
+
| `identityAccountId` | Yes (must start with `acc_`) | Throws at client construction |
|
|
63
|
+
| `fetch` | No | Falls back to `globalThis.fetch`; throws if neither exists |
|
|
64
|
+
| `fileReader` | Only for `ingots.uploadFromUri` | Throws on first upload-from-URI call |
|
|
65
|
+
| `fileDownloader` | Only for `ingots.downloadToFile` | Throws on first file-download call |
|
|
66
|
+
| `passkeyProvider` | Only for passkey sign-in UI | Passkey is hidden from the Identity dialog |
|
|
67
|
+
| `logger` | No | No-op logger |
|
|
68
|
+
|
|
69
|
+
## Modules
|
|
70
|
+
|
|
71
|
+
- `auth`: Identity config, TOTP/passkey login, JWKS/JWT verification, Core exchange, signup completion, profile, and account APIs.
|
|
72
|
+
- `vaults`: Vault CRUD, unseal/seal, sharing, access retention, and public upload settings.
|
|
73
|
+
- `folders`: Folder CRUD, breadcrumbs, folder moves, and ingot moves.
|
|
74
|
+
- `ingots`: List/search/get, URI streaming upload/replace, rename/delete, sharing, access logs, and SDK-managed file downloads.
|
|
75
|
+
- `health`: Root API availability checks.
|
|
76
|
+
- `sparks`: SparkSync spark list/read/create operations.
|
|
77
|
+
- `entropy`: HSM-backed entropy generation.
|
|
78
|
+
- `pushTokens`: Expo push-token registration/removal.
|
|
79
|
+
- `billing`: Account balance.
|
|
80
|
+
|
|
81
|
+
## Identity Dialog
|
|
82
|
+
|
|
83
|
+
`SparkVaultIdentityDialog` is the mobile equivalent of the web SDK Identity popup. It fetches Identity App config, renders email/phone input from `allowedIdentityTypes`, offers enabled mobile-supported methods (`passkey`, `totp_email`, `totp_sms`, `totp_voice`), applies configured branding and light/dark mode, and returns the raw signed Identity JWT plus JWKS metadata.
|
|
84
|
+
|
|
85
|
+
```tsx
|
|
86
|
+
import * as Passkey from 'react-native-passkeys';
|
|
87
|
+
import {
|
|
88
|
+
SparkVaultIdentityDialog,
|
|
89
|
+
type MobilePasskeyProvider,
|
|
90
|
+
} from '@sparkvault/sdk-mobile/identity-dialog';
|
|
91
|
+
|
|
92
|
+
const passkeyProvider: MobilePasskeyProvider = {
|
|
93
|
+
isSupported: () => Passkey.isSupported(),
|
|
94
|
+
authenticate: async (options) => {
|
|
95
|
+
const credential = await Passkey.get({
|
|
96
|
+
challenge: options.challenge,
|
|
97
|
+
timeout: options.timeout,
|
|
98
|
+
rpId: options.rpId,
|
|
99
|
+
allowCredentials: options.allowCredentials,
|
|
100
|
+
userVerification: options.userVerification,
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
return credential
|
|
104
|
+
? {
|
|
105
|
+
id: credential.id,
|
|
106
|
+
rawId: credential.rawId,
|
|
107
|
+
type: 'public-key',
|
|
108
|
+
response: {
|
|
109
|
+
clientDataJSON: credential.response.clientDataJSON,
|
|
110
|
+
authenticatorData: credential.response.authenticatorData,
|
|
111
|
+
signature: credential.response.signature,
|
|
112
|
+
},
|
|
113
|
+
}
|
|
114
|
+
: null;
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
<SparkVaultIdentityDialog
|
|
119
|
+
client={sparkVault}
|
|
120
|
+
visible={visible}
|
|
121
|
+
passkeyProvider={passkeyProvider}
|
|
122
|
+
onCancel={() => setVisible(false)}
|
|
123
|
+
onSuccess={async (result) => {
|
|
124
|
+
await sparkVault.auth.verifyIdentityToken(result.token, { jwks: result.jwks });
|
|
125
|
+
const session = await sparkVault.auth.exchangeIdentityToken(result.token);
|
|
126
|
+
// Continue only after local verification and Core exchange succeed.
|
|
127
|
+
}}
|
|
128
|
+
/>;
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Client-side JWT verification is an app integrity gate. Third-party backends must still verify the token server-side with the JWKS endpoint before creating their own sessions.
|
|
132
|
+
|
|
133
|
+
## Health Check
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
const status = await sparkVault.health.check();
|
|
137
|
+
const online = await sparkVault.health.isOnline();
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Ingot Upload
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
const session = await sparkVault.vaults.unseal(vaultId, { vmk });
|
|
144
|
+
|
|
145
|
+
await sparkVault.ingots.uploadFromUri({
|
|
146
|
+
vaultId,
|
|
147
|
+
vat: session.vat,
|
|
148
|
+
fileUri,
|
|
149
|
+
fileSize,
|
|
150
|
+
name: 'receipt.pdf',
|
|
151
|
+
contentType: 'application/pdf',
|
|
152
|
+
onProgress: (uploaded, total) => console.log(uploaded / total),
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
await sparkVault.ingots.replaceFromUri({
|
|
156
|
+
vaultId,
|
|
157
|
+
ingotId,
|
|
158
|
+
vat: session.vat,
|
|
159
|
+
fileUri,
|
|
160
|
+
fileSize,
|
|
161
|
+
name: 'receipt.pdf',
|
|
162
|
+
contentType: 'application/pdf',
|
|
163
|
+
});
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Uploads use Forge with tus v1.0.0 and read file chunks through the configured
|
|
167
|
+
`fileReader` adapter. Generated content should be staged to a file URI first so
|
|
168
|
+
all uploads use the same streaming path.
|
|
169
|
+
|
|
170
|
+
## Ingot Download
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
const result = await sparkVault.ingots.downloadToFile({
|
|
174
|
+
vaultId,
|
|
175
|
+
ingotId,
|
|
176
|
+
vat: session.vat,
|
|
177
|
+
fileUri: cacheFileUri,
|
|
178
|
+
expectedSizeBytes,
|
|
179
|
+
onProgress: (downloaded, total) => console.log(downloaded / total),
|
|
180
|
+
});
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Downloads use the configured `fileDownloader` adapter for one canonical
|
|
184
|
+
streaming path across every supported file size. Production adapters should
|
|
185
|
+
honor `abortSignal` and `timeoutMs`, and should throw on transport failure
|
|
186
|
+
instead of writing an error response as a successful file.
|
package/dist/auth.d.ts
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { type ResolvedMobileConfig } from './config.js';
|
|
2
|
+
import type { MobileHttpClient } from './http.js';
|
|
3
|
+
import type { CompleteSignupRequest, CompleteSignupResponse, IdentityConfig, IdentityJwks, IdentityTokenClaims, IdentityType, IdentityVerifyResult, PasskeyAuthChallenge, PasskeyCredential, PasskeyRegisterChallenge, PasskeySession, PasskeyStatus, PinVerifyRequest, PinVerifyResponse, RequestPinRequest, RequestPinResponse, SendTotpRequest, SparkVaultAccount, SparkVaultUser, VerifyTotpRequest } from './types.js';
|
|
4
|
+
export declare class MobileAuthClient {
|
|
5
|
+
private readonly config;
|
|
6
|
+
private readonly http;
|
|
7
|
+
private configCache;
|
|
8
|
+
private jwksCache;
|
|
9
|
+
private jwksCachedAt;
|
|
10
|
+
constructor(config: ResolvedMobileConfig, http: MobileHttpClient);
|
|
11
|
+
getJwksUri(): string;
|
|
12
|
+
getConfig(): Promise<IdentityConfig>;
|
|
13
|
+
preloadConfig(): void;
|
|
14
|
+
isConfigPreloaded(): boolean;
|
|
15
|
+
getJwks(options?: {
|
|
16
|
+
forceRefresh?: boolean;
|
|
17
|
+
}): Promise<IdentityJwks>;
|
|
18
|
+
verifyIdentityToken(token: string, options?: {
|
|
19
|
+
jwks?: IdentityJwks;
|
|
20
|
+
audience?: string;
|
|
21
|
+
clockSkewSeconds?: number;
|
|
22
|
+
}): Promise<IdentityTokenClaims>;
|
|
23
|
+
private resolveJwk;
|
|
24
|
+
private tryFindJwk;
|
|
25
|
+
sendTotp(request: SendTotpRequest): Promise<RequestPinResponse>;
|
|
26
|
+
requestPin(request: RequestPinRequest): Promise<RequestPinResponse>;
|
|
27
|
+
verifyTotp(request: VerifyTotpRequest): Promise<IdentityVerifyResult>;
|
|
28
|
+
verifyPin(request: PinVerifyRequest): Promise<PinVerifyResponse>;
|
|
29
|
+
completeSignup(signupToken: string, request: CompleteSignupRequest): Promise<CompleteSignupResponse>;
|
|
30
|
+
signup(params: {
|
|
31
|
+
signup_token: string;
|
|
32
|
+
organization_name: string;
|
|
33
|
+
full_name?: string;
|
|
34
|
+
}): Promise<CompleteSignupResponse>;
|
|
35
|
+
checkPasskeyStatus(identity: string, identityType?: IdentityType): Promise<{
|
|
36
|
+
hasPasskey: boolean;
|
|
37
|
+
}>;
|
|
38
|
+
getPasskeyAuthOptions(identity: string, identityType?: IdentityType): Promise<PasskeyAuthChallenge>;
|
|
39
|
+
completePasskeyAuthentication(credential: PasskeyCredential, session: PasskeySession): Promise<IdentityVerifyResult>;
|
|
40
|
+
verifyPasskeyAuth(credential: PasskeyCredential, session: PasskeySession): Promise<PinVerifyResponse>;
|
|
41
|
+
getPasskeyRegisterOptions(params?: {
|
|
42
|
+
email?: string;
|
|
43
|
+
deviceName?: string;
|
|
44
|
+
}): Promise<PasskeyRegisterChallenge>;
|
|
45
|
+
registerPasskey(credential: PasskeyCredential, session: PasskeySession): Promise<{
|
|
46
|
+
success: boolean;
|
|
47
|
+
credential_id: string;
|
|
48
|
+
}>;
|
|
49
|
+
getMyPasskeyStatus(): Promise<PasskeyStatus>;
|
|
50
|
+
getProfile(): Promise<SparkVaultUser>;
|
|
51
|
+
updateProfile(updates: {
|
|
52
|
+
name?: string;
|
|
53
|
+
}): Promise<SparkVaultUser>;
|
|
54
|
+
getAccount(): Promise<SparkVaultAccount>;
|
|
55
|
+
revokeRefreshToken(refreshToken?: string): Promise<void>;
|
|
56
|
+
logout(options?: {
|
|
57
|
+
revoke?: boolean;
|
|
58
|
+
clearTokens?: boolean;
|
|
59
|
+
}): Promise<void>;
|
|
60
|
+
exchangeIdentityToken(identityToken: string): Promise<PinVerifyResponse>;
|
|
61
|
+
private storeSession;
|
|
62
|
+
private verifyTotpWithRetryErrors;
|
|
63
|
+
private startConfigLoad;
|
|
64
|
+
private toIdentityVerifyResult;
|
|
65
|
+
private decodeIdentityTokenClaims;
|
|
66
|
+
private getIdentity;
|
|
67
|
+
private postIdentity;
|
|
68
|
+
private identityHeaders;
|
|
69
|
+
private identityLookupBody;
|
|
70
|
+
private normalizeIdentity;
|
|
71
|
+
private selectJwk;
|
|
72
|
+
private isIdentityTokenClaims;
|
|
73
|
+
private getStoredUserEmail;
|
|
74
|
+
}
|