-
Notifications
You must be signed in to change notification settings - Fork 201
/
encode.js
172 lines (166 loc) · 7.18 KB
/
encode.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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
// This is the JS that is injected into the web.
// Modifying this will modify the communication between the phone app and the
// web app.
var __webauthn_interface__;
var __webauthn_hooks__;
(function (__webauthn_hooks__) {
//Adding event listener to the interface for replies by default
__webauthn_interface__.addEventListener('message', onReply);
// pendingResolveGet/Create is the thunk to resolve an outstanding get request.
var pendingResolveGet = null;
var pendingResolveCreate = null;
// pendingRejectGet/Create is the thunk to fail an outstanding request.
var pendingRejectGet = null;
var pendingRejectCreate = null;
// create overrides 'navigator.credentials.create' which proxies webauthn requests
// to the create embedder
function create(request) {
if (!("publicKey" in request)) {
return __webauthn_hooks__.originalCreateFunction(request);
}
var ret = new Promise(function (resolve, reject) {
pendingResolveCreate = resolve;
pendingRejectCreate = reject;
});
var temppk = request.publicKey;
if (temppk.hasOwnProperty('challenge')) {
var str = CM_base64url_encode(temppk.challenge);
temppk.challenge = str;
}
if (temppk.hasOwnProperty('user') && temppk.user.hasOwnProperty('id')) {
var encodedString = CM_base64url_encode(temppk.user.id);
temppk.user.id = encodedString;
}
var jsonObj = {"type":"create", "request":temppk}
var json = JSON.stringify(jsonObj);
__webauthn_interface__.postMessage(json);
return ret;
}
__webauthn_hooks__.create = create;
// get overrides `navigator.credentials.get` and proxies any WebAuthn
// requests to the get embedder.
function get(request) {
if (!("publicKey" in request)) {
return __webauthn_hooks__.originalGetFunction(request);
}
var ret = new Promise(function (resolve, reject) {
pendingResolveGet = resolve;
pendingRejectGet = reject;
});
var temppk = request.publicKey;
if (temppk.hasOwnProperty('challenge')) {
var str = CM_base64url_encode(temppk.challenge);
temppk.challenge = str;
}
var jsonObj = {"type":"get", "request":temppk}
var json = JSON.stringify(jsonObj);
__webauthn_interface__.postMessage(json);
return ret;
}
__webauthn_hooks__.get = get;
// The embedder gives replies back here, caught by the event listener.
function onReply(msg) {
var reply = JSON.parse(msg.data);
var type = reply[2];
if(type === "get") {
onReplyGet(reply);
} else if (type === "create") {
onReplyCreate(reply);
} else {
console.log("Incorrect response format for reply");
}
}
// Resolves what is expected for get, called when the embedder is ready
function onReplyGet(reply) {
if (pendingResolveGet === null || pendingRejectGet === null) {
console.log("Reply failure: Resolve: " + pendingResolveCreate +
" and reject: " + pendingRejectCreate);
return;
}
if (reply[0] != 'success') {
var reject = pendingRejectGet;
pendingResolveGet = null;
pendingRejectGet = null;
reject(new DOMException(reply[1], "NotAllowedError"));
return;
}
var cred = credentialManagerDecode(reply[1]);
var resolve = pendingResolveGet;
pendingResolveGet = null;
pendingRejectGet = null;
resolve(cred);
}
__webauthn_hooks__.onReplyGet = onReplyGet;
// This a specific decoder for expected types contained in PublicKeyCredential json
function CM_base64url_decode(value) {
var m = value.length % 4;
return Uint8Array.from(atob(value.replace(/-/g, '+')
.replace(/_/g, '/')
.padEnd(value.length + (m === 0 ? 0 : 4 - m), '=')), function (c)
{ return c.charCodeAt(0); }).buffer;
}
__webauthn_hooks__.CM_base64url_decode = CM_base64url_decode;
function CM_base64url_encode(buffer) {
return btoa(Array.from(new Uint8Array(buffer), function (b)
{ return String.fromCharCode(b); }).join(''))
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+${'$'}/, '');
}
__webauthn_hooks__.CM_base64url_encode = CM_base64url_encode;
// Resolves what is expected for create, called when the embedder is ready
function onReplyCreate(reply) {
if (pendingResolveCreate === null || pendingRejectCreate === null) {
console.log("Reply failure: Resolve: " + pendingResolveCreate +
" and reject: " + pendingRejectCreate);
return;
}
if (reply[0] != 'success') {
var reject = pendingRejectCreate;
pendingResolveCreate = null;
pendingRejectCreate = null;
reject(new DOMException(reply[1], "NotAllowedError"));
return;
}
var cred = credentialManagerDecode(reply[1]);
var resolve = pendingResolveCreate;
pendingResolveCreate = null;
pendingRejectCreate = null;
resolve(cred);
}
__webauthn_hooks__.onReplyCreate = onReplyCreate;
/**
* This decodes the output from the credential manager flow to parse back into URL format. Both
* get and create flows ultimately return a PublicKeyCredential object.
* @param json_result
*/
function credentialManagerDecode(decoded_reply) {
decoded_reply.rawId = CM_base64url_decode(decoded_reply.rawId);
decoded_reply.response.clientDataJSON = CM_base64url_decode(decoded_reply.response.clientDataJSON);
if (decoded_reply.response.hasOwnProperty('attestationObject')) {
decoded_reply.response.attestationObject = CM_base64url_decode(decoded_reply.response.attestationObject);
}
if (decoded_reply.response.hasOwnProperty('authenticatorData')) {
decoded_reply.response.authenticatorData = CM_base64url_decode(decoded_reply.response.authenticatorData);
}
if (decoded_reply.response.hasOwnProperty('signature')) {
decoded_reply.response.signature = CM_base64url_decode(decoded_reply.response.signature);
}
if (decoded_reply.response.hasOwnProperty('userHandle')) {
decoded_reply.response.userHandle = CM_base64url_decode(decoded_reply.response.userHandle);
}
decoded_reply.getClientExtensionResults = function getClientExtensionResults() { return {}; };
return decoded_reply;
}
})(__webauthn_hooks__ || (__webauthn_hooks__ = {}));
__webauthn_hooks__.originalGetFunction = navigator.credentials.get;
__webauthn_hooks__.originalCreateFunction = navigator.credentials.create;
navigator.credentials.get = __webauthn_hooks__.get;
navigator.credentials.create = __webauthn_hooks__.create;
// Some sites test that `typeof window.PublicKeyCredential` is
// `function`.
window.PublicKeyCredential = (function () { });
window.PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable =
function () {
return Promise.resolve(false);
};