Skip to content

Commit ac40dd9

Browse files
committed
Merge branch 'main' into alvinashcraft/main-winappsdk-background-tasks
2 parents fbc80ec + 488b5a7 commit ac40dd9

File tree

5 files changed

+330
-3
lines changed

5 files changed

+330
-3
lines changed

hub/apps/develop/security/index.md

+8-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
title: Security and identity
33
description: This article provides an index of development features that are related to security and identity scenarios in Windows apps.
44
ms.topic: overview
5-
ms.date: 09/09/2024
5+
ms.date: 03/19/2025
66
#customer intent: As a Windows developer, I want to learn to use security and identity features available to Windows apps so that I can build more secure apps.
77
---
88

@@ -16,7 +16,11 @@ Windows provides a wide variety of APIs related to security and identity scenari
1616

1717
### Windows App SDK APIs
1818

19-
The [Windows App SDK](../../windows-app-sdk/index.md) currently does not provide APIs related to security and identity scenarios other than a few helper APIs in the [Microsoft.Windows.Security.AccessControl](/windows/windows-app-sdk/api/winrt/microsoft.windows.security.accesscontrol) namespace. These APIs are related to named object sharing between packaged apps and Win32 applications.
19+
The [Windows App SDK](../../windows-app-sdk/index.md) provides APIs related to OAuth 2.0 authorization flows. There are also a few helper APIs in the [Microsoft.Windows.Security.AccessControl](/windows/windows-app-sdk/api/winrt/microsoft.windows.security.accesscontrol) namespace. These APIs are related to named object sharing between packaged apps and Win32 applications.
20+
21+
| Article | Description |
22+
|---------|-------------|
23+
| [Implement OAuth 2.0 functionality in Windows apps](oauth2.md) | The new OAuth2Manager in Windows App SDK enables desktop applications such as WinUI to seamlessly perform OAuth 2.0 authentication in Windows apps. This article describes how to implement OAuth 2.0 with the Windows App SDK. |
2024

2125
### WinRT APIs
2226

@@ -57,6 +61,8 @@ The .NET SDK also provides APIs related to security and identity scenarios for W
5761

5862
## Other features
5963

64+
The following articles provide information about features related to security and identity scenarios with passkeys for Windows apps.
65+
6066
| Topic | Description |
6167
|---------|-------------|
6268
| [Intro to passkeys](./intro.md) | Passkeys are simpler, stronger, passwordless sign-ins. |

hub/apps/develop/security/oauth2.md

+318
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,318 @@
1+
---
2+
title: Implement OAuth 2.0 functionality in Windows apps
3+
description: Learn how to implement OAuth 2.0 functionality in Windows apps using the Windows App SDK's OAuth2Manager.
4+
ms.date: 03/19/2025
5+
ms.topic: concept-article
6+
keywords: windows, winui, winrt, dotnet, security
7+
#customer intent: As a Windows app developer, I want to learn how to implement OAuth 2.0 functionality in my app so that I can securely authenticate users and access protected resources.
8+
---
9+
10+
# Implement OAuth 2.0 functionality in Windows apps
11+
12+
The [OAuth2Manager](/windows/windows-app-sdk/api/winrt/microsoft.security.authentication.oauth.oauth2manager) in Windows App SDK enables desktop applications such as WinUI 3 to seamlessly perform OAuth 2.0 authorization on Windows. **OAuth2Manager** API intentionally doesn't provide APIs for the implicit request and resource owner password credential because of the security concerns that entails. It's recommended to use the authorization code grant type using Proof Key for Code Exchange (PKCE). For more information, see the [PKCE RFC](https://tools.ietf.org/html/rfc7636).
13+
14+
## OAuth background
15+
16+
The Windows Runtime (WinRT) [WebAuthenticationBroker](/uwp/api/windows.security.authentication.web.webauthenticationbroker), primarily designed for UWP apps, presents several challenges when used in desktop apps. Key issues include the dependency on [ApplicationView](/uwp/api/windows.ui.viewmanagement.applicationview), which isn't compatible with desktop app frameworks. As a result, developers are forced to resort to workarounds involving interop interfaces and additional code to implement OAuth 2.0 functionality into WinUI 3 and other desktop apps.
17+
18+
## OAuth2Manager API in Windows App SDK
19+
20+
The **OAuth2Manager** API for Windows App SDK aims to provide a streamlined solution that meets the expectations of developers. It offers seamless OAuth 2.0 capabilities with full feature parity across all Windows platforms supported by Windows App SDK. The new API eliminates the need for cumbersome workarounds and simplifies the process of incorporating OAuth 2.0 functionality into desktop apps.
21+
22+
The **OAuth2Manager** is different than the **WebAuthenticationBroker** in WinRT. It follows OAuth 2.0 best practices more closely - e.g. using the user's default browser. The best practices for the API are taken from the IETF (Internet Engineering Task Force) OAuth 2.0 Authorization Framework [RFC 6749](https://tools.ietf.org/html/rfc6749), PKCE [RFC 7636](https://tools.ietf.org/html/rfc7636), and OAuth 2.0 for Native Apps [RFC 8252](https://tools.ietf.org/html/rfc8252).
23+
24+
## Perform OAuth 2.0 examples
25+
26+
A full WinUI 3 sample app is available on [GitHub](https://github.com/microsoft/WindowsAppSDK-Samples/tree/release/experimental/Samples/OAuth2Manager). The following sections provide code snippets for the most common OAuth 2.0 flows using the **OAuth2Manager** API.
27+
28+
### Authorization code request
29+
30+
The following example demonstrates how to perform an authorization code request using the **OAuth2Manager** in Windows App SDK:
31+
32+
# [C++](#tab/cpp)
33+
34+
```cpp
35+
// Get the WindowId for the application window
36+
Microsoft::UI::WindowId parentWindowId = this->AppWindow().Id();
37+
38+
AuthRequestParams authRequestParams = AuthRequestParams::CreateForAuthorizationCodeRequest(L"my_client_id",
39+
Uri(L"my-app:/oauth-callback/"));
40+
authRequestParams.Scope(L"user:email user:birthday");
41+
42+
AuthRequestResult authRequestResult = co_await OAuth2Manager::RequestAuthWithParamsAsync(parentWindowId,
43+
Uri(L"https://my.server.com/oauth/authorize"), authRequestParams);
44+
if (AuthResponse authResponse = authRequestResult.Response())
45+
{
46+
//To obtain the authorization code
47+
//authResponse.Code();
48+
49+
//To obtain the access token
50+
DoTokenExchange(authResponse);
51+
}
52+
else
53+
{
54+
AuthFailure authFailure = authRequestResult.Failure();
55+
NotifyFailure(authFailure.Error(), authFailure.ErrorDescription());
56+
}
57+
```
58+
59+
# [C#](#tab/csharp)
60+
61+
```csharp
62+
// Get the WindowId for the application window
63+
Microsoft.UI.WindowId parentWindowId = this.AppWindow.Id;
64+
65+
AuthRequestParams authRequestParams = AuthRequestParams.CreateForAuthorizationCodeRequest("my_client_id",
66+
new Uri("my-app:/oauth-callback/"));
67+
authRequestParams.Scope = "user:email user:birthday";
68+
69+
AuthRequestResult authRequestResult = await OAuth2Manager.RequestAuthWithParamsAsync(parentWindowId,
70+
new Uri("https://my.server.com/oauth/authorize"), authRequestParams);
71+
72+
if (AuthResponse authResponse == authRequestResult.Response)
73+
{
74+
//To obtain the authorization code
75+
//authResponse.Code;
76+
77+
//To obtain the access token
78+
DoTokenExchange(authResponse);
79+
}
80+
else
81+
{
82+
AuthFailure authFailure = authRequestResult.Failure;
83+
NotifyFailure(authFailure.Error, authFailure.ErrorDescription);
84+
}
85+
```
86+
87+
---
88+
89+
### Exchange authorization code for access token
90+
91+
The following example demonstrates how to exchange an authorization code for an access token using the **OAuth2Manager**:
92+
93+
# [C++](#tab/cpp)
94+
95+
```cpp
96+
AuthResponse authResponse = authRequestResult.Response();
97+
TokenRequestParams tokenRequestParams = TokenRequestParams::CreateForAuthorizationCodeRequest(authResponse);
98+
ClientAuthentication clientAuth = ClientAuthentication::CreateForBasicAuthorization(L"my_client_id",
99+
L"my_client_secret");
100+
101+
TokenRequestResult tokenRequestResult = co_await OAuth2Manager::RequestTokenAsync(
102+
Uri(L"https://my.server.com/oauth/token"), tokenRequestParams, clientAuth);
103+
if (TokenResponse tokenResponse = tokenRequestResult.Response())
104+
{
105+
String accessToken = tokenResponse.AccessToken();
106+
String tokenType = tokenResponse.TokenType();
107+
108+
// RefreshToken string null/empty when not present
109+
if (String refreshToken = tokenResponse.RefreshToken(); !refreshToken.empty())
110+
{
111+
// ExpiresIn is zero when not present
112+
DateTime expires = winrt::clock::now();
113+
if (String expiresIn = tokenResponse.ExpiresIn(); std::stoi(expiresIn) != 0)
114+
{
115+
expires += std::chrono::seconds(static_cast<int64_t>(std::stoi(expiresIn)));
116+
}
117+
else
118+
{
119+
// Assume a duration of one hour
120+
expires += std::chrono::hours(1);
121+
}
122+
123+
//Schedule a refresh of the access token
124+
myAppState.ScheduleRefreshAt(expires, refreshToken);
125+
}
126+
127+
// Use the access token for resources
128+
DoRequestWithToken(accessToken, tokenType);
129+
}
130+
else
131+
{
132+
TokenFailure tokenFailure = tokenRequestResult.Failure();
133+
NotifyFailure(tokenFailure.Error(), tokenFailure.ErrorDescription());
134+
}
135+
```
136+
137+
# [C#](#tab/csharp)
138+
139+
```csharp
140+
AuthResponse authResponse = authRequestResult.Response;
141+
TokenRequestParams tokenRequestParams = TokenRequestParams.CreateForAuthorizationCodeRequest(authResponse);
142+
ClientAuthentication clientAuth = ClientAuthentication.CreateForBasicAuthorization("my_client_id",
143+
"my_client_secret");
144+
145+
TokenRequestResult tokenRequestResult = await OAuth2Manager.RequestTokenAsync(
146+
new Uri("https://my.server.com/oauth/token"), tokenRequestParams, clientAuth);
147+
148+
if (TokenResponse tokenResponse == tokenRequestResult.Response)
149+
{
150+
string accessToken = tokenResponse.AccessToken;
151+
string tokenType = tokenResponse.TokenType;
152+
153+
// RefreshToken string null/empty when not present
154+
if (!string.IsNullOrEmpty(tokenResponse.RefreshToken))
155+
{
156+
// ExpiresIn is zero when not present
157+
DateTime expires = DateTime.Now;
158+
if (tokenResponse.ExpiresIn != 0)
159+
{
160+
expires += TimeSpan.FromSeconds(tokenResponse.ExpiresIn);
161+
}
162+
else
163+
{
164+
// Assume a duration of one hour
165+
expires += TimeSpan.FromHours(1);
166+
}
167+
168+
//Schedule a refresh of the access token
169+
myAppState.ScheduleRefreshAt(expires, tokenResponse.RefreshToken);
170+
}
171+
172+
// Use the access token for resources
173+
DoRequestWithToken(accessToken, tokenType);
174+
}
175+
else
176+
{
177+
TokenFailure tokenFailure = tokenRequestResult.Failure;
178+
NotifyFailure(tokenFailure.Error, tokenFailure.ErrorDescription);
179+
}
180+
```
181+
182+
---
183+
184+
### Refresh an access token
185+
186+
The following example shows how to refresh an access token using the **OAuth2Manager**'s [RefreshTokenAsync](/windows/windows-app-sdk/api/winrt/microsoft.security.authentication.oauth.oauth2manager.requesttokenasync) method:
187+
188+
# [C++](#tab/cpp)
189+
190+
```cpp
191+
TokenRequestParams tokenRequestParams = TokenRequestParams::CreateForRefreshToken(refreshToken);
192+
ClientAuthentication clientAuth = ClientAuthentication::CreateForBasicAuthorization(L"my_client_id",
193+
L"my_client_secret");
194+
TokenRequestResult tokenRequestResult = co_await OAuth2Manager::RequestTokenAsync(
195+
Uri(L"https://my.server.com/oauth/token"), tokenRequestParams, clientAuth));
196+
if (TokenResponse tokenResponse = tokenRequestResult.Response())
197+
{
198+
UpdateToken(tokenResponse.AccessToken(), tokenResponse.TokenType(), tokenResponse.ExpiresIn());
199+
200+
//Store new refresh token if present
201+
if (String refreshToken = tokenResponse.RefreshToken(); !refreshToken.empty())
202+
{
203+
// ExpiresIn is zero when not present
204+
DateTime expires = winrt::clock::now();
205+
if (String expiresInStr = tokenResponse.ExpiresIn(); !expiresInStr.empty())
206+
{
207+
int expiresIn = std::stoi(expiresInStr);
208+
if (expiresIn != 0)
209+
{
210+
expires += std::chrono::seconds(static_cast<int64_t>(expiresIn));
211+
}
212+
}
213+
else
214+
{
215+
// Assume a duration of one hour
216+
expires += std::chrono::hours(1);
217+
}
218+
219+
//Schedule a refresh of the access token
220+
myAppState.ScheduleRefreshAt(expires, refreshToken);
221+
}
222+
}
223+
else
224+
{
225+
TokenFailure tokenFailure = tokenRequestResult.Failure();
226+
NotifyFailure(tokenFailure.Error(), tokenFailure.ErrorDescription());
227+
}
228+
```
229+
230+
# [C#](#tab/csharp)
231+
232+
```csharp
233+
TokenRequestParams tokenRequestParams = TokenRequestParams.CreateForRefreshToken(refreshToken);
234+
ClientAuthentication clientAuth = ClientAuthentication.CreateForBasicAuthorization("my_client_id",
235+
"my_client_secret");
236+
TokenRequestResult tokenRequestResult = await OAuth2Manager.RequestTokenAsync(
237+
new Uri("https://my.server.com/oauth/token"), tokenRequestParams, clientAuth);
238+
if (TokenResponse tokenResponse == tokenRequestResult.Response)
239+
{
240+
UpdateToken(tokenResponse.AccessToken, tokenResponse.TokenType, tokenResponse.ExpiresIn);
241+
242+
//Store new refresh token if present
243+
if (!string.IsNullOrEmpty(tokenResponse.RefreshToken))
244+
{
245+
// ExpiresIn is zero when not present
246+
DateTime expires = DateTime.Now;
247+
if (tokenResponse.ExpiresIn != 0)
248+
{
249+
expires += TimeSpan.FromSeconds(tokenResponse.ExpiresIn);
250+
}
251+
else
252+
{
253+
// Assume a duration of one hour
254+
expires += TimeSpan.FromHours(1);
255+
}
256+
257+
//Schedule a refresh of the access token
258+
myAppState.ScheduleRefreshAt(expires, tokenResponse.RefreshToken);
259+
}
260+
}
261+
else
262+
{
263+
TokenFailure tokenFailure = tokenRequestResult.Failure;
264+
NotifyFailure(tokenFailure.Error, tokenFailure.ErrorDescription);
265+
}
266+
```
267+
268+
---
269+
270+
### Complete an authorization request
271+
272+
Finally, to complete an authorization request from a protocol activation, your app should handle the [AppInstance.Activated](/windows/windows-app-sdk/api/winrt/microsoft.windows.applifecycle.appinstance.activated) event. This is required when having custom redirect logic. A full example is available on [GitHub](https://github.com/microsoft/WindowsAppSDK-Samples/tree/release/experimental/Samples/OAuth2Manager).
273+
274+
Use the following code:
275+
276+
# [C++](#tab/cpp)
277+
278+
```cpp
279+
void App::OnActivated(const IActivatedEventArgs& args)
280+
{
281+
if (args.Kind() == ActivationKind::Protocol)
282+
{
283+
auto protocolArgs = args.as<ProtocolActivatedEventArgs>();
284+
if (OAuth2Manager::CompleteAuthRequest(protocolArgs.Uri()))
285+
{
286+
TerminateCurrentProcess();
287+
}
288+
289+
DisplayUnhandledMessageToUser();
290+
}
291+
}
292+
```
293+
294+
# [C#](#tab/csharp)
295+
296+
```csharp
297+
protected override void OnActivated(IActivatedEventArgs args)
298+
{
299+
if (args.Kind == ActivationKind.Protocol)
300+
{
301+
ProtocolActivatedEventArgs protocolArgs = args as ProtocolActivatedEventArgs;
302+
if (OAuth2Manager.CompleteAuthRequest(protocolArgs.Uri))
303+
{
304+
TerminateCurrentProcess();
305+
}
306+
307+
DisplayUnhandledMessageToUser();
308+
}
309+
}
310+
```
311+
312+
---
313+
314+
## Related content
315+
316+
- [WebAuthenticationBroker](/uwp/api/windows.security.authentication.web.webauthenticationbroker)
317+
- [OAuth2Manager](/windows/windows-app-sdk/api/winrt/microsoft.security.authentication.oauth.oauth2manager)
318+
- [PKCE RFC 7636](https://tools.ietf.org/html/rfc7636)

hub/apps/toc.yml

+2
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,8 @@ items:
272272
href: develop/security/credential-locker.md
273273
- name: Fingerprint biometrics
274274
href: develop/security/fingerprint-biometrics.md
275+
- name: Implement OAuth 2.0 functionality
276+
href: develop/security/oauth2.md
275277
- name: Share certificates between apps
276278
href: develop/security/share-certificates.md
277279
- name: Smart cards

hub/apps/windows-app-sdk/migrate-to-windows-app-sdk/api-mapping-table.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ There are differences in the names of namespaces and classes (including UI contr
3232
| (**Windows.ApplicationModel.Resources.Core**) [**ResourceQualifierObservableMap.MapChanged**](/uwp/api/windows.applicationmodel.resources.core.resourcequalifierobservablemap.mapchanged) event | Detect environment changes for yourself. See [Resource qualifier value change](guides/mrtcore.md#resource-qualifier-value-change). |
3333
| (**Windows.Graphics.Printing**) [**PrintManager**](/uwp/api/windows.graphics.printing.printmanager) class | Not supported in Windows App SDK 1.0. |
3434
| (**Windows.Media.Capture**) [**CameraCaptureUI**](/uwp/api/windows.media.capture.cameracaptureui) class | Not supported in Windows App SDK 1.0. |
35-
| (**Windows.Security.Authentication.Web**) [**WebAuthenticationBroker**](/uwp/api/windows.security.authentication.web.webauthenticationbroker) class | Not supported in Windows App SDK 1.0. |
35+
| (**Windows.Security.Authentication.Web**) [**WebAuthenticationBroker**](/uwp/api/windows.security.authentication.web.webauthenticationbroker) class | (**Microsoft.Security.Authentication.OAuth**) [OAuth2Manager](/windows/windows-app-sdk/api/winrt/microsoft.security.authentication.oauth.oauth2manager) class (supported in Windows App SDK 1.7 and later). See [Implement OAuth functionality in Windows apps](/windows/apps/develop/security/oauth2) for more information on using **OAuth2Manager** and related APIs for performing OAuth 2.0 authentication. See [GitHub](https://github.com/microsoft/WindowsAppSDK-Samples/tree/release/experimental/Samples/OAuth2Manager) for a full sample application. |
3636
| (**Windows.Storage.Pickers**) [**FileOpenPicker**](/uwp/api/windows.storage.pickers.fileopenpicker), [**FileSavePicker**](/uwp/api/windows.storage.pickers.filesavepicker), and [**FolderPicker**](/uwp/api/windows.storage.pickers.folderpicker) classes | Supported, but you must use the [**IInitializeWithWindow**](/windows/win32/api/shobjidl_core/nn-shobjidl_core-iinitializewithwindow) interface. See [MessageDialog, and Pickers](guides/winui3.md#messagedialog-and-pickers). |
3737
| (**Windows.System.Display**) [**DisplayRequest**](/uwp/api/windows.system.display.displayrequest) class | Not supported in Windows App SDK 1.0. |
3838
| [**Windows.UI.Composition**](/uwp/api/windows.ui.composition) namespace | [**Microsoft.UI.Composition**](/windows/windows-app-sdk/api/winrt/microsoft.ui.composition) namespace |

hub/apps/windows-app-sdk/migrate-to-windows-app-sdk/feature-mapping-table.md

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ This topic compares major feature areas in the different forms in which they app
2525
| Resources | MRT | MRTCore | For more info, see [MRT to MRTCore migration](guides/mrtcore.md). |
2626
| .NET Runtime | .NET Native / C# 7 | .NET 6+/C# 9 | The Windows App SDK provides access to the modern .NET runtime, and access to new language features. However, .NET [ReadyToRun compilation](/dotnet/core/deploying/ready-to-run) is not the same as .NET Native, so you should evaluate performance tradeoffs. |
2727
| 2D Graphics | Win2D | Win2D for WinUI 3 | We're currently working on a version of Win2D that works with the Windows App SDK, in progress. See the [documentation](https://microsoft.github.io/Win2D/WinUI3/html/Introduction.htm) for more information. |
28+
| Web authentication | WebAuthenticationBroker | OAuth2Manager | The Windows App SDK provides a new API for OAuth 2.0 functionality. See [Implement OAuth functionality in Windows apps](../../develop/security/oauth2.md) for more information. |
2829
| Windows Runtime components | Windows Runtime component project templates for UWP |- C++: Use the **Windows Runtime Component (WinUI 3)** project template. <br> - C#: Use C#/WinRT to author Windows Runtime Components in a .NET Class Library. | We're currently working on support to [Author Windows Runtime Components using C#/WinRT](../../develop/platform/csharp-winrt/authoring.md) for use in the Windows App SDK and WinUI 3. |
2930

3031
## See Also

0 commit comments

Comments
 (0)