Skip to content

Commit 75bab8a

Browse files
authored
Add spec for Server Certificate API (#2099) (#2179)
* add server certificate api spec
1 parent 9ce94fb commit 75bab8a

File tree

1 file changed

+304
-0
lines changed

1 file changed

+304
-0
lines changed

specs/ServerCertificate.md

Lines changed: 304 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,304 @@
1+
Server Certificate API
2+
===
3+
# Background
4+
5+
The WebView2 team has been asked for an API to intercept when WebView2 cannot verify a server's digital certificate while loading a web page.
6+
This API provides an option to trust the server's TLS certificate at the application level and render the page without prompting the user about the TLS error or can cancel the request.
7+
8+
# Description
9+
10+
We propose adding `ServerCertificateErrorDetected` API that allows you to verify TLS certificates with errors, and either continue the request
11+
to load the resource or cancel the request.
12+
13+
When the event is raised, WebView2 will pass a `CoreWebView2ServerCertificateErrorDetectedEventArgs` , which lets you view the TLS certificate request Uri, error information, inspect the certificate metadata and several options for responding to the TLS request.
14+
15+
* You can perform your own verification of the certificate and allow the request to proceed if you trust it.
16+
* You can choose to cancel the request.
17+
* You can choose to display the default TLS interstitial page to let user respond to the request for page navigations. For others TLS certificate is rejected and the request is cancelled.
18+
19+
We also propose adding a `ClearServerCertificateErrorActions` API which clears cached `COREWEBVIEW2_SERVER_CERTIFICATE_ERROR_ACTION_ALWAYS_ALLOW` response for proceeding with TLS certificate errors.
20+
21+
# Examples
22+
23+
## Win32 C++
24+
``` cpp
25+
// When WebView2 doesn't trust a TLS certificate but host app does, this example bypasses
26+
// the default TLS interstitial page using the ReceivingServerCertificateError event handler and
27+
// continues the navigation to a server. Otherwise, cancel the request.
28+
void SettingsComponent::ToggleCustomServerCertificateSupport()
29+
{
30+
auto m_webview11 = m_webview.query<ICoreWebView2_11>();
31+
if (m_webview11)
32+
{
33+
if (m_ServerCertificateErrorToken.value == 0)
34+
{
35+
CHECK_FAILURE(m_webview11->add_ServerCertificateErrorDetected(
36+
Callback<ICoreWebView2ServerCertificateErrorDetectedEventHandler>(
37+
[this](
38+
ICoreWebView2* sender,
39+
ICoreWebView2ServerCertificateErrorDetectedEventArgs* args) {
40+
COREWEBVIEW2_WEB_ERROR_STATUS errorStatus;
41+
CHECK_FAILURE(args->get_ErrorStatus(&errorStatus));
42+
43+
wil::com_ptr<ICoreWebView2Certificate> certificate = nullptr;
44+
CHECK_FAILURE(args->get_ServerCertificate(&certificate));
45+
46+
// Continues the request to a server with a TLS certificate if the error status
47+
// is of type `COREWEBVIEW2_WEB_ERROR_STATUS_CERTIFICATE_IS_INVALID`
48+
// and trusted by the host app.
49+
if (errorStatus == COREWEBVIEW2_WEB_ERROR_STATUS_CERTIFICATE_IS_INVALID &&
50+
ValidateServerCertificate(certificate.get()))
51+
{
52+
CHECK_FAILURE(args->put_Action(
53+
COREWEBVIEW2_SERVER_CERTIFICATE_ERROR_ACTION_ALWAYS_ALLOW));
54+
}
55+
else
56+
{
57+
// Cancel the request for other TLS certificate error types or if untrusted by the host app.
58+
CHECK_FAILURE(args->put_Action(
59+
COREWEBVIEW2_SERVER_CERTIFICATE_ERROR_ACTION_CANCEL));
60+
}
61+
return S_OK;
62+
})
63+
.Get(),
64+
&m_ServerCertificateErrorToken));
65+
}
66+
else
67+
{
68+
CHECK_FAILURE(m_webview11->remove_ServerCertificateErrorDetected(
69+
m_ServerCertificateErrorToken));
70+
m_ServerCertificateErrorToken.value = 0;
71+
}
72+
}
73+
else
74+
{
75+
FeatureNotAvailable();
76+
}
77+
}
78+
79+
// Function to validate the server certificate for untrusted root or self-signed certificate.
80+
// You may also choose to defer server certificate validation.
81+
bool ValidateServerCertificate(ICoreWebView2Certificate* certificate)
82+
{
83+
// You may want to validate certificates in different ways depending on your app and scenario.
84+
// One way might be the following:
85+
// First, get the list of host app trusted certificates and its thumbprint.
86+
87+
// Then get the last chain element using `ICoreWebView2Certificate::get_PemEncodedIssuerCertificateChain`
88+
// that contains the raw data of the untrusted root CA/self-signed certificate. Get the untrusted
89+
// root CA/self signed certificate thumbprint from the raw certificate data and
90+
// validate the thumbprint against the host app trusted certificate list.
91+
92+
// Finally, return true if it exists in the host app's certificate trusted list, or otherwise return false.
93+
return true;
94+
}
95+
96+
// This example clears `AlwaysAllow` response that are added for proceeding with TLS certificate errors.
97+
if (m_webview11)
98+
{
99+
CHECK_FAILURE(m_webview11->ClearServerCertificateErrorActions(
100+
Callback<
101+
ICoreWebView2ClearServerCertificateErrorActionsCompletedHandler>(
102+
[](HRESULT result) -> HRESULT {
103+
auto showDialog = [result] {
104+
MessageBox(
105+
nullptr,
106+
(result == S_OK)
107+
? L"Clear server certificate error actions are succeeded."
108+
: L"Clear server certificate error actions are failed.",
109+
L"Clear server certificate error actions",
110+
MB_OK);
111+
};
112+
m_appWindow->RunAsync([showDialog]() { showDialog(); });
113+
return S_OK;
114+
})
115+
.Get()));
116+
}
117+
```
118+
## . NET/ WinRT
119+
```c#
120+
// When WebView2 doesn't trust a TLS certificate but host app does, this example bypasses
121+
// the default TLS interstitial page using the ReceivingServerCertificateError event handler and
122+
// continues the navigation to a server. Otherwise, cancel the request.
123+
private bool _isServerCertificateError = false;
124+
void ToggleCustomServerCertificateSupport()
125+
{
126+
if (!_isServerCertificateError)
127+
{
128+
webView.CoreWebView2.ServerCertificateErrorDetected += WebView_ServerCertificateErrorDetected;
129+
}
130+
else
131+
{
132+
webView.CoreWebView2.ServerCertificateErrorDetected -= WebView_ServerCertificateErrorDetected;
133+
}
134+
_isServerCertificateError = !_isServerCertificateError;
135+
136+
MessageBox.Show(this, "Custom server certificate support has been" +
137+
(_isServerCertificateError ? "enabled" : "disabled"),
138+
"Custom server certificate support");
139+
}
140+
141+
void WebView_ServerCertificateErrorDetected(object sender, CoreWebView2ServerCertificateErrorDetectedEventArgs e)
142+
{
143+
CoreWebView2Certificate certificate = e.ServerCertificate;
144+
145+
// Continues the request to a server with a TLS certificate if the error status
146+
// is of type `COREWEBVIEW2_WEB_ERROR_STATUS_CERTIFICATE_IS_INVALID`
147+
// and trusted by the host app.
148+
if (e.ErrorStatus == CoreWebView2WebErrorStatus.CertificateIsInvalid &&
149+
ValidateServerCertificate(certificate))
150+
{
151+
e.Action = CoreWebView2ServerCertificateErrorAction.AlwaysAllow;
152+
}
153+
else
154+
{
155+
// Cancel the request for other TLS certificate error types or if untrusted by the host app.
156+
e.Action = CoreWebView2ServerCertificateErrorAction.Cancel;
157+
}
158+
}
159+
160+
// Function to validate the server certificate for untrusted root or self-signed certificate.
161+
// You may also choose to defer server certificate validation.
162+
bool ValidateServerCertificate(CoreWebView2Certificate certificate)
163+
{
164+
// You may want to validate certificates in different ways depending on your app and scenario.
165+
// One way might be the following:
166+
// First, get the list of host app trusted certificates and its thumbprint.
167+
168+
// Then get the last chain element using `ICoreWebView2Certificate::get_PemEncodedIssuerCertificateChain`
169+
// that contains the raw data of the untrusted root CA/self-signed certificate. Get the untrusted
170+
// root CA/self signed certificate thumbprint from the raw certificate data and
171+
// validate the thumbprint against the host app trusted certificate list.
172+
173+
// Finally, return true if it exists in the host app's certificate trusted list, or otherwise return false.
174+
return true;
175+
}
176+
177+
// This example clears `AlwaysAllow` response that are added for proceeding with TLS certificate errors.
178+
async void ClearServerCertificateErrorActions()
179+
{
180+
bool isSuccessful = await webView.CoreWebView2.ClearServerCertificateErrorActionsAsync();
181+
MessageBox.Show(this, "message", "Clear server certificate error actions are succeeded");
182+
}
183+
```
184+
185+
# API Details
186+
187+
## Win32 C++
188+
``` cpp
189+
[v1_enum] typedef enum COREWEBVIEW2_SERVER_CERTIFICATE_ERROR_ACTION {
190+
/// Indicates to ignore the warning and continue the request with the TLS
191+
/// certificate. This decision is cached for the RequestUri's host and the server
192+
/// certificate in the session.
193+
COREWEBVIEW2_SERVER_CERTIFICATE_ERROR_ACTION_ALWAYS_ALLOW,
194+
195+
/// Indicates to reject the certificate and cancel the request.
196+
COREWEBVIEW2_SERVER_CERTIFICATE_ERROR_ACTION_CANCEL,
197+
198+
/// Indicates to display the default TLS interstitial page to user for page navigations.
199+
/// For others TLS certificate is rejected and the request is cancelled.
200+
COREWEBVIEW2_SERVER_CERTIFICATE_ERROR_ACTION_DEFAULT
201+
} COREWEBVIEW2_SERVER_CERTIFICATE_ERROR_ACTION;
202+
203+
[uuid(4B7FF0D2-8203-48B0-ACBF-ED9CFF82567A), object, pointer_default(unique)]
204+
interface ICoreWebView2_11 : ICoreWebView2_10 {
205+
/// Add an event handler for the ServerCertificateErrorDetected event.
206+
/// The ServerCertificateErrorDetected event is raised when the WebView2
207+
/// cannot verify server's digital certificate while loading a web page.
208+
///
209+
/// This event will raise for all web resources and follows the `WebResourceRequested` event.
210+
///
211+
/// If you don't handle the event, WebView2 will show the default TLS interstitial page to user.
212+
///
213+
/// WebView2 caches the response when action is `COREWEBVIEW2_SERVER_CERTIFICATE_ERROR_ACTION_ALWAYS_ALLOW`
214+
/// for the RequestUri's host and the server certificate in the session and the `ServerCertificateErrorDetected`
215+
/// event won't be raised again.
216+
///
217+
/// To raise the event again you must clear the cache using `ClearServerCertificateErrorActions`.
218+
///
219+
/// \snippet SettingsComponent.cpp ServerCertificateErrorDetected1
220+
HRESULT add_ServerCertificateErrorDetected(
221+
[in] ICoreWebView2ServerCertificateErrorDetectedEventHandler*
222+
eventHandler,
223+
[out] EventRegistrationToken* token);
224+
/// Remove an event handler previously added with add_ServerCertificateErrorDetected.
225+
HRESULT remove_ServerCertificateErrorDetected([in] EventRegistrationToken token);
226+
227+
/// Clears all cached decisions to proceed with TLS certificate errors from the
228+
/// ServerCertificateErrorDetected event for all WebView2's sharing the same session.
229+
HRESULT ClearServerCertificateErrorActions(
230+
[in] ICoreWebView2ClearServerCertificateErrorActionsCompletedHandler*
231+
handler);
232+
}
233+
234+
/// Receives the result of the `ClearServerCertificateErrorActions` method.
235+
[uuid(2F7B173D-3CE1-4945-BDE6-94F4C57B7209), object, pointer_default(unique)]
236+
interface ICoreWebView2ClearServerCertificateErrorActionsCompletedHandler : IUnknown {
237+
/// Provides the result of the corresponding asynchronous method.
238+
HRESULT Invoke([in] HRESULT errorCode);
239+
}
240+
241+
/// An event handler for the `ServerCertificateErrorDetected` event.
242+
[uuid(AAC28793-11FC-4EE5-A8D4-25A0279B1551), object, pointer_default(unique)]
243+
interface ICoreWebView2ServerCertificateErrorDetectedEventHandler : IUnknown {
244+
/// Provides the event args for the corresponding event.
245+
HRESULT Invoke([in] ICoreWebView2* sender,
246+
[in] ICoreWebView2ServerCertificateErrorDetectedEventArgs*
247+
args);
248+
}
249+
250+
/// Event args for the `ServerCertificateErrorDetected` event.
251+
[uuid(24EADEE7-31F9-447F-9FE7-7C13DC738C32), object, pointer_default(unique)]
252+
interface ICoreWebView2ServerCertificateErrorDetectedEventArgs : IUnknown {
253+
/// The TLS error code for the invalid certificate.
254+
[propget] HRESULT ErrorStatus([out, retval] COREWEBVIEW2_WEB_ERROR_STATUS* value);
255+
256+
/// URI associated with the request for the invalid certificate.
257+
[propget] HRESULT RequestUri([out, retval] LPWSTR* value);
258+
259+
/// Returns the server certificate.
260+
[propget] HRESULT ServerCertificate([out, retval] ICoreWebView2Certificate** value);
261+
262+
/// The action of the server certificate error detection.
263+
/// The default value is `COREWEBVIEW2_SERVER_CERTIFICATE_ERROR_ACTION_DEFAULT`.
264+
[propget] HRESULT Action([out, retval] COREWEBVIEW2_SERVER_CERTIFICATE_ERROR_ACTION* value);
265+
266+
/// Sets the `Action` property.
267+
[propput] HRESULT Action([in] COREWEBVIEW2_SERVER_CERTIFICATE_ERROR_ACTION value);
268+
269+
/// Returns an `ICoreWebView2Deferral` object. Use this operation to
270+
/// complete the event at a later time.
271+
HRESULT GetDeferral([out, retval] ICoreWebView2Deferral** deferral);
272+
}
273+
```
274+
275+
```c# (but really MIDL3)
276+
namespace Microsoft.Web.WebView2.Core
277+
{
278+
enum CoreWebView2ServerCertificateErrorAction
279+
{
280+
AlwaysAllow = 0,
281+
Cancel = 1,
282+
Default = 2
283+
};
284+
285+
runtimeclass CoreWebView2ServerCertificateErrorDetectedEventArgs
286+
{
287+
CoreWebView2WebErrorStatus ErrorStatus { get; };
288+
String RequestUri { get; };
289+
CoreWebView2Certificate ServerCertificate { get; };
290+
CoreWebView2ServerCertificateErrorAction Action { get; set; };
291+
Windows.Foundation.Deferral GetDeferral();
292+
}
293+
294+
runtimeclass CoreWebView2
295+
{
296+
// ...
297+
[interface_name("Microsoft.Web.WebView2.Core.ICoreWebView2_11")]
298+
{
299+
event Windows.Foundation.TypedEventHandler<CoreWebView2, CoreWebView2ServerCertificateErrorDetectedEventArgs> ServerCertificateErrorDetected;
300+
Windows.Foundation.IAsyncAction ClearServerCertificateErrorActionsAsync();
301+
}
302+
}
303+
}
304+
```

0 commit comments

Comments
 (0)