-
Notifications
You must be signed in to change notification settings - Fork 440
/
Copy pathAuthApi.cs
129 lines (101 loc) · 4.67 KB
/
AuthApi.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
using System.Security.Claims;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.DataProtection;
namespace Todo.Web.Server;
public static class AuthApi
{
public static RouteGroupBuilder MapAuth(this IEndpointRouteBuilder routes)
{
var group = routes.MapGroup("/auth");
group.MapPost("register", async (UserInfo userInfo, AuthClient client) =>
{
// Retrieve the access token given the user info
var token = await client.CreateUserAsync(userInfo);
if (token is null)
{
return Results.Unauthorized();
}
return SignIn(userInfo, token);
});
group.MapPost("login", async (UserInfo userInfo, AuthClient client) =>
{
// Retrieve the access token give the user info
var token = await client.GetTokenAsync(userInfo);
if (token is null)
{
return Results.Unauthorized();
}
return SignIn(userInfo, token);
});
group.MapPost("logout", async (HttpContext context) =>
{
await context.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
// TODO: Support remote logout
// If this is an external login then use it
//var result = await context.AuthenticateAsync();
//if (result.Properties?.GetExternalProvider() is string providerName)
//{
// await context.SignOutAsync(providerName, new() { RedirectUri = "/" });
//}
})
.RequireAuthorization();
// External login
group.MapGet("login/{provider}", (string provider) =>
{
// Trigger the external login flow by issuing a challenge with the provider name.
// This name maps to the registered authentication scheme names in AuthenticationExtensions.cs
return Results.Challenge(
properties: new() { RedirectUri = $"/auth/signin/{provider}" },
authenticationSchemes: [provider]);
});
group.MapGet("signin/{provider}", async (string provider, AuthClient client, HttpContext context, IDataProtectionProvider dataProtectionProvider) =>
{
// Grab the login information from the external login dance
var result = await context.AuthenticateAsync(AuthenticationSchemes.ExternalScheme);
if (result.Succeeded)
{
var principal = result.Principal;
var id = principal.FindFirstValue(ClaimTypes.NameIdentifier)!;
// TODO: We should have the user pick a user name to complete the external login dance
// for now we'll prefer the email address
var name = (principal.FindFirstValue(ClaimTypes.Email) ?? principal.Identity?.Name)!;
// Protect the user id so it for transport
var protector = dataProtectionProvider.CreateProtector(provider);
var token = await client.GetOrCreateUserAsync(provider, new() { Username = name, ProviderKey = protector.Protect(id) });
if (token is not null)
{
// Write the login cookie
await SignIn(id, name, token, provider).ExecuteAsync(context);
}
}
// Delete the external cookie
await context.SignOutAsync(AuthenticationSchemes.ExternalScheme);
// TODO: Handle the failure somehow
return Results.Redirect("/");
});
return group;
}
private static IResult SignIn(UserInfo userInfo, string token)
{
return SignIn(userInfo.Email, userInfo.Email, token, providerName: null);
}
private static IResult SignIn(string userId, string userName, string token, string? providerName)
{
var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme);
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, userId));
identity.AddClaim(new Claim(ClaimTypes.Name, userName));
var properties = new AuthenticationProperties();
// Store the external provider name so we can do remote sign out
if (providerName is not null)
{
properties.SetExternalProvider(providerName);
}
properties.StoreTokens([
new AuthenticationToken { Name = TokenNames.AccessToken, Value = token }
]);
return Results.SignIn(new ClaimsPrincipal(identity),
properties: properties,
authenticationScheme: CookieAuthenticationDefaults.AuthenticationScheme);
}
}