Skip to content

Commit a69a4cb

Browse files
authored
Merge pull request #13 from AzureADQuickStarts/jmprieur/updateToMsal3x
Jmprieur/update to msal3x
2 parents e4e846b + 4601f8c commit a69a4cb

10 files changed

Lines changed: 104 additions & 96 deletions

File tree

README.md

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,11 @@ If you want to register your apps manually, as a first step you'll need to:
7272

7373
1. Open the **app.config** file located in **TodoListClient** project's root folder and then paste **Application Id** from the application you just registered for your *TodoListService* under `TodoListServiceScope` parameter, replacing the string `{Enter the Application Id of your TodoListService from the app registration portal}`.
7474

75-
> Note: Make sure it uses has the format `api://{TodoListService-Application-Id}/access_as_user` (where {TodoListService-Application-Id} is the Guid representing the Application Id for your TodoListService).
75+
> Note: Make sure it uses the following format:
76+
>
77+
> `api://{TodoListService-Application-Id}/access_as_user`
78+
>
79+
>(where {TodoListService-Application-Id} is the Guid representing the Application Id for your TodoListService).
7680
7781
### Step 3: Register the client app (TodoListClient)
7882

@@ -84,34 +88,33 @@ In this step, you configure your *TodoListClient* project by registering a new a
8488
1. Select **New registration**.
8589
1. When the **Register an application page** appears, enter your application's registration information:
8690
- In the **Name** section, enter a meaningful application name that will be displayed to users of the app, for example `NativeClient-DotNet-TodoListClient`.
87-
- Change **Supported account types** to **Accounts in any organizational directory and personal Microsoft accounts (e.g. Skype, Xbox, Outlook.com)**.
91+
- Change **Supported account types** to **Accounts in any organizational directory**.
8892
- Select **Register** to create the application.
89-
1. On the app **Overview** page, find the **Application (client) ID** value and record it for later. You'll need it to configure the Visual Studio configuration file for this project (`ida:ClientId` in `TodoListClient\App.Config`).
9093
1. From the app's Overview page, select the **Authentication** section.
9194
- In the **Redirect URLs** | **Suggested Redirect URLs for public clients (mobile, desktop)** section, check **urn:ietf:wg:oauth:2.0:oob**
9295
- Select **Save**.
9396
1. Select the **API permissions** section
9497
- Click the **Add a permission** button and then,
95-
- Ensure that the **My APIs** tab is selected
98+
- Select the **My APIs** tab.
9699
- In the list of APIs, select the `AppModelv2-NativeClient-DotNet-TodoListService API`, or the name you entered for the Web API.
97-
- In the **Delegated permissions** section, ensure that the right permissions are checked: **access_as_user**. Use the search box if necessary.
100+
- Check the **access_as_user** permission if it's not already checked. Use the search box if necessary.
98101
- Select the **Add permissions** button
99102

100103
#### Configure your *TodoListClient* project
101104

102-
1. In the *Application registration portal*, copy the value of the **Application Id**
105+
1. In the *Application registration portal*, in the **Overview** page copy the value of the **Application (client) Id**
103106
1. Open the **app.config** file located in the **TodoListClient** project's root folder and then paste the value in the `ida:ClientId` parameter value
104107

105108
### Step 4: Run your project
106109

107110
1. Press `<F5>` to run your project. Your *TodoListClient* should open.
108-
1. Select **Sign in** in the top right and sign in with the same user you have used to register your aplication, or a user in the same directory.
111+
1. Select **Sign in** in the top right and sign in with the same user you have used to register your application, or a user in the same directory.
109112
1. At this point, if you are signing in for the first time, you may be prompted to consent to *TodoListService* Web Api.
110113
1. The sign-in also request the access token to the *access_as_user* scope to access *TodoListService* Web Api and manipulate the *To-Do* list.
111114

112115
### Step 5: Pre-authorize your client application
113116

114-
One of the ways to allow users from other directories to acces your Web API is by *pre-authorizing* the client applications to access your Web API by adding the Application Ids from client applications in the list of *pre-authorized* applications for your Web API. By adding a pre-authorized client, you will not require user to consent to use your Web API. Follow the steps below to pre-authorize your Web Application::
117+
One of the ways to allow users from other directories to access your Web API is by *pre-authorizing* the client applications to access your Web API by adding the Application Ids from client applications in the list of *pre-authorized* applications for your Web API. By adding a pre-authorized client, you will not require user to consent to use your Web API. Follow the steps below to pre-authorize your Web Application::
115118

116119
1. Go back to the *Application registration portal* and open the properties of your **TodoListService**.
117120
1. In the **Expose an API** section, click on **Add application** under the *Pre-authorized applications* section.

TodoListClient/App.config

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
</startup>
66
<appSettings>
77
<!-- ida:Client is a GUID representing the Application Id for the TodoListClient app that you copied from
8-
the App Registration Portal (https://apps.dev.microsoft.com) -->
8+
the App Registration Portal ((https://go.microsoft.com/fwlink/?linkid=2083908) -->
99

1010
<add key="ida:ClientId" value="{Enter the Application Id that you copied from the App Registration Portal.}" />
1111

@@ -19,7 +19,7 @@
1919
"https://login.microsoftonline.com/organizations/" instead of "https://login.microsoftonline.com/common/"
2020
-->
2121

22-
<add key="TodoListServiceScope" value="{Enter the Application Id of your TodoListService from the app registration portal}" />
22+
<add key="TodoListServiceScope" value="api://{Enter the Application Id of your TodoListService from the app registration portal}/access_as_user" />
2323
<add key="TodoListServiceBaseAddress" value="https://localhost:44321/" />
2424

2525
</appSettings>

TodoListClient/MainWindow.xaml.cs

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ public partial class MainWindow : Window
4848

4949

5050
private HttpClient httpClient = new HttpClient();
51-
private PublicClientApplication app = null;
51+
private IPublicClientApplication app = null;
5252

5353
private string[] Scopes = null;
5454

@@ -59,8 +59,11 @@ protected override async void OnInitialized(EventArgs e)
5959
Scopes = new string[] {todoListServiceScope};
6060

6161
// Initialize the PublicClientApplication
62-
app = new PublicClientApplication(clientId, "https://login.microsoftonline.com/common/v2.0", TokenCacheHelper.GetUserCache());
63-
AuthenticationResult result = null;
62+
app = PublicClientApplicationBuilder.Create(clientId)
63+
.Build();
64+
65+
TokenCacheHelper.EnableSerialization(app.UserTokenCache);
66+
6467

6568
// TODO: Check if the user is already signed in.
6669
// As the app starts, we want to check to see if the user is already signed in.
@@ -70,7 +73,8 @@ protected override async void OnInitialized(EventArgs e)
7073
try
7174
{
7275
var accounts = await app.GetAccountsAsync();
73-
result = await app.AcquireTokenSilentAsync(Scopes, accounts.FirstOrDefault());
76+
var result = await app.AcquireTokenSilent(Scopes, accounts.FirstOrDefault())
77+
.ExecuteAsync();
7478
// If we got here, a valid token is in the cache - or MSAL was able to get a new oen via refresh token.
7579
// Proceed to fetch the user's tasks from the TodoListService via the GetTodoList() method.
7680

@@ -121,7 +125,9 @@ private async Task GetTodoList()
121125
// without invoking any UI prompt. AcquireTokenSilentAsync forces
122126
// MSAL to throw an exception if it cannot get a token silently.
123127
var accounts = await app.GetAccountsAsync();
124-
result = await app.AcquireTokenSilentAsync(Scopes, accounts.FirstOrDefault());
128+
result = await app.AcquireTokenSilent(Scopes, accounts.FirstOrDefault())
129+
.ExecuteAsync()
130+
.ConfigureAwait(false);
125131
}
126132
catch (MsalException ex)
127133
{
@@ -186,7 +192,9 @@ private async void AddTodoItem(object sender, RoutedEventArgs e)
186192
try
187193
{
188194
var accounts = await app.GetAccountsAsync();
189-
result = await app.AcquireTokenSilentAsync(Scopes, accounts.FirstOrDefault());
195+
result = await app.AcquireTokenSilent(Scopes, accounts.FirstOrDefault())
196+
.ExecuteAsync()
197+
.ConfigureAwait(false);
190198
}
191199
catch (MsalException ex)
192200
{
@@ -231,11 +239,11 @@ private async void AddTodoItem(object sender, RoutedEventArgs e)
231239
/// <param name="app"></param>
232240
private async Task ClearCache(IPublicClientApplication app)
233241
{
234-
var accounts = await app.GetAccountsAsync();
242+
var accounts = (await app.GetAccountsAsync()).ToList();
235243
while (accounts.Any())
236244
{
237245
await app.RemoveAsync(accounts.First());
238-
accounts = await app.GetAccountsAsync();
246+
accounts = (await app.GetAccountsAsync()).ToList();
239247
}
240248
}
241249
private async void SignIn(object sender = null, RoutedEventArgs args = null)
@@ -261,10 +269,18 @@ private async void SignIn(object sender = null, RoutedEventArgs args = null)
261269
// MSAL will get a token for the TodoListService and cache it for you.
262270

263271
AuthenticationResult result = null;
272+
var accounts = await app.GetAccountsAsync();
264273
try
265274
{
266-
result = await app.AcquireTokenAsync(Scopes);
267-
SignInButton.Content = "Clear Cache";
275+
result = await app.AcquireTokenInteractive(Scopes)
276+
.WithAccount(accounts.FirstOrDefault())
277+
.WithPrompt(Prompt.SelectAccount)
278+
.ExecuteAsync()
279+
.ConfigureAwait(false);
280+
Dispatcher.Invoke(() =>
281+
{
282+
SignInButton.Content = "Clear Cache";
283+
});
268284
GetTodoList();
269285
}
270286
catch (MsalException ex)

TodoListClient/TodoListClient.csproj

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@
3434
<WarningLevel>4</WarningLevel>
3535
</PropertyGroup>
3636
<ItemGroup>
37-
<Reference Include="Microsoft.Identity.Client, Version=2.6.2.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae">
38-
<HintPath>..\packages\Microsoft.Identity.Client.2.6.2\lib\net45\Microsoft.Identity.Client.dll</HintPath>
37+
<Reference Include="Microsoft.Identity.Client, Version=3.0.5.0, Culture=neutral, PublicKeyToken=0a613f4dd989e8ae, processorArchitecture=MSIL">
38+
<HintPath>..\packages\Microsoft.Identity.Client.3.0.5-preview\lib\net45\Microsoft.Identity.Client.dll</HintPath>
3939
</Reference>
4040
<Reference Include="System" />
4141
<Reference Include="System.Configuration" />
@@ -63,8 +63,8 @@
6363
<Generator>MSBuild:Compile</Generator>
6464
<SubType>Designer</SubType>
6565
</ApplicationDefinition>
66-
<Compile Include="FileCache.cs" />
6766
<Compile Include="TodoItem.cs" />
67+
<Compile Include="TokenCacheHelper.cs" />
6868
<Page Include="MainWindow.xaml">
6969
<Generator>MSBuild:Compile</Generator>
7070
<SubType>Designer</SubType>
@@ -96,9 +96,7 @@
9696
<Generator>ResXFileCodeGenerator</Generator>
9797
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
9898
</EmbeddedResource>
99-
<None Include="packages.config">
100-
<SubType>Designer</SubType>
101-
</None>
99+
<None Include="packages.config" />
102100
<None Include="Properties\Settings.settings">
103101
<Generator>SettingsSingleFileGenerator</Generator>
104102
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
Lines changed: 12 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -34,59 +34,45 @@ namespace TodoListClient
3434
static class TokenCacheHelper
3535
{
3636

37-
/// <summary>
38-
/// Get the user token cache
39-
/// </summary>
40-
/// <returns></returns>
41-
public static TokenCache GetUserCache()
42-
{
43-
if (usertokenCache == null)
44-
{
45-
usertokenCache = new TokenCache();
46-
usertokenCache.SetBeforeAccess(BeforeAccessNotification);
47-
usertokenCache.SetAfterAccess(AfterAccessNotification);
48-
}
49-
return usertokenCache;
50-
}
51-
52-
static TokenCache usertokenCache;
53-
5437
/// <summary>
5538
/// Path to the token cache
5639
/// </summary>
57-
public static readonly string CacheFilePath = System.Reflection.Assembly.GetExecutingAssembly().Location + ".msalcache.bin";
40+
private static readonly string CacheFilePath = System.Reflection.Assembly.GetExecutingAssembly().Location + ".msalcache.bin";
5841

5942
private static readonly object FileLock = new object();
6043

61-
public static void BeforeAccessNotification(TokenCacheNotificationArgs args)
44+
private static void BeforeAccessNotification(TokenCacheNotificationArgs args)
6245
{
6346
lock (FileLock)
6447
{
65-
args.TokenCache.Deserialize(File.Exists(CacheFilePath)
48+
args.TokenCache.DeserializeMsalV3(File.Exists(CacheFilePath)
6649
? ProtectedData.Unprotect(File.ReadAllBytes(CacheFilePath),
6750
null,
6851
DataProtectionScope.CurrentUser)
6952
: null);
7053
}
7154
}
7255

73-
public static void AfterAccessNotification(TokenCacheNotificationArgs args)
56+
private static void AfterAccessNotification(TokenCacheNotificationArgs args)
7457
{
7558
// if the access operation resulted in a cache update
76-
if (args.TokenCache.HasStateChanged)
59+
if (args.HasStateChanged)
7760
{
7861
lock (FileLock)
7962
{
80-
// reflect changesgs in the persistent store
63+
// reflect changes in the persistent store
8164
File.WriteAllBytes(CacheFilePath,
82-
ProtectedData.Protect(args.TokenCache.Serialize(),
65+
ProtectedData.Protect(args.TokenCache.SerializeMsalV3(),
8366
null,
8467
DataProtectionScope.CurrentUser)
8568
);
86-
// once the write operationtakes place restore the HasStateChanged bit to filse
87-
args.TokenCache.HasStateChanged = false;
8869
}
8970
}
9071
}
72+
internal static void EnableSerialization(ITokenCache tokenCache)
73+
{
74+
tokenCache.SetBeforeAccess(BeforeAccessNotification);
75+
tokenCache.SetAfterAccess(AfterAccessNotification);
76+
}
9177
}
9278
}

TodoListClient/packages.config

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<packages>
3-
<package id="Microsoft.Identity.Client" version="2.6.2" allowedVersions="[2,3)" targetFramework="net45" />
3+
<package id="Microsoft.Identity.Client" version="3.0.5-preview" allowedVersions="[3,4)" targetFramework="net45" />
44
</packages>

TodoListService/App_Start/Startup.Auth.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,11 @@ public void ConfigureAuth(IAppBuilder app)
2727
new TokenValidationParameters
2828
{
2929
// Check if the audience is intended to be this application
30-
ValidAudience = clientId,
30+
ValidAudiences = new [] { clientId, $"api://{clientId}" },
3131

3232
// Change below to 'true' if you want this Web API to accept tokens issued to one Azure AD tenant only (single-tenant)
33+
// Note that this is a simplification for the quickstart here. You should validate the issuer. For details,
34+
// see https://github.com/Azure-Samples/active-directory-dotnet-native-aspnetcore
3335
ValidateIssuer = false,
3436

3537
},

0 commit comments

Comments
 (0)