Skip to content

Commit f40c079

Browse files
authored
Merge branch 'jennyf/b2cwebapi' into jmprieur/webappwebapib2c
2 parents b23fc76 + ab84785 commit f40c079

16 files changed

+259
-134
lines changed

4-WebApp-your-API/Client/Program.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
using Microsoft.AspNetCore.Hosting;
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
using Microsoft.AspNetCore.Hosting;
25
using Microsoft.Extensions.Hosting;
36

47
namespace WebApp_OpenIDConnect_DotNet

4-WebApp-your-API/Client/Services/TodoListService.cs

Lines changed: 16 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,5 @@
1-
/*
2-
The MIT License (MIT)
3-
4-
Copyright (c) 2018 Microsoft Corporation
5-
6-
Permission is hereby granted, free of charge, to any person obtaining a copy
7-
of this software and associated documentation files (the "Software"), to deal
8-
in the Software without restriction, including without limitation the rights
9-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10-
copies of the Software, and to permit persons to whom the Software is
11-
furnished to do so, subject to the following conditions:
12-
13-
The above copyright notice and this permission notice shall be included in all
14-
copies or substantial portions of the Software.
15-
16-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22-
SOFTWARE.
23-
*/
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
243

254
using Microsoft.AspNetCore.Http;
265
using Microsoft.Extensions.Configuration;
@@ -59,11 +38,11 @@ public class TodoListService : ITodoListService
5938

6039
public TodoListService(ITokenAcquisition tokenAcquisition, HttpClient httpClient, IConfiguration configuration, IHttpContextAccessor contextAccessor)
6140
{
62-
this._httpClient = httpClient;
63-
this._tokenAcquisition = tokenAcquisition;
64-
this._contextAccessor = contextAccessor;
65-
this._TodoListScope = configuration["TodoList:TodoListScope"];
66-
this._TodoListBaseAddress = configuration["TodoList:TodoListBaseAddress"];
41+
_httpClient = httpClient;
42+
_tokenAcquisition = tokenAcquisition;
43+
_contextAccessor = contextAccessor;
44+
_TodoListScope = configuration["TodoList:TodoListScope"];
45+
_TodoListBaseAddress = configuration["TodoList:TodoListBaseAddress"];
6746
}
6847

6948
public async Task<Todo> AddAsync(Todo todo)
@@ -73,7 +52,7 @@ public async Task<Todo> AddAsync(Todo todo)
7352
var jsonRequest = JsonConvert.SerializeObject(todo);
7453
var jsoncontent = new StringContent(jsonRequest, Encoding.UTF8, "application/json");
7554

76-
var response = await this._httpClient.PostAsync($"{this._TodoListBaseAddress}/api/todolist", jsoncontent);
55+
var response = await this._httpClient.PostAsync($"{ _TodoListBaseAddress}/api/todolist", jsoncontent);
7756

7857
if (response.StatusCode == HttpStatusCode.OK)
7958
{
@@ -90,7 +69,7 @@ public async Task DeleteAsync(int id)
9069
{
9170
await PrepareAuthenticatedClient();
9271

93-
var response = await this._httpClient.DeleteAsync($"{this._TodoListBaseAddress}/api/todolist/{id}");
72+
var response = await this._httpClient.DeleteAsync($"{ _TodoListBaseAddress}/api/todolist/{id}");
9473

9574
if (response.StatusCode == HttpStatusCode.OK)
9675
{
@@ -107,7 +86,7 @@ public async Task<Todo> EditAsync(Todo todo)
10786
var jsonRequest = JsonConvert.SerializeObject(todo);
10887
var jsoncontent = new StringContent(jsonRequest, Encoding.UTF8, "application/json-patch+json");
10988

110-
var response = await this._httpClient.PatchAsync($"{this._TodoListBaseAddress}/api/todolist/{todo.Id}", jsoncontent);
89+
var response = await _httpClient.PatchAsync($"{ _TodoListBaseAddress}/api/todolist/{todo.Id}", jsoncontent);
11190

11291
if (response.StatusCode == HttpStatusCode.OK)
11392
{
@@ -124,7 +103,7 @@ public async Task<IEnumerable<Todo>> GetAsync()
124103
{
125104
await PrepareAuthenticatedClient();
126105

127-
var response = await this._httpClient.GetAsync($"{this._TodoListBaseAddress}/api/todolist");
106+
var response = await _httpClient.GetAsync($"{ _TodoListBaseAddress}/api/todolist");
128107
if (response.StatusCode == HttpStatusCode.OK)
129108
{
130109
var content = await response.Content.ReadAsStringAsync();
@@ -137,18 +116,18 @@ public async Task<IEnumerable<Todo>> GetAsync()
137116
}
138117

139118
private async Task PrepareAuthenticatedClient()
140-
{
141-
var accessToken = await this._tokenAcquisition.GetAccessTokenForUserAsync(new[] { this._TodoListScope });
119+
{
120+
var accessToken = await _tokenAcquisition.GetAccessTokenForUserAsync(new[] { _TodoListScope });
142121
Debug.WriteLine($"access token-{accessToken}");
143-
this._httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
144-
this._httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
122+
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
123+
_httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
145124
}
146125

147126
public async Task<Todo> GetAsync(int id)
148127
{
149128
await PrepareAuthenticatedClient();
150129

151-
var response = await this._httpClient.GetAsync($"{this._TodoListBaseAddress}/api/todolist/{id}");
130+
var response = await _httpClient.GetAsync($"{ _TodoListBaseAddress}/api/todolist/{id}");
152131
if (response.StatusCode == HttpStatusCode.OK)
153132
{
154133
var content = await response.Content.ReadAsStringAsync();

4-WebApp-your-API/Client/Startup.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
using Microsoft.AspNetCore.Authorization;
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
using Microsoft.AspNetCore.Authorization;
25
using Microsoft.AspNetCore.Builder;
36
using Microsoft.AspNetCore.Hosting;
47
using Microsoft.AspNetCore.Http;
@@ -7,7 +10,6 @@
710
using Microsoft.Extensions.DependencyInjection;
811
using Microsoft.Identity.Web;
912
using Microsoft.Identity.Web.TokenCacheProviders.InMemory;
10-
using System.IdentityModel.Tokens.Jwt;
1113
using TodoListClient.Services;
1214
using Microsoft.Extensions.Hosting;
1315
using Microsoft.AspNetCore.Authentication.OpenIdConnect;

4-WebApp-your-API/Client/TodoListClient.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
<PropertyGroup>
44
<TargetFramework>netcoreapp3.1</TargetFramework>
5-
<UserSecretsId>aspnet-WebApp_OpenIDConnect_DotNet-81EA87AD-E64D-4755-A1CC-5EA47F49B5D8</UserSecretsId>
5+
<UserSecretsId>aspnet-WebApp_OpenIDConnect_DotNet-81EA87AD-E64D-4755-A1CC-5EA47F49B5D9</UserSecretsId>
66
<WebProject_DirectoryAccessLevelKey>0</WebProject_DirectoryAccessLevelKey>
77
</PropertyGroup>
88

4-WebApp-your-API/TodoListService/Program.cs

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,5 @@
1-
/*
2-
The MIT License (MIT)
3-
4-
Copyright (c) 2018 Microsoft Corporation
5-
6-
Permission is hereby granted, free of charge, to any person obtaining a copy
7-
of this software and associated documentation files (the "Software"), to deal
8-
in the Software without restriction, including without limitation the rights
9-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10-
copies of the Software, and to permit persons to whom the Software is
11-
furnished to do so, subject to the following conditions:
12-
13-
The above copyright notice and this permission notice shall be included in all
14-
copies or substantial portions of the Software.
15-
16-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22-
SOFTWARE.
23-
*/
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
243

254
using Microsoft.AspNetCore.Hosting;
265
using Microsoft.Extensions.Hosting;

4-WebApp-your-API/TodoListService/Startup.cs

Lines changed: 2 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,12 @@
1-
/*
2-
The MIT License (MIT)
3-
4-
Copyright (c) 2018 Microsoft Corporation
5-
6-
Permission is hereby granted, free of charge, to any person obtaining a copy
7-
of this software and associated documentation files (the "Software"), to deal
8-
in the Software without restriction, including without limitation the rights
9-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10-
copies of the Software, and to permit persons to whom the Software is
11-
furnished to do so, subject to the following conditions:
12-
13-
The above copyright notice and this permission notice shall be included in all
14-
copies or substantial portions of the Software.
15-
16-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22-
SOFTWARE.
23-
*/
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
243

254
using Microsoft.AspNetCore.Builder;
265
using Microsoft.AspNetCore.Hosting;
276
using Microsoft.Extensions.Hosting;
287
using Microsoft.Extensions.Configuration;
298
using Microsoft.Extensions.DependencyInjection;
309
using Microsoft.Identity.Web;
31-
using System.IdentityModel.Tokens.Jwt;
3210
using Microsoft.AspNetCore.Authentication.JwtBearer;
3311

3412
namespace TodoListService

Microsoft.Identity.Web/AuthorizeForScopesAttribute.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ public class AuthorizeForScopesAttribute : ExceptionFilterAttribute
4545
/// <param name="context">Context provided by ASP.NET Core</param>
4646
public override void OnException(ExceptionContext context)
4747
{
48+
string[] incrementalConsentScopes = new string[] { };
4849
MsalUiRequiredException msalUiRequiredException = context.Exception as MsalUiRequiredException;
4950

5051
if (msalUiRequiredException == null)
@@ -77,8 +78,12 @@ public override void OnException(ExceptionContext context)
7778
}
7879

7980
scopes = new string[] { configuration.GetValue<string>(ScopeKeySection) };
81+
82+
if (Scopes != null && Scopes.Length > 0 && scopes != null && scopes.Length > 0)
83+
{
84+
throw new InvalidOperationException("no scopes provided in scopes...");
85+
}
8086
}
81-
8287
else
8388
scopes = Scopes;
8489

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
using System;
5+
using System.Globalization;
6+
using System.Text;
7+
8+
namespace Microsoft.Identity.Web
9+
{
10+
internal static class Base64UrlHelpers
11+
{
12+
private const char Base64PadCharacter = '=';
13+
private const char Base64Character62 = '+';
14+
private const char Base64Character63 = '/';
15+
private const char Base64UrlCharacter62 = '-';
16+
private const char Base64UrlCharacter63 = '_';
17+
private static readonly Encoding TextEncoding = Encoding.UTF8;
18+
19+
private static readonly string DoubleBase64PadCharacter = string.Format(CultureInfo.InvariantCulture, "{0}{0}",
20+
Base64PadCharacter);
21+
22+
//
23+
// The following functions perform base64url encoding which differs from regular base64 encoding as follows
24+
// * padding is skipped so the pad character '=' doesn't have to be percent encoded
25+
// * the 62nd and 63rd regular base64 encoding characters ('+' and '/') are replace with ('-' and '_')
26+
// The changes make the encoding alphabet file and URL safe
27+
// See RFC4648, section 5 for more info
28+
//
29+
public static string Encode(string arg)
30+
{
31+
if (arg == null)
32+
{
33+
return null;
34+
}
35+
36+
return Encode(TextEncoding.GetBytes(arg));
37+
}
38+
39+
public static string DecodeToString(string arg)
40+
{
41+
byte[] decoded = DecodeToBytes(arg);
42+
return CreateString(decoded);
43+
}
44+
45+
public static string CreateString(byte[] bytes)
46+
{
47+
return Encoding.UTF8.GetString(bytes, 0, bytes.Length);
48+
}
49+
50+
public static byte[] DecodeToBytes(string arg)
51+
{
52+
string s = arg;
53+
s = s.Replace(Base64UrlCharacter62, Base64Character62); // 62nd char of encoding
54+
s = s.Replace(Base64UrlCharacter63, Base64Character63); // 63rd char of encoding
55+
56+
switch (s.Length % 4)
57+
{
58+
// Pad
59+
case 0:
60+
break; // No pad chars in this case
61+
case 2:
62+
s += DoubleBase64PadCharacter;
63+
break; // Two pad chars
64+
case 3:
65+
s += Base64PadCharacter;
66+
break; // One pad char
67+
default:
68+
throw new ArgumentException("Illegal base64url string!", nameof(arg));
69+
}
70+
71+
return Convert.FromBase64String(s); // Standard base64 decoder
72+
}
73+
74+
internal static string Encode(byte[] arg)
75+
{
76+
if (arg == null)
77+
{
78+
return null;
79+
}
80+
81+
string s = Convert.ToBase64String(arg);
82+
s = s.Split(Base64PadCharacter)[0]; // RemoveAccount any trailing padding
83+
s = s.Replace(Base64Character62, Base64UrlCharacter62); // 62nd char of encoding
84+
s = s.Replace(Base64Character63, Base64UrlCharacter63); // 63rd char of encoding
85+
86+
return s;
87+
}
88+
}
89+
}

Microsoft.Identity.Web/ClaimConstants.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ public static class ClaimConstants
1414
public const string PreferredUserName = "preferred_username";
1515
public const string TenantId = "http://schemas.microsoft.com/identity/claims/tenantid";
1616
public const string Tid = "tid";
17+
public const string ClientInfo = "client_info";
1718
// Older scope claim
1819
public const string Scope = "http://schemas.microsoft.com/identity/claims/scope";
1920
// Newer scope claim
@@ -23,6 +24,7 @@ public static class ClaimConstants
2324

2425
// Policy claims
2526
public const string Acr = "acr";
27+
public const string UserFlow = "http://schemas.microsoft.com/claims/authnclassreference";
2628
public const string Tfp = "tfp";
2729
}
2830
}

Microsoft.Identity.Web/ClaimsPrincipalExtensions.cs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,11 @@ public static string GetMsalAccountId(this ClaimsPrincipal claimsPrincipal)
2323
string tenantId = claimsPrincipal.GetTenantId();
2424
string userFlowId = claimsPrincipal.GetUserFlowId();
2525

26-
if (!string.IsNullOrWhiteSpace(nameIdentifierId) && !string.IsNullOrWhiteSpace(tenantId) && !string.IsNullOrWhiteSpace(userFlowId))
26+
if (!string.IsNullOrWhiteSpace(nameIdentifierId) &&
27+
!string.IsNullOrWhiteSpace(tenantId) &&
28+
!string.IsNullOrWhiteSpace(userFlowId))
2729
{
28-
// B2C pattern: {oid}-{tfp}.{tid}
30+
// B2C pattern: {oid}-{userFlow}.{tid}
2931
return $"{nameIdentifierId}-{userFlowId}.{tenantId}";
3032
}
3133
else if (!string.IsNullOrWhiteSpace(userObjectId) && !string.IsNullOrWhiteSpace(tenantId))
@@ -64,8 +66,9 @@ public static string GetTenantId(this ClaimsPrincipal claimsPrincipal)
6466
string tenantId = claimsPrincipal.FindFirstValue(ClaimConstants.Tid);
6567
if (string.IsNullOrEmpty(tenantId))
6668
{
67-
tenantId = claimsPrincipal.FindFirstValue(ClaimConstants.TenantId);
69+
return claimsPrincipal.FindFirstValue(ClaimConstants.TenantId);
6870
}
71+
6972
return tenantId;
7073
}
7174

@@ -125,7 +128,7 @@ public static string GetDisplayName(this ClaimsPrincipal claimsPrincipal)
125128
// Finally falling back to name
126129
return claimsPrincipal.FindFirstValue(ClaimConstants.Name);
127130
}
128-
131+
129132
/// <summary>
130133
/// Gets the user flow id associated with the <see cref="ClaimsPrincipal"/>
131134
/// </summary>
@@ -134,6 +137,11 @@ public static string GetDisplayName(this ClaimsPrincipal claimsPrincipal)
134137
public static string GetUserFlowId(this ClaimsPrincipal claimsPrincipal)
135138
{
136139
string userFlowId = claimsPrincipal.FindFirstValue(ClaimConstants.Tfp);
140+
if (string.IsNullOrEmpty(userFlowId))
141+
{
142+
return claimsPrincipal.FindFirstValue(ClaimConstants.UserFlow);
143+
}
144+
137145
return userFlowId;
138146
}
139147

0 commit comments

Comments
 (0)