diff --git a/Flawless-Version-Control.sln b/Flawless-Version-Control.sln
index e037c95..cad6658 100644
--- a/Flawless-Version-Control.sln
+++ b/Flawless-Version-Control.sln
@@ -8,6 +8,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Flawless.Client.Avanonia",
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Flawless.Server", "Flawless.Server\Flawless.Server.csproj", "{17EA1F4E-7D4E-485D-BDB5-18BBC3FB8DF9}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Flawless.Test.ConsoleApplication", "Flawless.Test.ConsoleApplication\Flawless.Test.ConsoleApplication.csproj", "{29FF9E82-23A4-4F3C-82B6-7ABC72D5990D}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -30,5 +32,9 @@ Global
{17EA1F4E-7D4E-485D-BDB5-18BBC3FB8DF9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{17EA1F4E-7D4E-485D-BDB5-18BBC3FB8DF9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{17EA1F4E-7D4E-485D-BDB5-18BBC3FB8DF9}.Release|Any CPU.Build.0 = Release|Any CPU
+ {29FF9E82-23A4-4F3C-82B6-7ABC72D5990D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {29FF9E82-23A4-4F3C-82B6-7ABC72D5990D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {29FF9E82-23A4-4F3C-82B6-7ABC72D5990D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {29FF9E82-23A4-4F3C-82B6-7ABC72D5990D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
diff --git a/Flawless-Version-Control.sln.DotSettings.user b/Flawless-Version-Control.sln.DotSettings.user
new file mode 100644
index 0000000..982ffe9
--- /dev/null
+++ b/Flawless-Version-Control.sln.DotSettings.user
@@ -0,0 +1,4 @@
+
+ ForceIncluded
+ ForceIncluded
+ ForceIncluded
\ No newline at end of file
diff --git a/Flawless.Client/Flawless.Client.csproj b/Flawless.Client/Flawless.Client.csproj
index bab7876..e6faf30 100644
--- a/Flawless.Client/Flawless.Client.csproj
+++ b/Flawless.Client/Flawless.Client.csproj
@@ -7,23 +7,14 @@
-
-
-
- all
- runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
-
-
-
- Server
- Public
- True
- True
- obj\Debug\net8.0\
- MSBuild:Compile
-
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
diff --git a/Flawless.Client/Protos/greet.proto b/Flawless.Client/Protos/greet.proto
deleted file mode 100644
index 78a1153..0000000
--- a/Flawless.Client/Protos/greet.proto
+++ /dev/null
@@ -1,21 +0,0 @@
-syntax = "proto3";
-
-option csharp_namespace = "Flawless.Server";
-
-package greet;
-
-// The greeting service definition.
-service Greeter {
- // Sends a greeting
- rpc SayHello (HelloRequest) returns (HelloReply);
-}
-
-// The request message containing the user's name.
-message HelloRequest {
- string name = 1;
-}
-
-// The response message containing the greetings.
-message HelloReply {
- string message = 1;
-}
diff --git a/Flawless.Client/TestFlight.cs b/Flawless.Client/TestFlight.cs
deleted file mode 100644
index 754543f..0000000
--- a/Flawless.Client/TestFlight.cs
+++ /dev/null
@@ -1,5 +0,0 @@
-namespace Flawless.Client;
-
-public class TestFlight
-{
-}
\ No newline at end of file
diff --git a/Flawless.Server/Flawless.Server.csproj b/Flawless.Server/Flawless.Server.csproj
index 398a782..45f718e 100644
--- a/Flawless.Server/Flawless.Server.csproj
+++ b/Flawless.Server/Flawless.Server.csproj
@@ -7,11 +7,13 @@
-
-
-
-
+
+
+
+
+ Protos\auth.proto
+
diff --git a/Flawless.Server/Program.cs b/Flawless.Server/Program.cs
index 8ab227f..3bbd001 100644
--- a/Flawless.Server/Program.cs
+++ b/Flawless.Server/Program.cs
@@ -1,16 +1,51 @@
using Flawless.Server.Services;
+using Flawless.Server.Utility;
+using Microsoft.AspNetCore.Authentication.JwtBearer;
+using Microsoft.IdentityModel.Tokens;
-var builder = WebApplication.CreateBuilder(args);
+bool init = true;
+while (init)
+{
+ init = false;
+ var builder = WebApplication.CreateBuilder(args);
+ builder.Services.AddGrpc(x =>
+ {
+ });
-// Add services to the container.
-builder.Services.AddGrpc();
+ builder.Services.AddAuthentication(x =>
+ {
+ x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
+ x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
+ }).AddJwtBearer(o =>
+ {
+ o.TokenValidationParameters = new TokenValidationParameters()
+ {
+ ValidateIssuer = true,
+ RequireExpirationTime = true,
+ ValidateAudience = true,
+ ValidateIssuerSigningKey = true,
+ IssuerSigningKey = AuthUtility.SecurityKey,
+ ValidIssuer = AuthUtility.Issuer,
+ ValidAudience = AuthUtility.Audience,
+ ClockSkew = TimeSpan.Zero,
+ };
+ });
-var app = builder.Build();
+ builder.Services.AddAuthorization();
-// Configure the HTTP request pipeline.
-app.MapGrpcService();
-app.MapGet("/",
- () =>
- "Communication with gRPC endpoints must be made through a gRPC client. To learn how to create a client, visit: https://go.microsoft.com/fwlink/?linkid=2086909");
+ using var app = builder.Build();
-app.Run();
\ No newline at end of file
+ // Enable call router
+ app.UseRouting();
+
+ // Enable authentication
+ app.UseAuthentication();
+ app.UseAuthorization();
+
+ // Configure the HTTP request pipeline.
+ app.MapGrpcService();
+ app.MapGet("/",
+ () => "Communication with gRPC endpoints must be made through a gRPC client. To learn how to create a client, visit: https://go.microsoft.com/fwlink/?linkid=2086909");
+
+ app.Run();
+}
diff --git a/Flawless.Server/Protos/greet.proto b/Flawless.Server/Protos/greet.proto
deleted file mode 100644
index 78a1153..0000000
--- a/Flawless.Server/Protos/greet.proto
+++ /dev/null
@@ -1,21 +0,0 @@
-syntax = "proto3";
-
-option csharp_namespace = "Flawless.Server";
-
-package greet;
-
-// The greeting service definition.
-service Greeter {
- // Sends a greeting
- rpc SayHello (HelloRequest) returns (HelloReply);
-}
-
-// The request message containing the user's name.
-message HelloRequest {
- string name = 1;
-}
-
-// The response message containing the greetings.
-message HelloReply {
- string message = 1;
-}
diff --git a/Flawless.Server/Services/AuthService.cs b/Flawless.Server/Services/AuthService.cs
new file mode 100644
index 0000000..ae71999
--- /dev/null
+++ b/Flawless.Server/Services/AuthService.cs
@@ -0,0 +1,58 @@
+using Flawless.Api;
+using Flawless.Server.Utility;
+using Google.Protobuf.WellKnownTypes;
+using Grpc.Core;
+using Microsoft.AspNetCore.Authorization;
+
+namespace Flawless.Server.Services;
+
+public class AuthService : Auth.AuthBase
+{
+
+ private ILogger _logger;
+
+ public AuthService(ILogger logger)
+ {
+ _logger = logger;
+ }
+
+ public override Task GainToken(AuthRequest request, ServerCallContext context)
+ {
+
+ if (request.UserName != "admin")
+ {
+ return Task.FromResult(new AuthResult()
+ {
+ Token = "",
+ Result = -1,
+ Message = "Invalid username or password"
+ });
+ }
+
+ var token = AuthUtility.GenerateJwtToken(request.UserName, request.Expires);
+
+ _logger.LogInformation($"User '{request.UserName}' has been login in.'");
+ return Task.FromResult(new AuthResult
+ {
+ Token = token,
+ Result = 0
+ });
+ }
+
+ [Authorize]
+ public override Task GetUserInfo(Empty request, ServerCallContext context)
+ {
+ return Task.FromResult(new AuthUserInfo
+ {
+ UserName = context.GetHttpContext().User.Identity?.Name ?? string.Empty,
+ IsSystemAdmin = true,
+ UserId = 1000
+ });
+ }
+
+ [Authorize]
+ public override Task Validate(Empty request, ServerCallContext context)
+ {
+ return Task.FromResult(new Empty());
+ }
+}
\ No newline at end of file
diff --git a/Flawless.Server/Services/GreeterService.cs b/Flawless.Server/Services/GreeterService.cs
deleted file mode 100644
index 9e7ab88..0000000
--- a/Flawless.Server/Services/GreeterService.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-using Grpc.Core;
-using Flawless.Server;
-
-namespace Flawless.Server.Services;
-
-public class GreeterService : Greeter.GreeterBase
-{
- private readonly ILogger _logger;
-
- public GreeterService(ILogger logger)
- {
- _logger = logger;
- }
-
- public override Task SayHello(HelloRequest request, ServerCallContext context)
- {
- return Task.FromResult(new HelloReply
- {
- Message = "Hello " + request.Name
- });
- }
-}
\ No newline at end of file
diff --git a/Flawless.Server/Utility/AuthUtility.cs b/Flawless.Server/Utility/AuthUtility.cs
new file mode 100644
index 0000000..650efb8
--- /dev/null
+++ b/Flawless.Server/Utility/AuthUtility.cs
@@ -0,0 +1,72 @@
+using System.IdentityModel.Tokens.Jwt;
+using System.Security.Claims;
+using System.Text;
+using Microsoft.IdentityModel.Tokens;
+
+namespace Flawless.Server.Utility;
+
+public static class AuthUtility
+{
+ private static JwtSecurityTokenHandler _tokenHandler = new();
+
+ private static SymmetricSecurityKey? _key;
+
+ public static string GenerateSecret(
+ string randomRange = "abcdefghijklmnopqrstuvwxyz1234567890!@#$%^&*()_+=-",
+ int length = 256 / 8)
+ {
+ var rng = Random.Shared;
+
+ String ran = "";
+ for (int i = 0; i < length; i++)
+ {
+ int x = rng.Next(randomRange.Length);
+ ran += randomRange[x];
+ }
+
+ return ran;
+ }
+
+ public static string JwtSecret { get; private set; } = GenerateSecret();
+
+ public static string Issuer { get; private set; } = Environment.GetEnvironmentVariable("issuer") ?? "jwt";
+
+ public static string Audience { get; private set; } = Environment.GetEnvironmentVariable("audience") ?? "jwt";
+
+ public static SymmetricSecurityKey SecurityKey
+ {
+ get
+ {
+ if (_key == null) _key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(JwtSecret));
+ return _key;
+ }
+ }
+
+ public static void ResetKey(string issuer, string audience, string? keySecret = null)
+ {
+ JwtSecret = keySecret ?? GenerateSecret();
+ Issuer = issuer;
+ Audience = audience;
+ _key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(JwtSecret));
+ }
+
+ public static string GenerateJwtToken(string username, uint expires)
+ {
+ var credentials = new SigningCredentials(SecurityKey, SecurityAlgorithms.HmacSha256Signature);
+ var claims = new List
+ {
+ new (ClaimTypes.Name, username),
+ };
+
+ var token = _tokenHandler.CreateJwtSecurityToken(
+ issuer: Issuer,
+ audience: Audience,
+ subject: new ClaimsIdentity(claims),
+ expires: DateTime.Now.AddSeconds(expires),
+ issuedAt: DateTime.Now,
+ notBefore: DateTime.Now,
+ signingCredentials: credentials);
+
+ return _tokenHandler.WriteToken(token);
+ }
+}
\ No newline at end of file
diff --git a/Flawless.Shared/Protos/auth.proto b/Flawless.Shared/Protos/auth.proto
new file mode 100644
index 0000000..46c76b1
--- /dev/null
+++ b/Flawless.Shared/Protos/auth.proto
@@ -0,0 +1,34 @@
+syntax = "proto3";
+option csharp_namespace = "Flawless.Api";
+
+import "google/protobuf/empty.proto";
+
+service Auth {
+ rpc GainToken(AuthRequest) returns (AuthResult);
+ rpc GetUserInfo(google.protobuf.Empty) returns (AuthUserInfo);
+ rpc Validate(google.protobuf.Empty) returns (google.protobuf.Empty);
+}
+
+message RegisterAuthRequest {
+ string user_name = 1;
+ string password = 2;
+}
+
+message AuthRequest {
+ string user_name = 1;
+ string password = 2;
+ uint32 expires = 3;
+}
+
+message AuthResult {
+ int32 result = 1;
+ string message = 2;
+ string token = 3;
+}
+
+message AuthUserInfo {
+ uint64 user_id = 1;
+ string user_name = 2;
+ uint64 last_login = 3;
+ bool is_system_admin = 4;
+}
diff --git a/Flawless.Test.ConsoleApplication/Flawless.Test.ConsoleApplication.csproj b/Flawless.Test.ConsoleApplication/Flawless.Test.ConsoleApplication.csproj
new file mode 100644
index 0000000..45191a0
--- /dev/null
+++ b/Flawless.Test.ConsoleApplication/Flawless.Test.ConsoleApplication.csproj
@@ -0,0 +1,21 @@
+
+
+
+ Exe
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
diff --git a/Flawless.Test.ConsoleApplication/Program.cs b/Flawless.Test.ConsoleApplication/Program.cs
new file mode 100644
index 0000000..ccfad5e
--- /dev/null
+++ b/Flawless.Test.ConsoleApplication/Program.cs
@@ -0,0 +1,31 @@
+// See https://aka.ms/new-console-template for more information
+
+using Flawless.Api;
+using Google.Protobuf.WellKnownTypes;
+using Grpc.Core;
+using Grpc.Net.Client;
+
+var path = "http://localhost:5150";
+
+var rpcChannel = GrpcChannel.ForAddress(path);
+var authService = new Auth.AuthClient(rpcChannel);
+
+var result = await authService.GainTokenAsync(new AuthRequest()
+{
+ UserName = "admin",
+ Expires = 10,
+ Password = "password"
+});
+
+
+if (result.Result == 0)
+{
+ Console.WriteLine($"Token granted: {result.Token}");
+
+ // Thread.Sleep(5 * 1000);
+ var userInfo = await authService.GetUserInfoAsync(new Empty(), new Metadata()
+ {
+ { "Authorization", $"Bearer {result.Token}" }
+ });
+ Console.WriteLine($"UserName: {userInfo.UserName}\nUID: {userInfo.UserId}\nIs Admin: {userInfo.IsSystemAdmin}");
+}
\ No newline at end of file