rename server
This commit is contained in:
45
AobaServer/AobaAuthenticationHandler.cs
Normal file
45
AobaServer/AobaAuthenticationHandler.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.Extensions.Options;
|
||||
using System.Text.Encodings.Web;
|
||||
|
||||
namespace AobaV2;
|
||||
|
||||
internal class AobaAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
|
||||
{
|
||||
public AobaAuthenticationHandler(IOptionsMonitor<AuthenticationSchemeOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock)
|
||||
{
|
||||
}
|
||||
|
||||
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
protected override Task HandleChallengeAsync(AuthenticationProperties properties)
|
||||
{
|
||||
//Don't challenge API requests
|
||||
if (OriginalPath.StartsWithSegments("/api"))
|
||||
{
|
||||
Response.StatusCode = StatusCodes.Status401Unauthorized;
|
||||
Response.BodyWriter.Complete();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
//Redirect to login page
|
||||
Response.Redirect($"/auth/login?ReturnUrl={Uri.EscapeDataString(OriginalPath)}");
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
protected override Task HandleForbiddenAsync(AuthenticationProperties properties)
|
||||
{
|
||||
//Don't show error page for api requests
|
||||
if (OriginalPath.StartsWithSegments("/api"))
|
||||
{
|
||||
Response.StatusCode = StatusCodes.Status403Forbidden;
|
||||
Response.BodyWriter.Complete();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
//Show Error page
|
||||
Response.Redirect($"/error/{StatusCodes.Status403Forbidden}");
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
27
AobaServer/AobaServer.csproj
Normal file
27
AobaServer/AobaServer.csproj
Normal file
@@ -0,0 +1,27 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<UserSecretsId>9ffcc706-7f1b-48e3-bf30-eab69a90fded</UserSecretsId>
|
||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Isopoh.Cryptography.Argon2" Version="2.0.0" />
|
||||
<PackageReference Include="MaybeError" Version="1.0.5" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.3" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="9.0.3" />
|
||||
<PackageReference Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.7.0" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.21.2" />
|
||||
<PackageReference Include="Microsoft.Web.LibraryManager.Build" Version="3.0.71" />
|
||||
<PackageReference Include="MongoDB.Analyzer" Version="1.5.0" />
|
||||
<PackageReference Include="MongoDB.Bson" Version="3.3.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\AobaCore\AobaCore.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
74
AobaServer/AuthInfo.cs
Normal file
74
AobaServer/AuthInfo.cs
Normal file
@@ -0,0 +1,74 @@
|
||||
using MongoDB.Bson.IO;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace AobaV2;
|
||||
|
||||
public class AuthInfo
|
||||
{
|
||||
public required string Issuer;
|
||||
public required string Audience;
|
||||
public required byte[] SecureKey;
|
||||
|
||||
/// <summary>
|
||||
/// Save this auth into in a json format to the sepcified file
|
||||
/// </summary>
|
||||
/// <param name="path">File path</param>
|
||||
/// <returns></returns>
|
||||
public AuthInfo Save(string path)
|
||||
{
|
||||
File.WriteAllText(path, JsonSerializer.Serialize(this));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a new Auth Info with newly generated keys
|
||||
/// </summary>
|
||||
/// <param name="issuer"></param>
|
||||
/// <param name="audience"></param>
|
||||
/// <returns></returns>
|
||||
public static AuthInfo Create(string issuer, string audience)
|
||||
{
|
||||
var auth = new AuthInfo
|
||||
{
|
||||
Issuer = issuer,
|
||||
Audience = audience,
|
||||
SecureKey = GenetateJWTKey()
|
||||
};
|
||||
return auth;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load auth info from a json file
|
||||
/// </summary>
|
||||
/// <param name="path">File path</param>
|
||||
/// <returns></returns>
|
||||
internal static AuthInfo? Load(string path)
|
||||
{
|
||||
return JsonSerializer.Deserialize<AuthInfo>(File.ReadAllText(path));
|
||||
}
|
||||
|
||||
internal static AuthInfo LoadOrCreate(string path, string issuer, string audience)
|
||||
{
|
||||
if (File.Exists(path))
|
||||
{
|
||||
var loaded = Load(path);
|
||||
if(loaded != null)
|
||||
return loaded;
|
||||
}
|
||||
var info = Create(issuer, audience);
|
||||
info.Save(path);
|
||||
return info;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a new key for use by JWT
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static byte[] GenetateJWTKey(int size = 64)
|
||||
{
|
||||
var key = new byte[size];
|
||||
RandomNumberGenerator.Fill(key);
|
||||
return key;
|
||||
}
|
||||
}
|
||||
19
AobaServer/Controllers/Api/AuthApi.cs
Normal file
19
AobaServer/Controllers/Api/AuthApi.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace AobaV2.Controllers.Api;
|
||||
|
||||
[Route("/api/auth")]
|
||||
public class AuthApi : ControllerBase
|
||||
{
|
||||
[HttpGet("login")]
|
||||
public async Task<IActionResult> LoginAsync()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
[HttpGet("register")]
|
||||
public async Task<IActionResult> RegisterAsync()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
23
AobaServer/Controllers/AuthController.cs
Normal file
23
AobaServer/Controllers/AuthController.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace AobaV2.Controllers;
|
||||
|
||||
[AllowAnonymous]
|
||||
[Route("auth")]
|
||||
public class AuthController : Controller
|
||||
{
|
||||
[HttpGet("login")]
|
||||
public IActionResult Login([FromQuery] string returnUrl)
|
||||
{
|
||||
ViewData["returnUrl"] = returnUrl;
|
||||
return View();
|
||||
}
|
||||
|
||||
[HttpGet("register/{token}")]
|
||||
public IActionResult Register(string token)
|
||||
{
|
||||
|
||||
return View(token);
|
||||
}
|
||||
}
|
||||
27
AobaServer/Controllers/MediaController.cs
Normal file
27
AobaServer/Controllers/MediaController.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using AobaCore;
|
||||
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Driver;
|
||||
|
||||
namespace AobaV2.Controllers;
|
||||
|
||||
[Route("/m")]
|
||||
public class MediaController(MediaService media) : Controller
|
||||
{
|
||||
[HttpGet("{id}")]
|
||||
public IActionResult Media(ObjectId id)
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
[HttpGet("/i/{id}/{*rest}")]
|
||||
public async Task<IActionResult> LegacyRedirectAsync(ObjectId id, string rest, [FromServices] AobaService aoba)
|
||||
{
|
||||
var media = await aoba.GetMediaAsync(id);
|
||||
if (media == null)
|
||||
return NotFound();
|
||||
return LocalRedirectPermanent($"/m/{media.Id}/{rest}");
|
||||
}
|
||||
}
|
||||
31
AobaServer/Dockerfile
Normal file
31
AobaServer/Dockerfile
Normal file
@@ -0,0 +1,31 @@
|
||||
# See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging.
|
||||
|
||||
# This stage is used when running from VS in fast mode (Default for Debug configuration)
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base
|
||||
USER $APP_UID
|
||||
WORKDIR /app
|
||||
EXPOSE 8080
|
||||
EXPOSE 8081
|
||||
|
||||
|
||||
# This stage is used to build the service project
|
||||
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
|
||||
ARG BUILD_CONFIGURATION=Release
|
||||
WORKDIR /src
|
||||
COPY ["AobaV2/AobaV2.csproj", "AobaV2/"]
|
||||
RUN dotnet restore "./AobaV2/AobaV2.csproj"
|
||||
RUN npm install
|
||||
COPY . .
|
||||
WORKDIR "/src/AobaV2"
|
||||
RUN dotnet build "./AobaV2.csproj" -c $BUILD_CONFIGURATION -o /app/build
|
||||
|
||||
# This stage is used to publish the service project to be copied to the final stage
|
||||
FROM build AS publish
|
||||
ARG BUILD_CONFIGURATION=Release
|
||||
RUN dotnet publish "./AobaV2.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
|
||||
|
||||
# This stage is used in production or when running from VS in regular mode (Default when not using the Debug configuration)
|
||||
FROM base AS final
|
||||
WORKDIR /app
|
||||
COPY --from=publish /app/publish .
|
||||
ENTRYPOINT ["dotnet", "AobaV2.dll"]
|
||||
31
AobaServer/Models/BsonIdModelBinderProvider.cs
Normal file
31
AobaServer/Models/BsonIdModelBinderProvider.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using MongoDB.Bson;
|
||||
|
||||
namespace AobaV2.Models;
|
||||
|
||||
public class BsonIdModelBinderProvider : IModelBinderProvider
|
||||
{
|
||||
public IModelBinder? GetBinder(ModelBinderProviderContext context)
|
||||
{
|
||||
if (context.Metadata.ModelType == typeof(ObjectId))
|
||||
return new BsonIdModelBinder();
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
public class BsonIdModelBinder : IModelBinder
|
||||
{
|
||||
public Task BindModelAsync(ModelBindingContext bindingContext)
|
||||
{
|
||||
var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
|
||||
if (value == ValueProviderResult.None)
|
||||
return Task.CompletedTask;
|
||||
|
||||
if (ObjectId.TryParse(value.FirstValue, out var id))
|
||||
bindingContext.Result = ModelBindingResult.Success(id);
|
||||
else
|
||||
bindingContext.Result = ModelBindingResult.Failed();
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
100
AobaServer/Program.cs
Normal file
100
AobaServer/Program.cs
Normal file
@@ -0,0 +1,100 @@
|
||||
using AobaCore;
|
||||
|
||||
using AobaV2;
|
||||
using AobaV2.Models;
|
||||
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// Add services to the container.
|
||||
builder.Services
|
||||
.AddControllers(opt => opt.ModelBinderProviders.Add(new BsonIdModelBinderProvider()));
|
||||
|
||||
|
||||
var authInfo = AuthInfo.LoadOrCreate("Auth.json", "aobaV2", "aoba");
|
||||
builder.Services.AddSingleton(authInfo);
|
||||
var signingKey = new SymmetricSecurityKey(authInfo.SecureKey);
|
||||
|
||||
var validationParams = new TokenValidationParameters
|
||||
{
|
||||
ValidateIssuerSigningKey = true,
|
||||
IssuerSigningKey = signingKey,
|
||||
ValidateIssuer = true,
|
||||
ValidIssuer = authInfo.Issuer,
|
||||
ValidateAudience = true,
|
||||
ValidAudience = authInfo.Audience,
|
||||
ValidateLifetime = false,
|
||||
ClockSkew = TimeSpan.FromMinutes(1),
|
||||
};
|
||||
|
||||
builder.Services.AddAuthentication(options =>
|
||||
{
|
||||
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||
options.DefaultChallengeScheme = "Aoba";
|
||||
}).AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options => //Bearer auth
|
||||
{
|
||||
options.TokenValidationParameters = validationParams;
|
||||
options.Events = new JwtBearerEvents
|
||||
{
|
||||
OnMessageReceived = ctx => //Retreive token from cookie if not found in headers
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(ctx.Token))
|
||||
ctx.Token = ctx.Request.Cookies["token"];
|
||||
if (string.IsNullOrWhiteSpace(ctx.Token))
|
||||
ctx.Token = ctx.Request.Headers["Authorization"].FirstOrDefault()?.Replace("Bearer ", "");
|
||||
return Task.CompletedTask;
|
||||
},
|
||||
OnAuthenticationFailed = ctx =>
|
||||
{
|
||||
ctx.Response.Cookies.Append("token", "", new CookieOptions
|
||||
{
|
||||
MaxAge = TimeSpan.Zero,
|
||||
Expires = DateTime.Now
|
||||
});
|
||||
ctx.Options.ForwardChallenge = "Aoba";
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
};
|
||||
}).AddScheme<AuthenticationSchemeOptions, AobaAuthenticationHandler>("Aoba", cfg => { });
|
||||
|
||||
builder.Services.AddAoba();
|
||||
builder.Services.Configure<FormOptions>(opt =>
|
||||
{
|
||||
opt.ValueLengthLimit = int.MaxValue;
|
||||
opt.MultipartBodyLengthLimit = int.MaxValue;
|
||||
});
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
if (!app.Environment.IsDevelopment())
|
||||
{
|
||||
app.UseExceptionHandler("/Home/Error");
|
||||
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
|
||||
app.UseHsts();
|
||||
}
|
||||
|
||||
|
||||
app.UseHttpsRedirection();
|
||||
app.UseRouting();
|
||||
|
||||
|
||||
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
|
||||
app.MapStaticAssets();
|
||||
|
||||
app.MapControllerRoute(
|
||||
name: "default",
|
||||
pattern: "{controller=Home}/{action=Index}/{id?}")
|
||||
.WithStaticAssets();
|
||||
|
||||
|
||||
app.Run();
|
||||
31
AobaServer/Properties/launchSettings.json
Normal file
31
AobaServer/Properties/launchSettings.json
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"profiles": {
|
||||
"http": {
|
||||
"commandName": "Project",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"dotnetRunMessages": true,
|
||||
"applicationUrl": "http://localhost:5164"
|
||||
},
|
||||
"https": {
|
||||
"commandName": "Project",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"dotnetRunMessages": true,
|
||||
"applicationUrl": "https://localhost:7167;http://localhost:5164"
|
||||
},
|
||||
"Container (Dockerfile)": {
|
||||
"commandName": "Docker",
|
||||
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_HTTPS_PORTS": "8081",
|
||||
"ASPNETCORE_HTTP_PORTS": "8080"
|
||||
},
|
||||
"publishAllPorts": true,
|
||||
"useSSL": true
|
||||
}
|
||||
},
|
||||
"$schema": "https://json.schemastore.org/launchsettings.json"
|
||||
}
|
||||
8
AobaServer/appsettings.Development.json
Normal file
8
AobaServer/appsettings.Development.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
}
|
||||
}
|
||||
9
AobaServer/appsettings.json
Normal file
9
AobaServer/appsettings.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
}
|
||||
Reference in New Issue
Block a user