@dfns/sdk-react-native 0.3.2 → 0.3.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.
Files changed (3) hide show
  1. package/index.d.ts +8 -6
  2. package/index.js +100 -15
  3. package/package.json +3 -2
package/index.d.ts CHANGED
@@ -1,14 +1,16 @@
1
1
  import { AllowCredential, CredentialSigner, CredentialStore, Fido2Assertion, Fido2Attestation, UserRegistrationChallenge } from '@dfns/sdk';
2
2
  export declare const DEFAULT_WAIT_TIMEOUT = 60000;
3
- export declare class PasskeySigner implements CredentialSigner<Fido2Assertion>, CredentialStore<Fido2Attestation> {
4
- private options;
5
- constructor(options: {
6
- rpId: string;
7
- timeout?: number;
8
- });
3
+ type PasskeysOptions = {
4
+ rpId: string;
5
+ timeout?: number;
6
+ };
7
+ export declare class PasskeysSigner implements CredentialSigner<Fido2Assertion>, CredentialStore<Fido2Attestation> {
8
+ private inner;
9
+ constructor(options: PasskeysOptions);
9
10
  sign(challenge: string, allowCredentials: {
10
11
  key: AllowCredential[];
11
12
  webauthn: AllowCredential[];
12
13
  }): Promise<Fido2Assertion>;
13
14
  create(challenge: UserRegistrationChallenge): Promise<Fido2Attestation>;
14
15
  }
16
+ export {};
package/index.js CHANGED
@@ -1,11 +1,19 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.PasskeySigner = exports.DEFAULT_WAIT_TIMEOUT = void 0;
3
+ exports.PasskeysSigner = exports.DEFAULT_WAIT_TIMEOUT = void 0;
4
4
  const utils_1 = require("@dfns/sdk/utils");
5
- const buffer_1 = require("buffer");
5
+ const react_native_1 = require("react-native");
6
6
  const react_native_passkey_1 = require("react-native-passkey");
7
7
  exports.DEFAULT_WAIT_TIMEOUT = 60000;
8
- class PasskeySigner {
8
+ const b64StandardToUrlSafe = (standard) => {
9
+ return standard.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
10
+ };
11
+ const b64UrlSafeToStandard = (urlSafe) => {
12
+ return (urlSafe + '==='.slice((urlSafe.length + 3) % 4)).replace(/-/g, '+').replace(/_/g, '/');
13
+ };
14
+ // react-native-passkey is incorrect encoding the credId with standard base64 for
15
+ // some reason. we have to undo that.
16
+ class AndroidPasskeys {
9
17
  constructor(options) {
10
18
  this.options = options;
11
19
  }
@@ -25,11 +33,11 @@ class PasskeySigner {
25
33
  return {
26
34
  kind: 'Fido2',
27
35
  credentialAssertion: {
28
- credId: credential.id,
29
- clientData: (0, utils_1.toBase64Url)(buffer_1.Buffer.from(credential.response.clientDataJSON)),
30
- authenticatorData: (0, utils_1.toBase64Url)(buffer_1.Buffer.from(credential.response.authenticatorData)),
31
- signature: (0, utils_1.toBase64Url)(buffer_1.Buffer.from(credential.response.signature)),
32
- userHandle: credential.response.userHandle ? (0, utils_1.toBase64Url)(buffer_1.Buffer.from(credential.response.userHandle)) : '',
36
+ credId: b64StandardToUrlSafe(credential.id),
37
+ clientData: credential.response.clientDataJSON,
38
+ authenticatorData: credential.response.authenticatorData,
39
+ signature: credential.response.signature,
40
+ userHandle: credential.response.userHandle,
33
41
  },
34
42
  };
35
43
  }
@@ -52,17 +60,94 @@ class PasskeySigner {
52
60
  timeout: this.options.timeout ?? exports.DEFAULT_WAIT_TIMEOUT,
53
61
  };
54
62
  const result = await react_native_passkey_1.Passkey.register(options);
55
- if (result === null) {
56
- throw Error(`Failed to create and sign with Passkey credential`);
57
- }
58
63
  return {
59
64
  credentialKind: 'Fido2',
60
65
  credentialInfo: {
61
- credId: result.id,
62
- attestationData: (0, utils_1.toBase64Url)(buffer_1.Buffer.from(result.response.attestationObject)),
63
- clientData: (0, utils_1.toBase64Url)(buffer_1.Buffer.from(result.response.clientDataJSON)),
66
+ credId: b64StandardToUrlSafe(result.id),
67
+ attestationData: result.response.attestationObject,
68
+ clientData: result.response.clientDataJSON,
69
+ },
70
+ };
71
+ }
72
+ }
73
+ // react-native-passkey's iOS implementation is not WebAuthn spec compliant. all values
74
+ // are standard base64 encoded instead of base64url encoded. we have to convert the
75
+ // encoding in both directions.
76
+ class iOsPasskeys {
77
+ constructor(options) {
78
+ this.options = options;
79
+ }
80
+ async sign(challenge, allowCredentials) {
81
+ const request = {
82
+ challenge: b64UrlSafeToStandard(challenge),
83
+ allowCredentials: allowCredentials.webauthn.map(({ id, type, transports }) => ({
84
+ id: b64UrlSafeToStandard(id),
85
+ type,
86
+ transports: transports ?? [],
87
+ })),
88
+ rpId: this.options.rpId,
89
+ userVerification: 'required',
90
+ timeout: this.options.timeout ?? exports.DEFAULT_WAIT_TIMEOUT,
91
+ };
92
+ const credential = await react_native_passkey_1.Passkey.authenticate(request);
93
+ return {
94
+ kind: 'Fido2',
95
+ credentialAssertion: {
96
+ credId: b64StandardToUrlSafe(credential.id),
97
+ clientData: b64StandardToUrlSafe(credential.response.clientDataJSON),
98
+ authenticatorData: b64StandardToUrlSafe(credential.response.authenticatorData),
99
+ signature: b64StandardToUrlSafe(credential.response.signature),
100
+ userHandle: b64StandardToUrlSafe(credential.response.userHandle),
101
+ },
102
+ };
103
+ }
104
+ async create(challenge) {
105
+ const options = {
106
+ challenge: b64UrlSafeToStandard(challenge.challenge),
107
+ pubKeyCredParams: challenge.pubKeyCredParams,
108
+ rp: challenge.rp,
109
+ user: {
110
+ displayName: challenge.user.displayName,
111
+ id: (0, utils_1.toBase64Url)(challenge.user.id),
112
+ name: challenge.user.name,
113
+ },
114
+ attestation: challenge.attestation,
115
+ excludeCredentials: challenge.excludeCredentials.map((cred) => ({
116
+ id: cred.id,
117
+ type: cred.type,
118
+ })),
119
+ authenticatorSelection: challenge.authenticatorSelection,
120
+ timeout: this.options.timeout ?? exports.DEFAULT_WAIT_TIMEOUT,
121
+ };
122
+ const result = await react_native_passkey_1.Passkey.register(options);
123
+ return {
124
+ credentialKind: 'Fido2',
125
+ credentialInfo: {
126
+ credId: b64StandardToUrlSafe(result.id),
127
+ attestationData: b64StandardToUrlSafe(result.response.attestationObject),
128
+ clientData: b64StandardToUrlSafe(result.response.clientDataJSON),
64
129
  },
65
130
  };
66
131
  }
67
132
  }
68
- exports.PasskeySigner = PasskeySigner;
133
+ class PasskeysSigner {
134
+ constructor(options) {
135
+ switch (react_native_1.Platform.OS) {
136
+ case 'android':
137
+ this.inner = new AndroidPasskeys(options);
138
+ break;
139
+ case 'ios':
140
+ this.inner = new iOsPasskeys(options);
141
+ break;
142
+ default:
143
+ throw Error(`${react_native_1.Platform.OS} not supported`);
144
+ }
145
+ }
146
+ async sign(challenge, allowCredentials) {
147
+ return this.inner.sign(challenge, allowCredentials);
148
+ }
149
+ async create(challenge) {
150
+ return this.inner.create(challenge);
151
+ }
152
+ }
153
+ exports.PasskeysSigner = PasskeysSigner;
package/package.json CHANGED
@@ -1,14 +1,15 @@
1
1
  {
2
2
  "name": "@dfns/sdk-react-native",
3
- "version": "0.3.2",
3
+ "version": "0.3.3",
4
4
  "dependencies": {
5
5
  "buffer": "6.0.3",
6
6
  "cross-fetch": "3.1.6",
7
+ "react-native": "0.73.2",
7
8
  "react-native-passkey": "^2.1.1",
8
9
  "uuid": "9.0.0"
9
10
  },
10
11
  "peerDependencies": {
11
- "@dfns/sdk": "0.3.2"
12
+ "@dfns/sdk": "0.3.3"
12
13
  },
13
14
  "main": "./index.js",
14
15
  "type": "commonjs"