Skip to content

Commit ffc871e

Browse files
committed
Protect the id for external auth with a shared sercret (using data protection).
1 parent 2d890e8 commit ffc871e

File tree

4 files changed

+21
-5
lines changed

4 files changed

+21
-5
lines changed

Todo.Api/Program.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66

77
builder.AddServiceDefaults();
88

9+
// Configure data protection, setup the application discriminator
10+
// so that the data protection keys can be shared between the BFF and this API
11+
builder.Services.AddDataProtection(o => o.ApplicationDiscriminator = "TodoApp");
12+
913
// Configure auth
1014
builder.Services.AddAuthentication().AddBearerToken(IdentityConstants.BearerScheme);
1115
builder.Services.AddAuthorizationBuilder().AddCurrentUserHandler();

Todo.Api/Users/UsersApi.cs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using Microsoft.AspNetCore.Authentication.BearerToken;
2+
using Microsoft.AspNetCore.DataProtection;
23
using Microsoft.AspNetCore.Http.HttpResults;
34
using Microsoft.AspNetCore.Identity;
45

@@ -20,9 +21,13 @@ public static RouteGroupBuilder MapUsers(this IEndpointRouteBuilder routes)
2021

2122
// The MapIdentityApi<T> doesn't expose an external login endpoint so we write this custom endpoint that follows
2223
// a similar pattern
23-
group.MapPost("/token/{provider}", async Task<Results<Ok<AccessTokenResponse>, SignInHttpResult, ValidationProblem>> (string provider, ExternalUserInfo userInfo, UserManager<TodoUser> userManager, SignInManager<TodoUser> signInManager) =>
24+
group.MapPost("/token/{provider}", async Task<Results<Ok<AccessTokenResponse>, SignInHttpResult, ValidationProblem>> (string provider, ExternalUserInfo userInfo, UserManager<TodoUser> userManager, SignInManager<TodoUser> signInManager, IDataProtectionProvider dataProtectionProvider) =>
2425
{
25-
var user = await userManager.FindByLoginAsync(provider, userInfo.ProviderKey);
26+
var protector = dataProtectionProvider.CreateProtector(provider);
27+
28+
var providerKey = protector.Unprotect(userInfo.ProviderKey);
29+
30+
var user = await userManager.FindByLoginAsync(provider, providerKey);
2631

2732
var result = IdentityResult.Success;
2833

@@ -34,7 +39,7 @@ public static RouteGroupBuilder MapUsers(this IEndpointRouteBuilder routes)
3439

3540
if (result.Succeeded)
3641
{
37-
result = await userManager.AddLoginAsync(user, new UserLoginInfo(provider, userInfo.ProviderKey, displayName: null));
42+
result = await userManager.AddLoginAsync(user, new UserLoginInfo(provider, providerKey, displayName: null));
3843
}
3944
}
4045

Todo.Web/Server/AuthApi.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System.Security.Claims;
22
using Microsoft.AspNetCore.Authentication;
33
using Microsoft.AspNetCore.Authentication.Cookies;
4+
using Microsoft.AspNetCore.DataProtection;
45

56
namespace Todo.Web.Server;
67

@@ -60,7 +61,7 @@ public static RouteGroupBuilder MapAuth(this IEndpointRouteBuilder routes)
6061
authenticationSchemes: [provider]);
6162
});
6263

63-
group.MapGet("signin/{provider}", async (string provider, AuthClient client, HttpContext context) =>
64+
group.MapGet("signin/{provider}", async (string provider, AuthClient client, HttpContext context, IDataProtectionProvider dataProtectionProvider) =>
6465
{
6566
// Grab the login information from the external login dance
6667
var result = await context.AuthenticateAsync(AuthenticationSchemes.ExternalScheme);
@@ -75,7 +76,10 @@ public static RouteGroupBuilder MapAuth(this IEndpointRouteBuilder routes)
7576
// for now we'll prefer the email address
7677
var name = (principal.FindFirstValue(ClaimTypes.Email) ?? principal.Identity?.Name)!;
7778

78-
var token = await client.GetOrCreateUserAsync(provider, new() { Username = name, ProviderKey = id });
79+
// Protect the user id so it for transport
80+
var protector = dataProtectionProvider.CreateProtector(provider);
81+
82+
var token = await client.GetOrCreateUserAsync(provider, new() { Username = name, ProviderKey = protector.Protect(id) });
7983

8084
if (token is not null)
8185
{

Todo.Web/Server/Program.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
builder.AddAuthentication();
1010
builder.Services.AddAuthorizationBuilder();
1111

12+
// Configure data protection, setup the application discriminator so that the data protection keys can be shared between the BFF and this API
13+
builder.Services.AddDataProtection(o => o.ApplicationDiscriminator = "TodoApp");
14+
1215
// Must add client services
1316
builder.Services.AddScoped<TodoClient>();
1417

0 commit comments

Comments
 (0)