-
Notifications
You must be signed in to change notification settings - Fork 17
/
webauthn.js
98 lines (94 loc) · 3.09 KB
/
webauthn.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
class WebAuthn {
// Decode a base64 string into a Uint8Array.
static _decodeBuffer(value) {
return Uint8Array.from(atob(value), c => c.charCodeAt(0));
}
// Encode an ArrayBuffer into a base64 string.
static _encodeBuffer(value) {
return btoa(new Uint8Array(value).reduce((s, byte) => s + String.fromCharCode(byte), ''));
}
// Checks whether the status returned matches the status given.
static _checkStatus(status) {
return res => {
if (res.status === status) {
return res;
}
throw new Error(res.statusText);
};
}
register() {
return fetch('/webauthn/registration/start', {
method: 'POST'
})
.then(WebAuthn._checkStatus(200))
.then(res => res.json())
.then(res => {
res.publicKey.challenge = WebAuthn._decodeBuffer(res.publicKey.challenge);
res.publicKey.user.id = WebAuthn._decodeBuffer(res.publicKey.user.id);
if (res.publicKey.excludeCredentials) {
for (var i = 0; i < res.publicKey.excludeCredentials.length; i++) {
res.publicKey.excludeCredentials[i].id = WebAuthn._decodeBuffer(res.publicKey.excludeCredentials[i].id);
}
}
return res;
})
.then(res => navigator.credentials.create(res))
.then(credential => {
return fetch('/webauthn/registration/finish', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
id: credential.id,
rawId: WebAuthn._encodeBuffer(credential.rawId),
response: {
attestationObject: WebAuthn._encodeBuffer(credential.response.attestationObject),
clientDataJSON: WebAuthn._encodeBuffer(credential.response.clientDataJSON)
},
type: credential.type
}),
})
})
.then(WebAuthn._checkStatus(201));
}
login() {
return fetch('/webauthn/login/start', {
method: 'POST'
})
.then(WebAuthn._checkStatus(200))
.then(res => res.json())
.then(res => {
res.publicKey.challenge = WebAuthn._decodeBuffer(res.publicKey.challenge);
if (res.publicKey.allowCredentials) {
for (let i = 0; i < res.publicKey.allowCredentials.length; i++) {
res.publicKey.allowCredentials[i].id = WebAuthn._decodeBuffer(res.publicKey.allowCredentials[i].id);
}
}
return res;
})
.then(res => navigator.credentials.get(res))
.then(credential => {
return fetch('/webauthn/login/finish', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
id: credential.id,
rawId: WebAuthn._encodeBuffer(credential.rawId),
response: {
clientDataJSON: WebAuthn._encodeBuffer(credential.response.clientDataJSON),
authenticatorData: WebAuthn._encodeBuffer(credential.response.authenticatorData),
signature: WebAuthn._encodeBuffer(credential.response.signature),
userHandle: WebAuthn._encodeBuffer(credential.response.userHandle),
},
type: credential.type
}),
})
})
.then(WebAuthn._checkStatus(200));
}
}