@ovixa/auth-client 0.1.3 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +25 -0
- package/README.md +104 -0
- package/dist/{chunk-CUACHOKT.js → chunk-5QEKSWV4.js} +406 -2
- package/dist/chunk-5QEKSWV4.js.map +1 -0
- package/dist/{chunk-DIZJA4PY.js → chunk-IU57RXBC.js} +2 -2
- package/dist/index.d.ts +398 -1
- package/dist/index.js +15 -3
- package/dist/middleware/astro.js +2 -2
- package/dist/middleware/express.js +2 -2
- package/package.json +1 -1
- package/dist/chunk-CUACHOKT.js.map +0 -1
- /package/dist/{chunk-DIZJA4PY.js.map → chunk-IU57RXBC.js.map} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,31 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
7
7
|
|
|
8
|
+
## [0.3.0] - 2026-01-30
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- `auth.webauthn.listPasskeys({ accessToken })` - List all passkeys for the authenticated user
|
|
13
|
+
- `auth.webauthn.deletePasskey({ accessToken, credentialId })` - Delete a specific passkey
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
|
|
17
|
+
- **BREAKING**: Moved WebAuthn methods to `auth.webauthn` namespace
|
|
18
|
+
- `auth.getPasskeyRegistrationOptions()` → `auth.webauthn.getRegistrationOptions()`
|
|
19
|
+
- `auth.verifyPasskeyRegistration()` → `auth.webauthn.verifyRegistration()`
|
|
20
|
+
- `auth.getPasskeyAuthenticationOptions()` → `auth.webauthn.getAuthenticationOptions()`
|
|
21
|
+
- `auth.verifyPasskeyAuthentication()` → `auth.webauthn.authenticate()`
|
|
22
|
+
|
|
23
|
+
## [0.2.0] - 2026-01-29
|
|
24
|
+
|
|
25
|
+
### Added
|
|
26
|
+
|
|
27
|
+
- **WebAuthn/Passkey authentication support** - Passwordless authentication using FIDO2
|
|
28
|
+
- `auth.getPasskeyRegistrationOptions({ accessToken })` - Get options to register a new passkey
|
|
29
|
+
- `auth.verifyPasskeyRegistration({ accessToken, registration, deviceName? })` - Complete passkey registration
|
|
30
|
+
- `auth.getPasskeyAuthenticationOptions({ email? })` - Get options to authenticate with passkey
|
|
31
|
+
- `auth.verifyPasskeyAuthentication({ authentication })` - Authenticate and receive tokens
|
|
32
|
+
|
|
8
33
|
## [0.1.3] - 2026-01-29
|
|
9
34
|
|
|
10
35
|
### Added
|
package/README.md
CHANGED
|
@@ -5,6 +5,7 @@ Client SDK for the Ovixa Auth service. Provides authentication, token verificati
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
7
|
- Email/password authentication (signup, login, password reset)
|
|
8
|
+
- **Passkey (WebAuthn) authentication** - phishing-resistant passwordless sign-in
|
|
8
9
|
- OAuth integration (Google, GitHub)
|
|
9
10
|
- JWT verification with JWKS caching
|
|
10
11
|
- Automatic token refresh
|
|
@@ -243,6 +244,109 @@ try {
|
|
|
243
244
|
}
|
|
244
245
|
```
|
|
245
246
|
|
|
247
|
+
### Passkeys (WebAuthn)
|
|
248
|
+
|
|
249
|
+
Passkeys provide phishing-resistant, passwordless authentication using FIDO2/WebAuthn.
|
|
250
|
+
|
|
251
|
+
#### `webauthn.getRegistrationOptions(options)`
|
|
252
|
+
|
|
253
|
+
Get options for registering a new passkey. **Requires authentication.**
|
|
254
|
+
|
|
255
|
+
```typescript
|
|
256
|
+
const options = await auth.webauthn.getRegistrationOptions({
|
|
257
|
+
accessToken: 'user-access-token',
|
|
258
|
+
});
|
|
259
|
+
// Returns PublicKeyCredentialCreationOptions for navigator.credentials.create()
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
#### `webauthn.verifyRegistration(options)`
|
|
263
|
+
|
|
264
|
+
Verify and store a new passkey registration. **Requires authentication.**
|
|
265
|
+
|
|
266
|
+
```typescript
|
|
267
|
+
const result = await auth.webauthn.verifyRegistration({
|
|
268
|
+
accessToken: 'user-access-token',
|
|
269
|
+
registration: credentialResponse, // From navigator.credentials.create()
|
|
270
|
+
deviceName: 'My MacBook', // Optional friendly name
|
|
271
|
+
});
|
|
272
|
+
// Returns: { success: true, credential_id: string, device_name?: string }
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
#### `webauthn.getAuthenticationOptions(options)`
|
|
276
|
+
|
|
277
|
+
Get options for signing in with a passkey. Does not require authentication.
|
|
278
|
+
|
|
279
|
+
```typescript
|
|
280
|
+
const options = await auth.webauthn.getAuthenticationOptions({
|
|
281
|
+
email: 'user@example.com', // Optional - provides allowCredentials hint
|
|
282
|
+
});
|
|
283
|
+
// Returns PublicKeyCredentialRequestOptions for navigator.credentials.get()
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
#### `webauthn.authenticate(options)`
|
|
287
|
+
|
|
288
|
+
Verify passkey authentication and receive tokens.
|
|
289
|
+
|
|
290
|
+
```typescript
|
|
291
|
+
const tokens = await auth.webauthn.authenticate({
|
|
292
|
+
authentication: credentialResponse, // From navigator.credentials.get()
|
|
293
|
+
});
|
|
294
|
+
// Returns: { access_token, refresh_token, token_type, expires_in }
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
#### Complete Passkey Flow Example
|
|
298
|
+
|
|
299
|
+
```typescript
|
|
300
|
+
import { OvixaAuth } from '@ovixa/auth-client';
|
|
301
|
+
|
|
302
|
+
const auth = new OvixaAuth({
|
|
303
|
+
authUrl: 'https://auth.ovixa.io',
|
|
304
|
+
realmId: 'your-realm-id',
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
// Register a passkey (user must be logged in)
|
|
308
|
+
async function registerPasskey(accessToken: string) {
|
|
309
|
+
// 1. Get registration options from server
|
|
310
|
+
const options = await auth.webauthn.getRegistrationOptions({ accessToken });
|
|
311
|
+
|
|
312
|
+
// 2. Create credential using browser API
|
|
313
|
+
const credential = await navigator.credentials.create({
|
|
314
|
+
publicKey: options,
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
if (!credential) throw new Error('Registration cancelled');
|
|
318
|
+
|
|
319
|
+
// 3. Verify with server
|
|
320
|
+
const result = await auth.webauthn.verifyRegistration({
|
|
321
|
+
accessToken,
|
|
322
|
+
registration: credential as PublicKeyCredential,
|
|
323
|
+
deviceName: 'My Device',
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
return result;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// Sign in with passkey
|
|
330
|
+
async function signInWithPasskey(email?: string) {
|
|
331
|
+
// 1. Get authentication options
|
|
332
|
+
const options = await auth.webauthn.getAuthenticationOptions({ email });
|
|
333
|
+
|
|
334
|
+
// 2. Get credential using browser API
|
|
335
|
+
const credential = await navigator.credentials.get({
|
|
336
|
+
publicKey: options,
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
if (!credential) throw new Error('Authentication cancelled');
|
|
340
|
+
|
|
341
|
+
// 3. Verify and get tokens
|
|
342
|
+
const tokens = await auth.webauthn.authenticate({
|
|
343
|
+
authentication: credential as PublicKeyCredential,
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
return tokens;
|
|
347
|
+
}
|
|
348
|
+
```
|
|
349
|
+
|
|
246
350
|
### OAuth
|
|
247
351
|
|
|
248
352
|
#### `getOAuthUrl(options)`
|
|
@@ -683,6 +683,37 @@ var OvixaAuth = class {
|
|
|
683
683
|
}
|
|
684
684
|
return new OvixaAuthAdmin(this.config);
|
|
685
685
|
}
|
|
686
|
+
/**
|
|
687
|
+
* Get the WebAuthn API interface for passkey operations.
|
|
688
|
+
*
|
|
689
|
+
* The WebAuthn namespace provides methods for registering and authenticating
|
|
690
|
+
* with passkeys, as well as managing existing passkeys.
|
|
691
|
+
*
|
|
692
|
+
* @returns OvixaAuthWebAuthn interface for passkey operations
|
|
693
|
+
*
|
|
694
|
+
* @example
|
|
695
|
+
* ```typescript
|
|
696
|
+
* const auth = new OvixaAuth({
|
|
697
|
+
* authUrl: 'https://auth.ovixa.io',
|
|
698
|
+
* realmId: 'your-realm',
|
|
699
|
+
* });
|
|
700
|
+
*
|
|
701
|
+
* // Register a passkey
|
|
702
|
+
* const options = await auth.webauthn.getRegistrationOptions({ accessToken });
|
|
703
|
+
*
|
|
704
|
+
* // Authenticate with passkey
|
|
705
|
+
* const authOptions = await auth.webauthn.getAuthenticationOptions();
|
|
706
|
+
*
|
|
707
|
+
* // List passkeys
|
|
708
|
+
* const { passkeys } = await auth.webauthn.listPasskeys({ accessToken });
|
|
709
|
+
*
|
|
710
|
+
* // Delete a passkey
|
|
711
|
+
* await auth.webauthn.deletePasskey({ accessToken, credentialId });
|
|
712
|
+
* ```
|
|
713
|
+
*/
|
|
714
|
+
get webauthn() {
|
|
715
|
+
return new OvixaAuthWebAuthn(this.config);
|
|
716
|
+
}
|
|
686
717
|
};
|
|
687
718
|
var OvixaAuthAdmin = class {
|
|
688
719
|
config;
|
|
@@ -773,9 +804,382 @@ var OvixaAuthAdmin = class {
|
|
|
773
804
|
}
|
|
774
805
|
}
|
|
775
806
|
};
|
|
807
|
+
var OvixaAuthWebAuthn = class {
|
|
808
|
+
config;
|
|
809
|
+
constructor(config) {
|
|
810
|
+
this.config = config;
|
|
811
|
+
}
|
|
812
|
+
/**
|
|
813
|
+
* Get registration options for creating a new passkey.
|
|
814
|
+
*
|
|
815
|
+
* The user must be logged in to register a passkey. Use the returned options
|
|
816
|
+
* with navigator.credentials.create() to create the credential.
|
|
817
|
+
*
|
|
818
|
+
* @param options - Registration options with access token
|
|
819
|
+
* @returns Registration options to pass to navigator.credentials.create()
|
|
820
|
+
* @throws {OvixaAuthError} If request fails
|
|
821
|
+
*/
|
|
822
|
+
async getRegistrationOptions(options) {
|
|
823
|
+
const url = `${this.config.authUrl}/webauthn/register/options`;
|
|
824
|
+
try {
|
|
825
|
+
const response = await fetch(url, {
|
|
826
|
+
method: "POST",
|
|
827
|
+
headers: {
|
|
828
|
+
"Content-Type": "application/json",
|
|
829
|
+
Authorization: `Bearer ${options.accessToken}`
|
|
830
|
+
},
|
|
831
|
+
body: JSON.stringify({
|
|
832
|
+
realm_id: this.config.realmId
|
|
833
|
+
})
|
|
834
|
+
});
|
|
835
|
+
if (!response.ok) {
|
|
836
|
+
await this.handleErrorResponse(response);
|
|
837
|
+
}
|
|
838
|
+
return await response.json();
|
|
839
|
+
} catch (error) {
|
|
840
|
+
if (error instanceof OvixaAuthError) {
|
|
841
|
+
throw error;
|
|
842
|
+
}
|
|
843
|
+
if (error instanceof Error) {
|
|
844
|
+
throw new OvixaAuthError(`Network error: ${error.message}`, "NETWORK_ERROR");
|
|
845
|
+
}
|
|
846
|
+
throw new OvixaAuthError("Request failed", "REQUEST_FAILED");
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
/**
|
|
850
|
+
* Verify a passkey registration and store the credential.
|
|
851
|
+
*
|
|
852
|
+
* Call this after navigator.credentials.create() succeeds to complete the
|
|
853
|
+
* registration on the server.
|
|
854
|
+
*
|
|
855
|
+
* @param options - Verification options with registration response
|
|
856
|
+
* @returns Registration response with credential ID
|
|
857
|
+
* @throws {OvixaAuthError} If verification fails
|
|
858
|
+
*/
|
|
859
|
+
async verifyRegistration(options) {
|
|
860
|
+
const url = `${this.config.authUrl}/webauthn/register/verify`;
|
|
861
|
+
const body = {
|
|
862
|
+
realm_id: this.config.realmId,
|
|
863
|
+
registration: options.registration
|
|
864
|
+
};
|
|
865
|
+
if (options.deviceName) {
|
|
866
|
+
body.device_name = options.deviceName;
|
|
867
|
+
}
|
|
868
|
+
try {
|
|
869
|
+
const response = await fetch(url, {
|
|
870
|
+
method: "POST",
|
|
871
|
+
headers: {
|
|
872
|
+
"Content-Type": "application/json",
|
|
873
|
+
Authorization: `Bearer ${options.accessToken}`
|
|
874
|
+
},
|
|
875
|
+
body: JSON.stringify(body)
|
|
876
|
+
});
|
|
877
|
+
if (!response.ok) {
|
|
878
|
+
await this.handleErrorResponse(response);
|
|
879
|
+
}
|
|
880
|
+
return await response.json();
|
|
881
|
+
} catch (error) {
|
|
882
|
+
if (error instanceof OvixaAuthError) {
|
|
883
|
+
throw error;
|
|
884
|
+
}
|
|
885
|
+
if (error instanceof Error) {
|
|
886
|
+
throw new OvixaAuthError(`Network error: ${error.message}`, "NETWORK_ERROR");
|
|
887
|
+
}
|
|
888
|
+
throw new OvixaAuthError("Request failed", "REQUEST_FAILED");
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
/**
|
|
892
|
+
* Get authentication options for signing in with a passkey.
|
|
893
|
+
*
|
|
894
|
+
* Use the returned options with navigator.credentials.get() to authenticate.
|
|
895
|
+
*
|
|
896
|
+
* @param options - Optional email for allowCredentials hint
|
|
897
|
+
* @returns Authentication options to pass to navigator.credentials.get()
|
|
898
|
+
* @throws {OvixaAuthError} If request fails
|
|
899
|
+
*/
|
|
900
|
+
async getAuthenticationOptions(options) {
|
|
901
|
+
const url = `${this.config.authUrl}/webauthn/authenticate/options`;
|
|
902
|
+
const body = {
|
|
903
|
+
realm_id: this.config.realmId
|
|
904
|
+
};
|
|
905
|
+
if (options?.email) {
|
|
906
|
+
body.email = options.email;
|
|
907
|
+
}
|
|
908
|
+
try {
|
|
909
|
+
const response = await fetch(url, {
|
|
910
|
+
method: "POST",
|
|
911
|
+
headers: {
|
|
912
|
+
"Content-Type": "application/json"
|
|
913
|
+
},
|
|
914
|
+
body: JSON.stringify(body)
|
|
915
|
+
});
|
|
916
|
+
if (!response.ok) {
|
|
917
|
+
await this.handleErrorResponse(response);
|
|
918
|
+
}
|
|
919
|
+
return await response.json();
|
|
920
|
+
} catch (error) {
|
|
921
|
+
if (error instanceof OvixaAuthError) {
|
|
922
|
+
throw error;
|
|
923
|
+
}
|
|
924
|
+
if (error instanceof Error) {
|
|
925
|
+
throw new OvixaAuthError(`Network error: ${error.message}`, "NETWORK_ERROR");
|
|
926
|
+
}
|
|
927
|
+
throw new OvixaAuthError("Request failed", "REQUEST_FAILED");
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
/**
|
|
931
|
+
* Verify a passkey authentication and get tokens.
|
|
932
|
+
*
|
|
933
|
+
* Call this after navigator.credentials.get() succeeds to complete the
|
|
934
|
+
* authentication and receive access/refresh tokens.
|
|
935
|
+
*
|
|
936
|
+
* @param options - Verification options with authentication response
|
|
937
|
+
* @returns Token response with access and refresh tokens
|
|
938
|
+
* @throws {OvixaAuthError} If verification fails
|
|
939
|
+
*/
|
|
940
|
+
async authenticate(options) {
|
|
941
|
+
const url = `${this.config.authUrl}/webauthn/authenticate/verify`;
|
|
942
|
+
const body = {
|
|
943
|
+
realm_id: this.config.realmId,
|
|
944
|
+
authentication: options.authentication
|
|
945
|
+
};
|
|
946
|
+
try {
|
|
947
|
+
const response = await fetch(url, {
|
|
948
|
+
method: "POST",
|
|
949
|
+
headers: {
|
|
950
|
+
"Content-Type": "application/json"
|
|
951
|
+
},
|
|
952
|
+
body: JSON.stringify(body)
|
|
953
|
+
});
|
|
954
|
+
if (!response.ok) {
|
|
955
|
+
await this.handleErrorResponse(response);
|
|
956
|
+
}
|
|
957
|
+
return await response.json();
|
|
958
|
+
} catch (error) {
|
|
959
|
+
if (error instanceof OvixaAuthError) {
|
|
960
|
+
throw error;
|
|
961
|
+
}
|
|
962
|
+
if (error instanceof Error) {
|
|
963
|
+
throw new OvixaAuthError(`Network error: ${error.message}`, "NETWORK_ERROR");
|
|
964
|
+
}
|
|
965
|
+
throw new OvixaAuthError("Request failed", "REQUEST_FAILED");
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
/**
|
|
969
|
+
* List all passkeys for the authenticated user.
|
|
970
|
+
*
|
|
971
|
+
* @param options - Options with access token
|
|
972
|
+
* @returns List of passkeys
|
|
973
|
+
* @throws {OvixaAuthError} If request fails
|
|
974
|
+
*
|
|
975
|
+
* @example
|
|
976
|
+
* ```typescript
|
|
977
|
+
* const { passkeys } = await auth.webauthn.listPasskeys({ accessToken });
|
|
978
|
+
* for (const passkey of passkeys) {
|
|
979
|
+
* console.log(passkey.device_name, passkey.created_at);
|
|
980
|
+
* }
|
|
981
|
+
* ```
|
|
982
|
+
*/
|
|
983
|
+
async listPasskeys(options) {
|
|
984
|
+
const url = `${this.config.authUrl}/webauthn/passkeys`;
|
|
985
|
+
try {
|
|
986
|
+
const response = await fetch(url, {
|
|
987
|
+
method: "POST",
|
|
988
|
+
headers: {
|
|
989
|
+
"Content-Type": "application/json",
|
|
990
|
+
Authorization: `Bearer ${options.accessToken}`
|
|
991
|
+
},
|
|
992
|
+
body: JSON.stringify({
|
|
993
|
+
realm_id: this.config.realmId
|
|
994
|
+
})
|
|
995
|
+
});
|
|
996
|
+
if (!response.ok) {
|
|
997
|
+
await this.handleErrorResponse(response);
|
|
998
|
+
}
|
|
999
|
+
return await response.json();
|
|
1000
|
+
} catch (error) {
|
|
1001
|
+
if (error instanceof OvixaAuthError) {
|
|
1002
|
+
throw error;
|
|
1003
|
+
}
|
|
1004
|
+
if (error instanceof Error) {
|
|
1005
|
+
throw new OvixaAuthError(`Network error: ${error.message}`, "NETWORK_ERROR");
|
|
1006
|
+
}
|
|
1007
|
+
throw new OvixaAuthError("Request failed", "REQUEST_FAILED");
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
/**
|
|
1011
|
+
* Delete a passkey for the authenticated user.
|
|
1012
|
+
*
|
|
1013
|
+
* @param options - Options with access token and credential ID
|
|
1014
|
+
* @returns Success response
|
|
1015
|
+
* @throws {OvixaAuthError} If deletion fails
|
|
1016
|
+
*
|
|
1017
|
+
* @example
|
|
1018
|
+
* ```typescript
|
|
1019
|
+
* await auth.webauthn.deletePasskey({
|
|
1020
|
+
* accessToken,
|
|
1021
|
+
* credentialId: 'credential-id-to-delete',
|
|
1022
|
+
* });
|
|
1023
|
+
* ```
|
|
1024
|
+
*/
|
|
1025
|
+
async deletePasskey(options) {
|
|
1026
|
+
const url = `${this.config.authUrl}/webauthn/passkeys/${encodeURIComponent(options.credentialId)}`;
|
|
1027
|
+
try {
|
|
1028
|
+
const response = await fetch(url, {
|
|
1029
|
+
method: "DELETE",
|
|
1030
|
+
headers: {
|
|
1031
|
+
"Content-Type": "application/json",
|
|
1032
|
+
Authorization: `Bearer ${options.accessToken}`
|
|
1033
|
+
},
|
|
1034
|
+
body: JSON.stringify({
|
|
1035
|
+
realm_id: this.config.realmId
|
|
1036
|
+
})
|
|
1037
|
+
});
|
|
1038
|
+
if (!response.ok) {
|
|
1039
|
+
await this.handleErrorResponse(response);
|
|
1040
|
+
}
|
|
1041
|
+
return await response.json();
|
|
1042
|
+
} catch (error) {
|
|
1043
|
+
if (error instanceof OvixaAuthError) {
|
|
1044
|
+
throw error;
|
|
1045
|
+
}
|
|
1046
|
+
if (error instanceof Error) {
|
|
1047
|
+
throw new OvixaAuthError(`Network error: ${error.message}`, "NETWORK_ERROR");
|
|
1048
|
+
}
|
|
1049
|
+
throw new OvixaAuthError("Request failed", "REQUEST_FAILED");
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
/**
|
|
1053
|
+
* Handle error response from the server.
|
|
1054
|
+
* @throws {OvixaAuthError} Always throws with appropriate error details
|
|
1055
|
+
*/
|
|
1056
|
+
async handleErrorResponse(response) {
|
|
1057
|
+
const errorBody = await response.json().catch(() => ({}));
|
|
1058
|
+
const errorData = errorBody;
|
|
1059
|
+
let errorCode;
|
|
1060
|
+
let errorMessage;
|
|
1061
|
+
if (typeof errorData.error === "object" && errorData.error) {
|
|
1062
|
+
errorCode = errorData.error.code || this.mapHttpStatusToErrorCode(response.status);
|
|
1063
|
+
errorMessage = errorData.error.message || "Request failed";
|
|
1064
|
+
} else {
|
|
1065
|
+
errorCode = this.mapHttpStatusToErrorCode(response.status);
|
|
1066
|
+
errorMessage = typeof errorData.error === "string" ? errorData.error : "Request failed";
|
|
1067
|
+
}
|
|
1068
|
+
throw new OvixaAuthError(errorMessage, errorCode, response.status);
|
|
1069
|
+
}
|
|
1070
|
+
/**
|
|
1071
|
+
* Map HTTP status codes to error codes.
|
|
1072
|
+
*/
|
|
1073
|
+
mapHttpStatusToErrorCode(status) {
|
|
1074
|
+
switch (status) {
|
|
1075
|
+
case 400:
|
|
1076
|
+
return "BAD_REQUEST";
|
|
1077
|
+
case 401:
|
|
1078
|
+
return "UNAUTHORIZED";
|
|
1079
|
+
case 403:
|
|
1080
|
+
return "FORBIDDEN";
|
|
1081
|
+
case 404:
|
|
1082
|
+
return "NOT_FOUND";
|
|
1083
|
+
case 429:
|
|
1084
|
+
return "RATE_LIMITED";
|
|
1085
|
+
case 500:
|
|
1086
|
+
case 502:
|
|
1087
|
+
case 503:
|
|
1088
|
+
return "SERVER_ERROR";
|
|
1089
|
+
default:
|
|
1090
|
+
return "UNKNOWN_ERROR";
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
};
|
|
1094
|
+
function arrayBufferToBase64Url(buffer) {
|
|
1095
|
+
const bytes = new Uint8Array(buffer);
|
|
1096
|
+
let binary = "";
|
|
1097
|
+
for (const byte of bytes) {
|
|
1098
|
+
binary += String.fromCharCode(byte);
|
|
1099
|
+
}
|
|
1100
|
+
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
1101
|
+
}
|
|
1102
|
+
function base64UrlToArrayBuffer(base64url) {
|
|
1103
|
+
const base64 = base64url.replace(/-/g, "+").replace(/_/g, "/");
|
|
1104
|
+
const padded = base64 + "=".repeat((4 - base64.length % 4) % 4);
|
|
1105
|
+
const binary = atob(padded);
|
|
1106
|
+
const bytes = new Uint8Array(binary.length);
|
|
1107
|
+
for (let i = 0; i < binary.length; i++) {
|
|
1108
|
+
bytes[i] = binary.charCodeAt(i);
|
|
1109
|
+
}
|
|
1110
|
+
return bytes.buffer;
|
|
1111
|
+
}
|
|
1112
|
+
function prepareRegistrationOptions(options) {
|
|
1113
|
+
return {
|
|
1114
|
+
challenge: base64UrlToArrayBuffer(options.challenge),
|
|
1115
|
+
rp: options.rp,
|
|
1116
|
+
user: {
|
|
1117
|
+
id: new TextEncoder().encode(options.user.id),
|
|
1118
|
+
name: options.user.name,
|
|
1119
|
+
displayName: options.user.displayName
|
|
1120
|
+
},
|
|
1121
|
+
pubKeyCredParams: options.pubKeyCredParams,
|
|
1122
|
+
excludeCredentials: options.excludeCredentials.map((cred) => ({
|
|
1123
|
+
id: base64UrlToArrayBuffer(cred.id),
|
|
1124
|
+
type: cred.type,
|
|
1125
|
+
transports: cred.transports
|
|
1126
|
+
})),
|
|
1127
|
+
authenticatorSelection: options.authenticatorSelection,
|
|
1128
|
+
timeout: options.timeout,
|
|
1129
|
+
attestation: options.attestation
|
|
1130
|
+
};
|
|
1131
|
+
}
|
|
1132
|
+
function prepareAuthenticationOptions(options) {
|
|
1133
|
+
return {
|
|
1134
|
+
challenge: base64UrlToArrayBuffer(options.challenge),
|
|
1135
|
+
rpId: options.rpId,
|
|
1136
|
+
allowCredentials: options.allowCredentials.map((cred) => ({
|
|
1137
|
+
id: base64UrlToArrayBuffer(cred.id),
|
|
1138
|
+
type: cred.type,
|
|
1139
|
+
transports: cred.transports
|
|
1140
|
+
})),
|
|
1141
|
+
userVerification: options.userVerification,
|
|
1142
|
+
timeout: options.timeout
|
|
1143
|
+
};
|
|
1144
|
+
}
|
|
1145
|
+
function formatRegistrationResponse(credential) {
|
|
1146
|
+
const response = credential.response;
|
|
1147
|
+
return {
|
|
1148
|
+
id: credential.id,
|
|
1149
|
+
rawId: arrayBufferToBase64Url(credential.rawId),
|
|
1150
|
+
type: "public-key",
|
|
1151
|
+
response: {
|
|
1152
|
+
clientDataJSON: arrayBufferToBase64Url(response.clientDataJSON),
|
|
1153
|
+
attestationObject: arrayBufferToBase64Url(response.attestationObject),
|
|
1154
|
+
transports: response.getTransports?.()
|
|
1155
|
+
},
|
|
1156
|
+
authenticatorAttachment: credential.authenticatorAttachment
|
|
1157
|
+
};
|
|
1158
|
+
}
|
|
1159
|
+
function formatAuthenticationResponse(credential) {
|
|
1160
|
+
const response = credential.response;
|
|
1161
|
+
return {
|
|
1162
|
+
id: credential.id,
|
|
1163
|
+
rawId: arrayBufferToBase64Url(credential.rawId),
|
|
1164
|
+
type: "public-key",
|
|
1165
|
+
response: {
|
|
1166
|
+
clientDataJSON: arrayBufferToBase64Url(response.clientDataJSON),
|
|
1167
|
+
authenticatorData: arrayBufferToBase64Url(response.authenticatorData),
|
|
1168
|
+
signature: arrayBufferToBase64Url(response.signature),
|
|
1169
|
+
userHandle: response.userHandle ? arrayBufferToBase64Url(response.userHandle) : null
|
|
1170
|
+
},
|
|
1171
|
+
authenticatorAttachment: credential.authenticatorAttachment
|
|
1172
|
+
};
|
|
1173
|
+
}
|
|
776
1174
|
|
|
777
1175
|
export {
|
|
778
1176
|
OvixaAuthError,
|
|
779
|
-
OvixaAuth
|
|
1177
|
+
OvixaAuth,
|
|
1178
|
+
arrayBufferToBase64Url,
|
|
1179
|
+
base64UrlToArrayBuffer,
|
|
1180
|
+
prepareRegistrationOptions,
|
|
1181
|
+
prepareAuthenticationOptions,
|
|
1182
|
+
formatRegistrationResponse,
|
|
1183
|
+
formatAuthenticationResponse
|
|
780
1184
|
};
|
|
781
|
-
//# sourceMappingURL=chunk-
|
|
1185
|
+
//# sourceMappingURL=chunk-5QEKSWV4.js.map
|