-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcredentials.js
97 lines (83 loc) · 3.11 KB
/
credentials.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
// simple javascript-only demonstration of the WebAuthn API
// run in a recent browser like Chrome over https or http://localhost
// NOTE: in other browsers, you may need some polyfills:
// https://github.com/MasterKale/webauthn-polyfills
const ES256 = -7; // ECDSA w/ SHA-256
const RS256 = -257; // RSA w/ SHA-256
const algo = { name: "ECDSA", namedCurve: "P-256", hash: { name: "SHA-256" } };
const userID = new Uint8Array(16);
self.crypto.getRandomValues(userID); // generate a random user ID
const challenge = new Uint8Array(32);
// convert an ArrayBuffer to a hexstring
function btoh(bin) {
bytes = new Uint8Array(bin);
return [...bytes].map(b => b.toString(16).padStart(2, "0")).join("");
}
/// REGISTER ///
const createCredentialDefaultArgs = {
publicKey: {
rp: {
name: "Example Relying Party"
},
user: {
id: userID.buffer,
name: "deleteMe",
displayName: "John Doe"
},
pubKeyCredParams: [
{ type: "public-key", alg: ES256 },
{ type: "public-key", alg: RS256 }
],
authenticatorSelection: {
residentKey: "required",
},
excludeCredentials: [],
challenge: challenge.buffer
}
};
// retrieve challenge and other options for credentials.get
function createOptions() {
self.crypto.getRandomValues(challenge);
return createCredentialDefaultArgs;
}
// create a credential
async function register() {
try {
options = createOptions();
attestation = await navigator.credentials.create(options);
console.log(attestation);
// prevent re-registration on the same authenticator:
createCredentialDefaultArgs.publicKey.excludeCredentials.push( {id: attestation.rawId, type: "public-key"} );
// TODO: store credential on backend
document.getElementById("message").innerHTML +=
`<br/>Created credential with ID <span class="code">${ btoh( (attestation.rawId) )}</span>` +
` for user ID <span class="code">${ btoh((options.publicKey.user.id)) }</span>`;
} catch (e) {
document.getElementById("message").innerHTML += `<br/><b>Registration failed</b>: ${ e.message }`;
}
}
/// AUTHENTICATE ///
const getCredentialDefaultArgs = {
publicKey: {
challenge: challenge.buffer
},
};
// retrieve challenge and other options for credentials.get
async function getOptions() {
self.crypto.getRandomValues(challenge);
return getCredentialDefaultArgs;
}
// get an assertion
async function authenticate() {
try {
options = await getOptions();
assertion = await navigator.credentials.get(options);
console.log(assertion);
// TODO: send assertion to backend for validation
document.getElementById("message").innerHTML +=
`<br/>Obtained assertion for credential ID <span class="code">${ btoh((assertion.rawId)) }</span>` +
` and user ID <span class="code">${ btoh((assertion.response.userHandle)) }</span>`;
} catch (e) {
document.getElementById("message").innerHTML += `<br/><b>Authentication failed</b>: ${ e.message }`;
}
}