ngx-webauthn 0.0.2
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 +412 -0
- package/fesm2022/ngx-webauthn.mjs +1058 -0
- package/fesm2022/ngx-webauthn.mjs.map +1 -0
- package/index.d.ts +702 -0
- package/package.json +23 -0
package/README.md
ADDED
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
# ngx-webauthn
|
|
2
|
+
|
|
3
|
+
A comprehensive Angular library that provides a clean, type-safe abstraction over the WebAuthn API. This library simplifies the implementation of passwordless authentication using passkeys, security keys, and biometric authentication.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- ๐ **Complete WebAuthn Support** - Registration and authentication flows
|
|
8
|
+
- ๐ก๏ธ **Type Safety** - Full TypeScript support with comprehensive interfaces
|
|
9
|
+
- ๐ฏ **Clean API** - Simplified abstraction over complex WebAuthn APIs
|
|
10
|
+
- ๐ฑ **Cross-Platform** - Works with platform authenticators (Face ID, Touch ID, Windows Hello)
|
|
11
|
+
- ๐ **Security Keys** - Support for external authenticators (YubiKey, etc.)
|
|
12
|
+
- โก **RxJS Integration** - Observable-based API that fits naturally into Angular
|
|
13
|
+
- ๐งช **Well Tested** - Comprehensive unit test coverage
|
|
14
|
+
- ๐ **Browser Support** - Works in all modern browsers with WebAuthn support
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install ngx-webauthn
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Quick Start
|
|
23
|
+
|
|
24
|
+
### 1. Configure the Service
|
|
25
|
+
|
|
26
|
+
First, configure the WebAuthn service with your relying party information:
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
// main.ts (standalone app)
|
|
30
|
+
import { bootstrapApplication } from '@angular/platform-browser';
|
|
31
|
+
import { provideWebAuthn } from 'ngx-webauthn';
|
|
32
|
+
import { AppComponent } from './app/app.component';
|
|
33
|
+
|
|
34
|
+
bootstrapApplication(AppComponent, {
|
|
35
|
+
providers: [
|
|
36
|
+
provideWebAuthn(
|
|
37
|
+
{ name: 'My App', id: 'myapp.com' }, // Required relying party config
|
|
38
|
+
{ defaultTimeout: 30000 } // Optional overrides
|
|
39
|
+
),
|
|
40
|
+
// ... other providers
|
|
41
|
+
],
|
|
42
|
+
});
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
// app.module.ts (module-based app)
|
|
47
|
+
import { NgModule } from '@angular/core';
|
|
48
|
+
import { BrowserModule } from '@angular/platform-browser';
|
|
49
|
+
import { WEBAUTHN_CONFIG, createWebAuthnConfig } from 'ngx-webauthn';
|
|
50
|
+
|
|
51
|
+
@NgModule({
|
|
52
|
+
imports: [BrowserModule],
|
|
53
|
+
providers: [
|
|
54
|
+
{
|
|
55
|
+
provide: WEBAUTHN_CONFIG,
|
|
56
|
+
useValue: createWebAuthnConfig({ name: 'My App', id: 'myapp.com' }, { defaultTimeout: 30000 }),
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
// ...
|
|
60
|
+
})
|
|
61
|
+
export class AppModule {}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### 2. Use the Service with Presets
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
import { Component, inject } from '@angular/core';
|
|
68
|
+
import { WebAuthnService, WebAuthnError, WebAuthnErrorType } from 'ngx-webauthn';
|
|
69
|
+
|
|
70
|
+
@Component({
|
|
71
|
+
selector: 'app-auth',
|
|
72
|
+
template: `
|
|
73
|
+
<div>
|
|
74
|
+
<button (click)="register()" [disabled]="!isSupported">Register</button>
|
|
75
|
+
<button (click)="authenticate()" [disabled]="!isSupported">Authenticate</button>
|
|
76
|
+
<p *ngIf="!isSupported">WebAuthn is not supported in this browser</p>
|
|
77
|
+
</div>
|
|
78
|
+
`,
|
|
79
|
+
})
|
|
80
|
+
export class AuthComponent {
|
|
81
|
+
private webAuthnService = inject(WebAuthnService);
|
|
82
|
+
|
|
83
|
+
isSupported = this.webAuthnService.isSupported();
|
|
84
|
+
|
|
85
|
+
register() {
|
|
86
|
+
this.webAuthnService
|
|
87
|
+
.register({
|
|
88
|
+
username: 'john.doe@example.com',
|
|
89
|
+
preset: 'passkey', // Easy preset configuration!
|
|
90
|
+
})
|
|
91
|
+
.subscribe({
|
|
92
|
+
next: (result) => {
|
|
93
|
+
console.log('Registration successful:', result);
|
|
94
|
+
// Send result.credentialId, result.publicKey, etc. to your server
|
|
95
|
+
},
|
|
96
|
+
error: (error: WebAuthnError) => {
|
|
97
|
+
console.error('Registration failed:', error.message);
|
|
98
|
+
if (error.type === WebAuthnErrorType.USER_CANCELLED) {
|
|
99
|
+
// User cancelled or denied permission
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
authenticate() {
|
|
106
|
+
this.webAuthnService
|
|
107
|
+
.authenticate({
|
|
108
|
+
preset: 'passkey', // Uses the same preset for consistency
|
|
109
|
+
// Optional: specify allowed credentials
|
|
110
|
+
allowCredentials: ['credential-id-from-registration'],
|
|
111
|
+
})
|
|
112
|
+
.subscribe({
|
|
113
|
+
next: (result) => {
|
|
114
|
+
console.log('Authentication successful:', result);
|
|
115
|
+
// Send result to your server for verification
|
|
116
|
+
},
|
|
117
|
+
error: (error: WebAuthnError) => {
|
|
118
|
+
console.error('Authentication failed:', error.message);
|
|
119
|
+
},
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### 2. Check Browser Support
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
import { Component, OnInit, inject } from '@angular/core';
|
|
129
|
+
import { WebAuthnService } from 'ngx-webauthn';
|
|
130
|
+
|
|
131
|
+
@Component({
|
|
132
|
+
selector: 'app-support-check',
|
|
133
|
+
template: `
|
|
134
|
+
<div *ngIf="support">
|
|
135
|
+
<p>WebAuthn Support: {{ support.isSupported ? 'Yes' : 'No' }}</p>
|
|
136
|
+
<p>Platform Authenticator: {{ support.isPlatformAuthenticatorAvailable ? 'Available' : 'Not Available' }}</p>
|
|
137
|
+
<p>Supported Transports: {{ support.supportedTransports.join(', ') }}</p>
|
|
138
|
+
</div>
|
|
139
|
+
`,
|
|
140
|
+
})
|
|
141
|
+
export class SupportCheckComponent implements OnInit {
|
|
142
|
+
private webAuthnService = inject(WebAuthnService);
|
|
143
|
+
support: any;
|
|
144
|
+
|
|
145
|
+
ngOnInit() {
|
|
146
|
+
this.webAuthnService.getSupport().subscribe({
|
|
147
|
+
next: (support) => {
|
|
148
|
+
this.support = support;
|
|
149
|
+
},
|
|
150
|
+
error: (error) => {
|
|
151
|
+
console.error('Failed to check WebAuthn support:', error);
|
|
152
|
+
},
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Features Overview
|
|
159
|
+
|
|
160
|
+
### ๐ฏ **Preset System**
|
|
161
|
+
|
|
162
|
+
The library includes intelligent presets for common WebAuthn use cases:
|
|
163
|
+
|
|
164
|
+
- **`passkey`** - Modern passwordless authentication with credential syncing
|
|
165
|
+
- **`secondFactor`** - Traditional 2FA with hardware security keys
|
|
166
|
+
- **`deviceBound`** - High-security single-device authentication
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
// Simple preset usage
|
|
170
|
+
this.webAuthnService.register({ username: 'user@example.com', preset: 'passkey' });
|
|
171
|
+
|
|
172
|
+
// Preset with custom overrides
|
|
173
|
+
this.webAuthnService.register({
|
|
174
|
+
username: 'user@example.com',
|
|
175
|
+
preset: 'passkey',
|
|
176
|
+
authenticatorSelection: { userVerification: 'required' },
|
|
177
|
+
});
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### ๐ง **Enhanced Configuration**
|
|
181
|
+
|
|
182
|
+
The new configuration system ensures proper security defaults:
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
interface WebAuthnConfig {
|
|
186
|
+
relyingParty: {
|
|
187
|
+
name: string; // Required: Your app name
|
|
188
|
+
id?: string; // Optional: Your domain
|
|
189
|
+
};
|
|
190
|
+
defaultTimeout?: number; // Default: 60000ms
|
|
191
|
+
defaultAlgorithms?: PublicKeyCredentialParameters[];
|
|
192
|
+
enforceUserVerification?: boolean; // Default: false
|
|
193
|
+
defaultAttestation?: AttestationConveyancePreference;
|
|
194
|
+
defaultAuthenticatorSelection?: AuthenticatorSelectionCriteria;
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### ๐ **Flexible Input Types**
|
|
199
|
+
|
|
200
|
+
Supports multiple input formats for maximum flexibility:
|
|
201
|
+
|
|
202
|
+
```typescript
|
|
203
|
+
// High-level config (recommended)
|
|
204
|
+
service.register({ username: 'user@example.com', preset: 'passkey' });
|
|
205
|
+
|
|
206
|
+
// Native WebAuthn options
|
|
207
|
+
service.register(nativeCreationOptions);
|
|
208
|
+
|
|
209
|
+
// JSON WebAuthn options (base64url strings)
|
|
210
|
+
service.register(jsonCreationOptions);
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
## API Reference
|
|
214
|
+
|
|
215
|
+
### WebAuthnService
|
|
216
|
+
|
|
217
|
+
#### Methods
|
|
218
|
+
|
|
219
|
+
##### `isSupported(): boolean`
|
|
220
|
+
|
|
221
|
+
Synchronously checks if WebAuthn is supported in the current browser.
|
|
222
|
+
|
|
223
|
+
##### `getSupport(): Observable<WebAuthnSupport>`
|
|
224
|
+
|
|
225
|
+
Returns detailed information about WebAuthn support including platform authenticator availability.
|
|
226
|
+
|
|
227
|
+
##### `register(input: RegisterInput): Observable<RegistrationResponse>`
|
|
228
|
+
|
|
229
|
+
Registers a new WebAuthn credential. Accepts either high-level config objects or direct WebAuthn options.
|
|
230
|
+
|
|
231
|
+
##### `authenticate(input: AuthenticateInput): Observable<AuthenticationResponse>`
|
|
232
|
+
|
|
233
|
+
Authenticates using an existing WebAuthn credential. Accepts either high-level config objects or direct WebAuthn options.
|
|
234
|
+
|
|
235
|
+
### Response Types
|
|
236
|
+
|
|
237
|
+
#### RegistrationResponse
|
|
238
|
+
|
|
239
|
+
```typescript
|
|
240
|
+
interface RegistrationResponse {
|
|
241
|
+
success: boolean;
|
|
242
|
+
credentialId: string; // Base64url encoded
|
|
243
|
+
publicKey?: string; // Base64url encoded (if available)
|
|
244
|
+
transports?: AuthenticatorTransport[];
|
|
245
|
+
rawResponse?: WebAuthnRegistrationResult; // For advanced usage
|
|
246
|
+
}
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
#### AuthenticationResponse
|
|
250
|
+
|
|
251
|
+
```typescript
|
|
252
|
+
interface AuthenticationResponse {
|
|
253
|
+
success: boolean;
|
|
254
|
+
credentialId: string; // Base64url encoded
|
|
255
|
+
userHandle?: string; // Base64url encoded
|
|
256
|
+
rawResponse?: WebAuthnAuthenticationResult; // For advanced usage
|
|
257
|
+
}
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
#### WebAuthnRegistrationResult
|
|
261
|
+
|
|
262
|
+
```typescript
|
|
263
|
+
interface WebAuthnRegistrationResult {
|
|
264
|
+
credentialId: string; // Base64url encoded
|
|
265
|
+
publicKey: string; // Base64url encoded
|
|
266
|
+
attestationObject: string; // Base64url encoded
|
|
267
|
+
clientDataJSON: string; // Base64url encoded
|
|
268
|
+
transports?: AuthenticatorTransport[];
|
|
269
|
+
}
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
#### WebAuthnAuthenticationResult
|
|
273
|
+
|
|
274
|
+
```typescript
|
|
275
|
+
interface WebAuthnAuthenticationResult {
|
|
276
|
+
credentialId: string; // Base64url encoded
|
|
277
|
+
authenticatorData: string; // Base64url encoded
|
|
278
|
+
clientDataJSON: string; // Base64url encoded
|
|
279
|
+
signature: string; // Base64url encoded
|
|
280
|
+
userHandle?: string; // Base64url encoded
|
|
281
|
+
}
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
### Error Handling
|
|
285
|
+
|
|
286
|
+
The library provides structured error handling through the `WebAuthnError` class:
|
|
287
|
+
|
|
288
|
+
```typescript
|
|
289
|
+
enum WebAuthnErrorType {
|
|
290
|
+
NOT_SUPPORTED = 'NOT_SUPPORTED',
|
|
291
|
+
NOT_ALLOWED = 'NOT_ALLOWED',
|
|
292
|
+
INVALID_STATE = 'INVALID_STATE',
|
|
293
|
+
CONSTRAINT = 'CONSTRAINT',
|
|
294
|
+
SECURITY = 'SECURITY',
|
|
295
|
+
NETWORK = 'NETWORK',
|
|
296
|
+
ABORT = 'ABORT',
|
|
297
|
+
TIMEOUT = 'TIMEOUT',
|
|
298
|
+
ENCODING = 'ENCODING',
|
|
299
|
+
UNKNOWN = 'UNKNOWN',
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
class WebAuthnError extends Error {
|
|
303
|
+
constructor(public readonly type: WebAuthnErrorType, message: string, public readonly originalError?: Error);
|
|
304
|
+
}
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
## Advanced Usage
|
|
308
|
+
|
|
309
|
+
### Using Direct WebAuthn Options
|
|
310
|
+
|
|
311
|
+
```typescript
|
|
312
|
+
// Native creation options
|
|
313
|
+
const creationOptions: PublicKeyCredentialCreationOptions = {
|
|
314
|
+
rp: { name: 'My App' },
|
|
315
|
+
user: {
|
|
316
|
+
id: new Uint8Array([1, 2, 3, 4]),
|
|
317
|
+
name: 'john.doe@example.com',
|
|
318
|
+
displayName: 'John Doe',
|
|
319
|
+
},
|
|
320
|
+
challenge: new Uint8Array([5, 6, 7, 8]),
|
|
321
|
+
pubKeyCredParams: [{ type: 'public-key', alg: -7 }],
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
this.webAuthnService.register(creationOptions).subscribe((result) => {
|
|
325
|
+
console.log('Registration complete:', result);
|
|
326
|
+
});
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
### JSON WebAuthn Options
|
|
330
|
+
|
|
331
|
+
```typescript
|
|
332
|
+
// JSON options also work (base64url strings instead of ArrayBuffers)
|
|
333
|
+
const jsonOptions: PublicKeyCredentialCreationOptionsJSON = {
|
|
334
|
+
rp: { name: 'My App' },
|
|
335
|
+
user: {
|
|
336
|
+
id: 'dXNlcklk',
|
|
337
|
+
name: 'john.doe@example.com',
|
|
338
|
+
displayName: 'John Doe',
|
|
339
|
+
},
|
|
340
|
+
challenge: 'Y2hhbGxlbmdl',
|
|
341
|
+
pubKeyCredParams: [{ type: 'public-key', alg: -7 }],
|
|
342
|
+
};
|
|
343
|
+
|
|
344
|
+
this.webAuthnService.register(jsonOptions).subscribe((result) => {
|
|
345
|
+
console.log('Registration complete:', result);
|
|
346
|
+
});
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
### Config with Custom Challenge
|
|
350
|
+
|
|
351
|
+
```typescript
|
|
352
|
+
// High-level config with custom challenge
|
|
353
|
+
this.webAuthnService
|
|
354
|
+
.register({
|
|
355
|
+
username: 'john.doe@example.com',
|
|
356
|
+
preset: 'passkey',
|
|
357
|
+
challenge: 'your-base64url-encoded-challenge',
|
|
358
|
+
authenticatorSelection: {
|
|
359
|
+
authenticatorAttachment: 'platform',
|
|
360
|
+
userVerification: 'required',
|
|
361
|
+
},
|
|
362
|
+
})
|
|
363
|
+
.subscribe((result) => {
|
|
364
|
+
console.log('Passkey registered:', result);
|
|
365
|
+
});
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
## Server-Side Integration
|
|
369
|
+
|
|
370
|
+
This library handles the client-side WebAuthn flow. You'll need a server-side implementation to:
|
|
371
|
+
|
|
372
|
+
1. **Generate challenges** (optional - library can generate them)
|
|
373
|
+
2. **Verify registration responses**
|
|
374
|
+
3. **Store public keys and credential metadata**
|
|
375
|
+
4. **Verify authentication responses**
|
|
376
|
+
|
|
377
|
+
Popular server-side libraries:
|
|
378
|
+
|
|
379
|
+
- **Node.js**: `@simplewebauthn/server`
|
|
380
|
+
- **.NET**: `Fido2NetLib`
|
|
381
|
+
- **Java**: `webauthn4j`
|
|
382
|
+
- **Python**: `py_webauthn`
|
|
383
|
+
- **Go**: `go-webauthn`
|
|
384
|
+
|
|
385
|
+
## Browser Support
|
|
386
|
+
|
|
387
|
+
WebAuthn is supported in all modern browsers:
|
|
388
|
+
|
|
389
|
+
- Chrome 67+
|
|
390
|
+
- Firefox 60+
|
|
391
|
+
- Safari 14+
|
|
392
|
+
- Edge 18+
|
|
393
|
+
|
|
394
|
+
Check current support at [caniuse.com/webauthn](https://caniuse.com/webauthn).
|
|
395
|
+
|
|
396
|
+
## Development
|
|
397
|
+
|
|
398
|
+
### Running unit tests
|
|
399
|
+
|
|
400
|
+
Run `nx test ngx-webauthn` to execute the unit tests.
|
|
401
|
+
|
|
402
|
+
### Building the library
|
|
403
|
+
|
|
404
|
+
Run `nx build ngx-webauthn` to build the library.
|
|
405
|
+
|
|
406
|
+
## Contributing
|
|
407
|
+
|
|
408
|
+
Contributions are welcome! Please read our contributing guidelines and submit pull requests to our repository.
|
|
409
|
+
|
|
410
|
+
## License
|
|
411
|
+
|
|
412
|
+
MIT License - see LICENSE file for details.
|