Skip to content

Commit 27f9343

Browse files
First working process from first run to successful login
1 parent d103222 commit 27f9343

10 files changed

+294
-41
lines changed

Modules/Authentication.Web/Concern/WebAuthenticationBuilder.cs

+11-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ public sealed class WebAuthenticationBuilder : IConcernBuilder
1111

1212
private SessionConfig? _SessionConfig;
1313

14+
private LoginConfig? _LoginConfig;
15+
1416
private SetupConfig? _SetupConfig;
1517

1618
#region Functionality
@@ -33,11 +35,19 @@ public WebAuthenticationBuilder EnableSetup(SetupConfig setupConfig)
3335
return this;
3436
}
3537

38+
public WebAuthenticationBuilder Login(LoginConfig loginConfig)
39+
{
40+
_LoginConfig = loginConfig;
41+
return this;
42+
}
43+
3644
public IConcern Build(IHandler parent, Func<IHandler, IHandler> contentFactory)
3745
{
3846
var sessionConfig = _SessionConfig ?? throw new BuilderMissingPropertyException("Sessions");
3947

40-
return new WebAuthenticationConcern(parent, contentFactory, _AllowAnonymous, sessionConfig, _SetupConfig);
48+
var loginConfig = _LoginConfig ?? throw new BuilderMissingPropertyException("Login");
49+
50+
return new WebAuthenticationConcern(parent, contentFactory, _AllowAnonymous, sessionConfig, loginConfig, _SetupConfig);
4151
}
4252

4353
#endregion

Modules/Authentication.Web/Concern/WebAuthenticationConcern.cs

+72-12
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
1-
using GenHTTP.Api.Content;
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Threading.Tasks;
4+
5+
using GenHTTP.Api.Content;
26
using GenHTTP.Api.Content.Authentication;
37
using GenHTTP.Api.Protocol;
48
using GenHTTP.Api.Routing;
9+
510
using GenHTTP.Modules.Basics;
6-
using Microsoft.AspNetCore.Razor.Language.Intermediate;
7-
using System;
8-
using System.Collections.Generic;
9-
using System.Threading.Tasks;
1011

1112
namespace GenHTTP.Modules.Authentication.Web.Concern
1213
{
@@ -24,6 +25,10 @@ public sealed class WebAuthenticationConcern : IConcern, IRootPathAppender, IHan
2425

2526
private SessionConfig SessionConfig { get; }
2627

28+
private LoginConfig LoginConfig { get; }
29+
30+
private IHandler LoginHandler { get; }
31+
2732
private SetupConfig? SetupConfig { get; }
2833

2934
private IHandler? SetupHandler { get; }
@@ -33,14 +38,17 @@ public sealed class WebAuthenticationConcern : IConcern, IRootPathAppender, IHan
3338
#region Initialization
3439

3540
public WebAuthenticationConcern(IHandler parent, Func<IHandler, IHandler> contentFactory, bool allowAnonymous,
36-
SessionConfig sessionConfig, SetupConfig? setupConfig)
41+
SessionConfig sessionConfig, LoginConfig loginConfig, SetupConfig? setupConfig)
3742
{
3843
Parent = parent;
3944
Content = contentFactory(this);
4045

4146
AllowAnonymous = allowAnonymous;
4247
SessionConfig = sessionConfig;
4348

49+
LoginConfig = loginConfig;
50+
LoginHandler = loginConfig.Handler.Build(this);
51+
4452
SetupConfig = setupConfig;
4553
SetupHandler = setupConfig?.Handler.Build(this);
4654
}
@@ -55,6 +63,9 @@ public WebAuthenticationConcern(IHandler parent, Func<IHandler, IHandler> conten
5563

5664
public async ValueTask<IResponse?> HandleAsync(IRequest request)
5765
{
66+
Login.SetConfig(request, LoginConfig);
67+
SessionHandling.SetConfig(request, SessionConfig);
68+
5869
var segment = request.Target.Current;
5970

6071
if ((SetupConfig != null) && (SetupHandler != null))
@@ -77,7 +88,7 @@ public WebAuthenticationConcern(IHandler parent, Func<IHandler, IHandler> conten
7788
return await SetupHandler.HandleAsync(request);
7889
}
7990
}
80-
else if (segment?.Value == SetupConfig.Route)
91+
else if (segment?.Value == SetupConfig.Route)
8192
{
8293
// do not allow setup to be called again
8394
return await Redirect.To("{web-auth}", true)
@@ -95,10 +106,46 @@ public WebAuthenticationConcern(IHandler parent, Func<IHandler, IHandler> conten
95106
if (authenticatedUser != null)
96107
{
97108
// we're logged in
98-
return await Content.HandleAsync(request);
109+
request.SetUser(authenticatedUser);
110+
111+
// deny login and registration (todo)
112+
113+
var response = await Content.HandleAsync(request);
114+
115+
if (response != null)
116+
{
117+
// refresh the token, so the user will not be logged out eventually
118+
SessionConfig.WriteToken(response, token);
119+
}
120+
121+
return response;
99122
}
100123
}
101124

125+
// handle login and registration (todo)
126+
if (segment?.Value == LoginConfig.Route)
127+
{
128+
request.Target.Advance();
129+
130+
var loginResponse = await LoginHandler.HandleAsync(request);
131+
132+
if (loginResponse != null)
133+
{
134+
// establish the session if the user was authenticated
135+
var authenticatedUser = request.GetUser<IUser>();
136+
137+
if (authenticatedUser != null)
138+
{
139+
var generatedToken = await SessionConfig.StartSession(request, authenticatedUser);
140+
141+
// actually tell the client about the token
142+
SessionConfig.WriteToken(loginResponse, generatedToken);
143+
}
144+
}
145+
146+
return loginResponse;
147+
}
148+
102149
if (AllowAnonymous)
103150
{
104151
var response = await Content.HandleAsync(request);
@@ -111,14 +158,22 @@ public WebAuthenticationConcern(IHandler parent, Func<IHandler, IHandler> conten
111158

112159
return null;
113160
}
114-
115-
// enforce login (todo)
116-
117-
return null;
161+
else
162+
{
163+
// enforce login
164+
return await Redirect.To("{login}/", true)
165+
.Build(this)
166+
.HandleAsync(request);
167+
}
118168
}
119169

120170
public void Append(PathBuilder path, IRequest request, IHandler? child = null)
121171
{
172+
if (child == LoginHandler)
173+
{
174+
path.Preprend(LoginConfig.Route);
175+
}
176+
122177
if (SetupConfig != null)
123178
{
124179
if (child == SetupHandler)
@@ -135,6 +190,11 @@ public void Append(PathBuilder path, IRequest request, IHandler? child = null)
135190
return this;
136191
}
137192

193+
if (segment == "{login}")
194+
{
195+
return LoginHandler;
196+
}
197+
138198
if (segment == "{setup}")
139199
{
140200
return SetupHandler;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
using System.Threading.Tasks;
2+
3+
using GenHTTP.Api.Content;
4+
using GenHTTP.Api.Content.Templating;
5+
using GenHTTP.Api.Protocol;
6+
using GenHTTP.Modules.Basics;
7+
using GenHTTP.Modules.Controllers;
8+
using GenHTTP.Modules.IO;
9+
using GenHTTP.Modules.Razor;
10+
11+
namespace GenHTTP.Modules.Authentication.Web.Controllers
12+
{
13+
14+
public class LoginController
15+
{
16+
17+
public IHandlerBuilder Index()
18+
{
19+
// ToDo: already logged in
20+
return RenderLogin();
21+
}
22+
23+
[ControllerAction(RequestMethod.POST)]
24+
public async Task<IHandlerBuilder> Index(string user, string password, IRequest request)
25+
{
26+
var loginConfig = Login.GetConfig(request);
27+
28+
var result = await loginConfig.PerformLogin!(request, user, password);
29+
30+
if (result.Status == LoginStatus.Success)
31+
{
32+
request.SetUser(result.AuthenticatedUser!);
33+
34+
return Redirect.To("{web-auth}/", true);
35+
}
36+
else
37+
{
38+
return RenderLogin();
39+
}
40+
}
41+
42+
private static IHandlerBuilder RenderLogin()
43+
{
44+
return ModRazor.Page(Resource.FromAssembly("EnterAccount.cshtml"), (r, h) => new BasicModel(r, h))
45+
.Title("Login");
46+
}
47+
48+
}
49+
50+
}

Modules/Authentication.Web/Controllers/SetupController.cs

+6-4
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1-
using GenHTTP.Api.Content;
1+
using System.Threading.Tasks;
2+
3+
using GenHTTP.Api.Content;
24
using GenHTTP.Api.Content.Templating;
35
using GenHTTP.Api.Protocol;
6+
47
using GenHTTP.Modules.Basics;
58
using GenHTTP.Modules.Controllers;
69
using GenHTTP.Modules.IO;
710
using GenHTTP.Modules.Razor;
8-
using System.Threading.Tasks;
911

1012
namespace GenHTTP.Modules.Authentication.Web.Controllers
1113
{
@@ -20,11 +22,11 @@ public IHandlerBuilder Index()
2022
}
2123

2224
[ControllerAction(RequestMethod.POST)]
23-
public async Task<IHandlerBuilder> Index(string username, string password, IRequest request)
25+
public async Task<IHandlerBuilder> Index(string user, string password, IRequest request)
2426
{
2527
var setupConfig = Setup.GetConfig(request);
2628

27-
var result = await setupConfig.PerformSetup!(request, username, password);
29+
var result = await setupConfig.PerformSetup!(request, user, password);
2830

2931
return Redirect.To("{web-auth}/", true);
3032
}

Modules/Authentication.Web/GenHTTP.Modules.Authentication.Web.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747

4848
<ProjectReference Include="..\..\API\GenHTTP.Api.csproj" />
4949

50+
<ProjectReference Include="..\Authentication\GenHTTP.Modules.Authentication.csproj" />
5051
<ProjectReference Include="..\Controllers\GenHTTP.Modules.Controllers.csproj" />
5152
<ProjectReference Include="..\Razor\GenHTTP.Modules.Razor.csproj" />
5253

Modules/Authentication.Web/Login.cs

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
using System;
2+
using System.Threading.Tasks;
3+
4+
using GenHTTP.Api.Content;
5+
using GenHTTP.Api.Content.Authentication;
6+
using GenHTTP.Api.Protocol;
7+
8+
using GenHTTP.Modules.Authentication.Web.Controllers;
9+
using GenHTTP.Modules.Controllers;
10+
11+
namespace GenHTTP.Modules.Authentication.Web
12+
{
13+
14+
public enum LoginStatus
15+
{
16+
Success
17+
}
18+
19+
public record class LoginResult
20+
(
21+
22+
LoginStatus Status,
23+
24+
IUser? AuthenticatedUser
25+
26+
);
27+
28+
public record class LoginConfig
29+
(
30+
31+
IHandlerBuilder Handler,
32+
33+
string Route,
34+
35+
Func<IRequest, string, string, ValueTask<LoginResult>>? PerformLogin
36+
37+
);
38+
39+
public static class Login
40+
{
41+
private const string CONFIG_KEY = "__AUTH_WEB_LOGINCONFIG";
42+
43+
public static LoginConfig BuiltIn(Func<IRequest, string, string, ValueTask<LoginResult>> performLogin, string route = "login")
44+
{
45+
return new LoginConfig(Controller.From<LoginController>(), route, performLogin);
46+
}
47+
48+
public static LoginConfig Custom(IHandlerBuilder handler, string route = "login")
49+
{
50+
return new LoginConfig(handler, route, null);
51+
}
52+
53+
public static LoginConfig GetConfig(IRequest request) => (LoginConfig)request.Properties[CONFIG_KEY];
54+
55+
public static void SetConfig(IRequest request, LoginConfig config) => request.Properties[CONFIG_KEY] = config;
56+
57+
}
58+
59+
}

Modules/Authentication.Web/SessionHandling.cs

+16-8
Original file line numberDiff line numberDiff line change
@@ -12,28 +12,32 @@ public record class SessionConfig
1212

1313
Func<IRequest, ValueTask<string?>> ReadToken,
1414

15-
Action<IResponseBuilder, string> WriteToken,
15+
Action<IResponse, string> WriteToken,
1616

1717
Action<IResponse> ClearToken,
1818

19-
Func<string, ValueTask<IUser?>> VerifyToken
19+
Func<string, ValueTask<IUser?>> VerifyToken,
20+
21+
Func<IRequest, IUser, ValueTask<string>> StartSession
2022

2123
);
2224

2325
public static class SessionHandling
2426
{
27+
private const string CONFIG_KEY = "__AUTH_WEB_SESSIONCONFIG";
28+
2529
private const string COOKIE_NAME = "wa_session";
2630

2731
private const ulong COOKIE_TIMEOUT = 2592000; // 30d
2832

29-
public static SessionConfig BuiltIn(Func<string, ValueTask<IUser?>> verifyToken)
33+
public static SessionConfig BuiltIn(Func<string, ValueTask<IUser?>> verifyToken, Func<IRequest, IUser, ValueTask<string>> startSession)
3034
{
31-
return new(ReadToken, WriteToken, ClearToken, verifyToken);
35+
return new(ReadToken, WriteToken, ClearToken, verifyToken, startSession);
3236
}
3337

34-
public static SessionConfig Custom(Func<IRequest, ValueTask<string?>> readToken, Action<IResponseBuilder, string> writeToken, Action<IResponse> clearToken, Func<string, ValueTask<IUser?>> verifyToken)
38+
public static SessionConfig Custom(Func<IRequest, ValueTask<string?>> readToken, Action<IResponse, string> writeToken, Action<IResponse> clearToken, Func<string, ValueTask<IUser?>> verifyToken, Func<IRequest, IUser, ValueTask<string>> startSession)
3539
{
36-
return new(readToken, writeToken, clearToken, verifyToken);
40+
return new(readToken, writeToken, clearToken, verifyToken, startSession);
3741
}
3842

3943
private static ValueTask<string?> ReadToken(IRequest request)
@@ -46,16 +50,20 @@ public static SessionConfig Custom(Func<IRequest, ValueTask<string?>> readToken,
4650
return new();
4751
}
4852

49-
private static void WriteToken(IResponseBuilder response, string value)
53+
private static void WriteToken(IResponse response, string value)
5054
{
51-
response.Cookie(new(COOKIE_NAME, value, COOKIE_TIMEOUT));
55+
response.SetCookie(new(COOKIE_NAME, value, COOKIE_TIMEOUT));
5256
}
5357

5458
private static void ClearToken(IResponse response)
5559
{
5660
response.SetCookie(new(COOKIE_NAME, string.Empty, 0));
5761
}
5862

63+
public static SessionConfig GetConfig(IRequest request) => (SessionConfig)request.Properties[CONFIG_KEY];
64+
65+
public static void SetConfig(IRequest request, SessionConfig config) => request.Properties[CONFIG_KEY] = config;
66+
5967
}
6068

6169
}

0 commit comments

Comments
 (0)