Starting updating mooncore dependency usage
This commit is contained in:
@@ -29,34 +29,18 @@ public class AppConfiguration
|
||||
|
||||
public class AuthenticationConfig
|
||||
{
|
||||
public string AccessSecret { get; set; } = Formatter.GenerateString(32);
|
||||
public string RefreshSecret { get; set; } = Formatter.GenerateString(32);
|
||||
public int AccessDuration { get; set; } = 60;
|
||||
public int RefreshDuration { get; set; } = 3600;
|
||||
|
||||
public string Secret { get; set; } = Formatter.GenerateString(32);
|
||||
public int TokenDuration { get; set; } = 3600;
|
||||
|
||||
public OAuth2Data OAuth2 { get; set; } = new();
|
||||
public bool UseLocalOAuth2 { get; set; } = true;
|
||||
|
||||
public LocalOAuth2Data LocalOAuth2 { get; set; } = new();
|
||||
|
||||
public class LocalOAuth2Data
|
||||
{
|
||||
public string AccessSecret { get; set; } = Formatter.GenerateString(32);
|
||||
public string RefreshSecret { get; set; } = Formatter.GenerateString(32);
|
||||
public string CodeSecret { get; set; } = Formatter.GenerateString(32);
|
||||
|
||||
public int AccessTokenDuration { get; set; } = 60;
|
||||
public int RefreshTokenDuration { get; set; } = 3600;
|
||||
}
|
||||
|
||||
public class OAuth2Data
|
||||
{
|
||||
public string Secret { get; set; } = Formatter.GenerateString(32);
|
||||
public string ClientId { get; set; } = Formatter.GenerateString(8);
|
||||
public string ClientSecret { get; set; } = Formatter.GenerateString(32);
|
||||
public string? AuthorizationUri { get; set; }
|
||||
public string? AuthorizationEndpoint { get; set; }
|
||||
public string? AuthorizationRedirect { get; set; }
|
||||
public string? AccessEndpoint { get; set; }
|
||||
public string? RefreshEndpoint { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
using MoonCore.Extended.OAuth2.Consumer;
|
||||
namespace Moonlight.ApiServer.Database.Entities;
|
||||
|
||||
namespace Moonlight.ApiServer.Database.Entities;
|
||||
|
||||
public class User : IUserModel
|
||||
public class User
|
||||
{
|
||||
public int Id { get; set; }
|
||||
|
||||
@@ -10,10 +8,6 @@ public class User : IUserModel
|
||||
public string Email { get; set; }
|
||||
public string Password { get; set; }
|
||||
|
||||
public DateTime TokenValidTimestamp { get; set; } = DateTime.UtcNow;
|
||||
public DateTime TokenValidTimestamp { get; set; } = DateTime.MinValue;
|
||||
public string PermissionsJson { get; set; } = "[]";
|
||||
|
||||
public string AccessToken { get; set; } = "";
|
||||
public string RefreshToken { get; set; } = "";
|
||||
public DateTime RefreshTimestamp { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using MoonCore.Attributes;
|
||||
using MoonCore.Extended.Abstractions;
|
||||
using MoonCore.Extended.Helpers;
|
||||
using MoonCore.Extended.PermFilter;
|
||||
using MoonCore.Helpers;
|
||||
using MoonCore.Models;
|
||||
using Moonlight.ApiServer.Database.Entities;
|
||||
|
||||
@@ -1,8 +1,18 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using MoonCore.Attributes;
|
||||
using MoonCore.Authentication;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using MoonCore.Exceptions;
|
||||
using MoonCore.Extended.Abstractions;
|
||||
using MoonCore.Helpers;
|
||||
using Moonlight.ApiServer.Configuration;
|
||||
using Moonlight.ApiServer.Database.Entities;
|
||||
using Moonlight.Shared.Http.Requests.Auth;
|
||||
using Moonlight.Shared.Http.Responses.Auth;
|
||||
using Moonlight.Shared.Http.Responses.OAuth2;
|
||||
|
||||
namespace Moonlight.ApiServer.Http.Controllers.Auth;
|
||||
|
||||
@@ -10,206 +20,138 @@ namespace Moonlight.ApiServer.Http.Controllers.Auth;
|
||||
[Route("api/auth")]
|
||||
public class AuthController : Controller
|
||||
{
|
||||
/*
|
||||
private readonly OAuth2Service OAuth2Service;
|
||||
private readonly TokenHelper TokenHelper;
|
||||
private readonly DatabaseRepository<User> UserRepository;
|
||||
private readonly ILogger<AuthController> Logger;
|
||||
private readonly AppConfiguration Configuration;
|
||||
private readonly IOAuth2Provider[] OAuth2Providers;
|
||||
private readonly IAuthInterceptor[] AuthInterceptors;
|
||||
private readonly ILogger<AuthController> Logger;
|
||||
private readonly DatabaseRepository<User> UserRepository;
|
||||
|
||||
public AuthController(
|
||||
OAuth2Service oAuth2Service,
|
||||
TokenHelper tokenHelper,
|
||||
DatabaseRepository<User> userRepository,
|
||||
AppConfiguration configuration,
|
||||
ILogger<AuthController> logger,
|
||||
IOAuth2Provider[] oAuth2Providers,
|
||||
IAuthInterceptor[] authInterceptors,
|
||||
AppConfiguration configuration)
|
||||
DatabaseRepository<User> userRepository
|
||||
)
|
||||
{
|
||||
OAuth2Service = oAuth2Service;
|
||||
TokenHelper = tokenHelper;
|
||||
UserRepository = userRepository;
|
||||
Logger = logger;
|
||||
OAuth2Providers = oAuth2Providers;
|
||||
AuthInterceptors = authInterceptors;
|
||||
Configuration = configuration;
|
||||
Logger = logger;
|
||||
UserRepository = userRepository;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<OAuth2StartResponse> Start()
|
||||
[AllowAnonymous]
|
||||
[HttpGet("start")]
|
||||
public Task<LoginStartResponse> Start()
|
||||
{
|
||||
var data = await OAuth2Service.StartAuthorizing();
|
||||
|
||||
return Mapper.Map<OAuth2StartResponse>(data);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<OAuth2HandleResponse> Handle([FromBody] OAuth2HandleRequest request)
|
||||
{
|
||||
var accessData = await OAuth2Service.RequestAccess(request.Code);
|
||||
|
||||
// Find oauth2 provider
|
||||
var provider = OAuth2Providers.FirstOrDefault();
|
||||
|
||||
if (provider == null)
|
||||
throw new HttpApiException("No oauth2 provider has been registered", 500);
|
||||
|
||||
// Sync user from oauth2 provider
|
||||
var user = await provider.Sync(HttpContext.RequestServices, accessData.AccessToken);
|
||||
|
||||
if (user == null)
|
||||
throw new HttpApiException("The oauth2 provider was unable to authenticate you", 401);
|
||||
|
||||
// Allow plugins to intercept access calls
|
||||
if (AuthInterceptors.Any(interceptor => !interceptor.AllowAccess(user, HttpContext.RequestServices)))
|
||||
throw new HttpApiException("Unable to get access token", 401);
|
||||
|
||||
// Save oauth2 refresh and access tokens for later use (re-authentication etc.).
|
||||
// Fetch user model in current db context, just in case the oauth2 provider
|
||||
// uses a different db context or smth
|
||||
|
||||
var userModel = UserRepository
|
||||
.Get()
|
||||
.First(x => x.Id == user.Id);
|
||||
|
||||
userModel.AccessToken = accessData.AccessToken;
|
||||
userModel.RefreshToken = accessData.RefreshToken;
|
||||
userModel.RefreshTimestamp = DateTime.UtcNow.AddSeconds(accessData.ExpiresIn);
|
||||
|
||||
UserRepository.Update(userModel);
|
||||
|
||||
// Generate local token-pair for the authentication
|
||||
// between client and the api server
|
||||
|
||||
var authConfig = Configuration.Authentication;
|
||||
|
||||
var tokenPair = TokenHelper.GeneratePair(
|
||||
authConfig.AccessSecret,
|
||||
authConfig.RefreshSecret,
|
||||
data => { data.Add("userId", user.Id); },
|
||||
authConfig.AccessDuration,
|
||||
authConfig.RefreshDuration
|
||||
);
|
||||
|
||||
// Authentication finished. Return data to client
|
||||
|
||||
return new OAuth2HandleResponse()
|
||||
var response = new LoginStartResponse()
|
||||
{
|
||||
AccessToken = tokenPair.AccessToken,
|
||||
RefreshToken = tokenPair.RefreshToken,
|
||||
ExpiresAt = DateTime.UtcNow.AddSeconds(authConfig.AccessDuration)
|
||||
};
|
||||
}
|
||||
|
||||
[HttpPost("refresh")]
|
||||
public async Task<RefreshResponse> Refresh([FromBody] RefreshRequest request)
|
||||
{
|
||||
var authConfig = Configuration.Authentication;
|
||||
|
||||
var tokenPair = TokenHelper.RefreshPair(
|
||||
request.RefreshToken,
|
||||
authConfig.AccessSecret,
|
||||
authConfig.RefreshSecret,
|
||||
(refreshData, newData)
|
||||
=> ProcessRefreshData(refreshData, newData, HttpContext.RequestServices),
|
||||
authConfig.AccessDuration,
|
||||
authConfig.RefreshDuration
|
||||
);
|
||||
|
||||
// Handle refresh error
|
||||
if (!tokenPair.HasValue)
|
||||
throw new HttpApiException("Unable to refresh token", 401);
|
||||
|
||||
// Return data
|
||||
return new RefreshResponse()
|
||||
{
|
||||
AccessToken = tokenPair.Value.AccessToken,
|
||||
RefreshToken = tokenPair.Value.RefreshToken,
|
||||
ExpiresAt = DateTime.UtcNow.AddSeconds(authConfig.AccessDuration)
|
||||
};
|
||||
}
|
||||
|
||||
private bool ProcessRefreshData(Dictionary<string, JsonElement> refreshTokenData, Dictionary<string, object> newData, IServiceProvider serviceProvider)
|
||||
{
|
||||
// Find oauth2 provider
|
||||
var provider = OAuth2Providers.FirstOrDefault();
|
||||
|
||||
if (provider == null)
|
||||
throw new HttpApiException("No oauth2 provider has been registered", 500);
|
||||
|
||||
// Check if the userId is present in the refresh token
|
||||
if (!refreshTokenData.TryGetValue("userId", out var userIdStr) || !userIdStr.TryGetInt32(out var userId))
|
||||
return false;
|
||||
|
||||
// Load user from database if existent
|
||||
var user = UserRepository
|
||||
.Get()
|
||||
.FirstOrDefault(x => x.Id == userId);
|
||||
|
||||
if (user == null)
|
||||
return false;
|
||||
|
||||
// Allow plugins to intercept the refresh call
|
||||
if (AuthInterceptors.Any(interceptor => !interceptor.AllowRefresh(user, serviceProvider)))
|
||||
return false;
|
||||
|
||||
// Check if it's time to resync with the oauth2 provider
|
||||
if (DateTime.UtcNow >= user.RefreshTimestamp)
|
||||
{
|
||||
try
|
||||
{
|
||||
// It's time to refresh the access to the external oauth2 provider
|
||||
var refreshData = OAuth2Service.RefreshAccess(user.RefreshToken).Result;
|
||||
|
||||
// Sync user with oauth2 provider
|
||||
var syncedUser = provider.Sync(serviceProvider, refreshData.AccessToken).Result;
|
||||
|
||||
if (syncedUser == null) // User sync has failed. No refresh allowed
|
||||
return false;
|
||||
|
||||
// Save oauth2 refresh and access tokens for later use (re-authentication etc.).
|
||||
// Fetch user model in current db context, just in case the oauth2 provider
|
||||
// uses a different db context or smth
|
||||
|
||||
var userModel = UserRepository
|
||||
.Get()
|
||||
.First(x => x.Id == syncedUser.Id);
|
||||
|
||||
userModel.AccessToken = refreshData.AccessToken;
|
||||
userModel.RefreshToken = refreshData.RefreshToken;
|
||||
userModel.RefreshTimestamp = DateTime.UtcNow.AddSeconds(refreshData.ExpiresIn);
|
||||
|
||||
UserRepository.Update(userModel);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// We are handling this error more softly, because it will occur when a user hasn't logged in a long period of time
|
||||
Logger.LogDebug("An error occured while refreshing external oauth2 access: {e}", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// All checks have passed, allow refresh
|
||||
newData.Add("userId", user.Id);
|
||||
return true;
|
||||
}*/
|
||||
|
||||
[HttpGet("check")]
|
||||
[RequirePermission("meta.authenticated")]
|
||||
public Task<CheckResponse> Check()
|
||||
{
|
||||
var permClaim = (HttpContext.User as PermClaimsPrinciple)!;
|
||||
var user = (User)permClaim.IdentityModel;
|
||||
|
||||
var response = new CheckResponse()
|
||||
{
|
||||
Email = user.Email,
|
||||
Username = user.Username,
|
||||
Permissions = permClaim.Permissions
|
||||
ClientId = Configuration.Authentication.OAuth2.ClientId,
|
||||
RedirectUri = Configuration.Authentication.OAuth2.AuthorizationRedirect ?? Configuration.PublicUrl,
|
||||
Endpoint = Configuration.Authentication.OAuth2.AuthorizationEndpoint ?? Configuration.PublicUrl + "/oauth2/authorize"
|
||||
};
|
||||
|
||||
return Task.FromResult(response);
|
||||
}
|
||||
|
||||
[AllowAnonymous]
|
||||
[HttpPost("complete")]
|
||||
public async Task<LoginCompleteResponse> Complete([FromBody] LoginCompleteRequest request)
|
||||
{
|
||||
// TODO: Make modular
|
||||
|
||||
// Create http client to call the auth provider
|
||||
using var httpClient = new HttpClient();
|
||||
httpClient.BaseAddress = new Uri(Configuration.PublicUrl);
|
||||
httpClient.DefaultRequestHeaders.Add("Authorization", $"Basic {Configuration.Authentication.OAuth2.ClientSecret}");
|
||||
|
||||
var httpApiClient = new HttpApiClient(httpClient);
|
||||
|
||||
// Call the auth provider
|
||||
OAuth2HandleResponse handleData;
|
||||
|
||||
try
|
||||
{
|
||||
handleData = await httpApiClient.PostJson<OAuth2HandleResponse>("oauth2/handle", new FormUrlEncodedContent(
|
||||
[
|
||||
new KeyValuePair<string, string>("grant_type", "authorization_code"),
|
||||
new KeyValuePair<string, string>("code", request.Code),
|
||||
new KeyValuePair<string, string>("redirect_uri", Configuration.Authentication.OAuth2.AuthorizationRedirect ?? Configuration.PublicUrl),
|
||||
new KeyValuePair<string, string>("client_id", Configuration.Authentication.OAuth2.ClientId)
|
||||
]
|
||||
));
|
||||
}
|
||||
catch (HttpApiException e)
|
||||
{
|
||||
if (e.Status == 400)
|
||||
Logger.LogTrace("The auth server returned an error: {e}", e);
|
||||
else
|
||||
Logger.LogCritical("The auth server returned an error: {e}", e);
|
||||
|
||||
throw new HttpApiException("Unable to request user data", 500);
|
||||
}
|
||||
|
||||
// Handle the returned data
|
||||
var userId = handleData.UserId;
|
||||
|
||||
var user = await UserRepository
|
||||
.Get()
|
||||
.FirstOrDefaultAsync(x => x.Id == userId);
|
||||
|
||||
if (user == null)
|
||||
throw new HttpApiException("Unable to load user data", 500);
|
||||
|
||||
//
|
||||
var permissions = JsonSerializer.Deserialize<string[]>(user.PermissionsJson) ?? [];
|
||||
|
||||
// Generate token
|
||||
var securityTokenDescriptor = new SecurityTokenDescriptor()
|
||||
{
|
||||
Expires = DateTime.Now.AddDays(10),
|
||||
IssuedAt = DateTime.Now,
|
||||
NotBefore = DateTime.Now.AddMinutes(-1),
|
||||
Claims = new Dictionary<string, object>()
|
||||
{
|
||||
{
|
||||
"userId",
|
||||
user.Id
|
||||
},
|
||||
{
|
||||
"permissions",
|
||||
string.Join(";", permissions)
|
||||
}
|
||||
},
|
||||
SigningCredentials = new SigningCredentials(
|
||||
new SymmetricSecurityKey(
|
||||
Encoding.UTF8.GetBytes(Configuration.Authentication.Secret)
|
||||
),
|
||||
SecurityAlgorithms.HmacSha256
|
||||
),
|
||||
Issuer = Configuration.PublicUrl,
|
||||
Audience = Configuration.PublicUrl
|
||||
};
|
||||
|
||||
var jwtSecurityTokenHandler = new JwtSecurityTokenHandler();
|
||||
var securityToken = jwtSecurityTokenHandler.CreateToken(securityTokenDescriptor);
|
||||
|
||||
var jwt = jwtSecurityTokenHandler.WriteToken(securityToken);
|
||||
|
||||
return new()
|
||||
{
|
||||
AccessToken = jwt
|
||||
};
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
[HttpGet("check")]
|
||||
public async Task<CheckResponse> Check()
|
||||
{
|
||||
var userIdClaim = User.Claims.First(x => x.Type == "userId");
|
||||
var userId = int.Parse(userIdClaim.Value);
|
||||
var user = await UserRepository.Get().FirstAsync(x => x.Id == userId);
|
||||
|
||||
var permissions = JsonSerializer.Deserialize<string[]>(user.PermissionsJson) ?? [];
|
||||
|
||||
return new()
|
||||
{
|
||||
Email = user.Email,
|
||||
Username = user.Username,
|
||||
Permissions = string.Join(";", permissions)
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,7 @@ public class FrontendController : Controller
|
||||
}
|
||||
|
||||
[HttpGet("frontend.json")]
|
||||
public async Task<FrontendConfiguration> GetConfiguration()
|
||||
public Task<FrontendConfiguration> GetConfiguration()
|
||||
{
|
||||
var configuration = new FrontendConfiguration()
|
||||
{
|
||||
@@ -39,7 +39,7 @@ public class FrontendController : Controller
|
||||
|
||||
configuration.Scripts = AssetService.GetJavascriptAssets();
|
||||
|
||||
return configuration;
|
||||
return Task.FromResult(configuration);
|
||||
}
|
||||
|
||||
[HttpGet("plugins/{assemblyName}")] // TODO: Test this
|
||||
|
||||
67
Moonlight.ApiServer/Http/Controllers/OAuth2/Login.razor
Normal file
67
Moonlight.ApiServer/Http/Controllers/OAuth2/Login.razor
Normal file
@@ -0,0 +1,67 @@
|
||||
<html lang="en" class="h-full bg-gray-900">
|
||||
<head>
|
||||
<title>Login into your account</title>
|
||||
<meta charset="UTF-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<script src="https://cdn.tailwindcss.com?plugins=forms"></script>
|
||||
</head>
|
||||
<body class="h-full">
|
||||
|
||||
<div class="flex min-h-full flex-col justify-center py-12 sm:px-6 lg:px-8">
|
||||
<div class="sm:mx-auto sm:w-full sm:max-w-md">
|
||||
<img class="mx-auto h-14 w-auto" src="https://help.moonlightpanel.xyz/images/logo.svg" alt="Your Company">
|
||||
<h2 class="mt-6 text-center text-2xl font-bold leading-9 tracking-tight text-gray-100">Login into your account</h2>
|
||||
</div>
|
||||
|
||||
<div class="mt-10 sm:mx-auto sm:w-full sm:max-w-[480px]">
|
||||
<div class="bg-gray-800 px-6 py-12 shadow sm:rounded-lg sm:px-12">
|
||||
|
||||
@if (!string.IsNullOrEmpty(ErrorMessage))
|
||||
{
|
||||
<div class="rounded-lg bg-red-500 p-5 text-center text-white mb-8">
|
||||
@ErrorMessage
|
||||
</div>
|
||||
}
|
||||
|
||||
<form class="space-y-6" method="POST">
|
||||
<div>
|
||||
<label for="email" class="block text-sm font-medium leading-6 text-gray-100">Email address</label>
|
||||
<div class="mt-2">
|
||||
<input id="email" name="email" type="email" autocomplete="email" required class="block bg-white/5 w-full rounded-md border-0 py-1.5 text-gray-100 shadow-sm ring-1 ring-inset ring-gray-700 placeholder:text-gray-600 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="password" class="block text-sm font-medium leading-6 text-gray-100">Password</label>
|
||||
<div class="mt-2">
|
||||
<input id="password" name="password" type="password" autocomplete="current-password" required class="block bg-white/5 w-full rounded-md border-0 py-1.5 text-gray-100 shadow-sm ring-1 ring-inset ring-gray-700 placeholder:text-gray-600 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<button type="submit" class="flex w-full justify-center rounded-md bg-indigo-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600">
|
||||
Login
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<p class="mt-5 text-center text-sm text-gray-500">
|
||||
No account?
|
||||
<a href="?client_id=@(ClientId)&redirect_uri=@(RedirectUri)&response_type=@(ResponseType)&view=register" class="font-semibold leading-6 text-indigo-600 hover:text-indigo-500">
|
||||
Register
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@code
|
||||
{
|
||||
[Parameter] public string ClientId { get; set; }
|
||||
[Parameter] public string RedirectUri { get; set; }
|
||||
[Parameter] public string ResponseType { get; set; }
|
||||
[Parameter] public string? ErrorMessage { get; set; }
|
||||
}
|
||||
290
Moonlight.ApiServer/Http/Controllers/OAuth2/OAuth2Controller.cs
Normal file
290
Moonlight.ApiServer/Http/Controllers/OAuth2/OAuth2Controller.cs
Normal file
@@ -0,0 +1,290 @@
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Security.Claims;
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using MoonCore.Exceptions;
|
||||
using MoonCore.Extended.Abstractions;
|
||||
using MoonCore.Extended.Helpers;
|
||||
using MoonCore.Helpers;
|
||||
using Moonlight.ApiServer.Configuration;
|
||||
using Moonlight.ApiServer.Database.Entities;
|
||||
using Moonlight.Shared.Http.Responses.OAuth2;
|
||||
|
||||
namespace Moonlight.ApiServer.Http.Controllers.OAuth2;
|
||||
|
||||
[ApiController]
|
||||
[Route("oauth2")]
|
||||
public class OAuth2Controller : Controller
|
||||
{
|
||||
private readonly AppConfiguration Configuration;
|
||||
private readonly DatabaseRepository<User> UserRepository;
|
||||
|
||||
public OAuth2Controller(AppConfiguration configuration, DatabaseRepository<User> userRepository)
|
||||
{
|
||||
Configuration = configuration;
|
||||
UserRepository = userRepository;
|
||||
}
|
||||
|
||||
[AllowAnonymous]
|
||||
[HttpGet("authorize")]
|
||||
public async Task Authorize(
|
||||
[FromQuery(Name = "client_id")] string clientId,
|
||||
[FromQuery(Name = "redirect_uri")] string redirectUri,
|
||||
[FromQuery(Name = "response_type")] string responseType,
|
||||
[FromQuery(Name = "view")] string view = "login"
|
||||
)
|
||||
{
|
||||
var requiredRedirectUri = Configuration.Authentication.OAuth2.AuthorizationRedirect ?? Configuration.PublicUrl;
|
||||
|
||||
if (Configuration.Authentication.OAuth2.ClientId != clientId ||
|
||||
requiredRedirectUri != redirectUri ||
|
||||
responseType != "code")
|
||||
{
|
||||
throw new HttpApiException("Invalid oauth2 request", 400);
|
||||
}
|
||||
|
||||
Response.StatusCode = 200;
|
||||
|
||||
if (view == "register")
|
||||
{
|
||||
var html = await ComponentHelper.RenderComponent<Register>(HttpContext.RequestServices, parameters =>
|
||||
{
|
||||
parameters.Add("ClientId", clientId);
|
||||
parameters.Add("RedirectUri", redirectUri);
|
||||
parameters.Add("ResponseType", responseType);
|
||||
});
|
||||
|
||||
await Response.WriteAsync(html);
|
||||
}
|
||||
else
|
||||
{
|
||||
var html = await ComponentHelper.RenderComponent<Login>(HttpContext.RequestServices, parameters =>
|
||||
{
|
||||
parameters.Add("ClientId", clientId);
|
||||
parameters.Add("RedirectUri", redirectUri);
|
||||
parameters.Add("ResponseType", responseType);
|
||||
});
|
||||
|
||||
await Response.WriteAsync(html);
|
||||
}
|
||||
}
|
||||
|
||||
[AllowAnonymous]
|
||||
[HttpPost("authorize")]
|
||||
public async Task AuthorizePost(
|
||||
[FromQuery(Name = "client_id")] string clientId,
|
||||
[FromQuery(Name = "redirect_uri")] string redirectUri,
|
||||
[FromQuery(Name = "response_type")] string responseType,
|
||||
[FromForm(Name = "email")] string email,
|
||||
[FromForm(Name = "password")] string password,
|
||||
[FromForm(Name = "username")] string username = "",
|
||||
[FromQuery(Name = "view")] string view = "login"
|
||||
)
|
||||
{
|
||||
var requiredRedirectUri = Configuration.Authentication.OAuth2.AuthorizationRedirect ?? Configuration.PublicUrl;
|
||||
|
||||
if (Configuration.Authentication.OAuth2.ClientId != clientId ||
|
||||
requiredRedirectUri != redirectUri ||
|
||||
responseType != "code")
|
||||
{
|
||||
throw new HttpApiException("Invalid oauth2 request", 400);
|
||||
}
|
||||
|
||||
if (view == "register" && string.IsNullOrEmpty(username))
|
||||
throw new HttpApiException("You need to provide a username", 400);
|
||||
|
||||
string? errorMessage = null;
|
||||
|
||||
try
|
||||
{
|
||||
if (view == "register")
|
||||
{
|
||||
var user = await Register(username, email, password);
|
||||
var code = await GenerateCode(user);
|
||||
|
||||
Response.Redirect($"{redirectUri}?code={code}");
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
var user = await Login(email, password);
|
||||
var code = await GenerateCode(user);
|
||||
|
||||
Response.Redirect($"{redirectUri}?code={code}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (HttpApiException e)
|
||||
{
|
||||
errorMessage = e.Title;
|
||||
}
|
||||
|
||||
Response.StatusCode = 200;
|
||||
|
||||
if (view == "register")
|
||||
{
|
||||
var html = await ComponentHelper.RenderComponent<Register>(HttpContext.RequestServices, parameters =>
|
||||
{
|
||||
parameters.Add("ClientId", clientId);
|
||||
parameters.Add("RedirectUri", redirectUri);
|
||||
parameters.Add("ResponseType", responseType);
|
||||
parameters.Add("ErrorMessage", errorMessage!);
|
||||
});
|
||||
|
||||
await Response.WriteAsync(html);
|
||||
}
|
||||
else
|
||||
{
|
||||
var html = await ComponentHelper.RenderComponent<Login>(HttpContext.RequestServices, parameters =>
|
||||
{
|
||||
parameters.Add("ClientId", clientId);
|
||||
parameters.Add("RedirectUri", redirectUri);
|
||||
parameters.Add("ResponseType", responseType);
|
||||
parameters.Add("ErrorMessage", errorMessage!);
|
||||
});
|
||||
|
||||
await Response.WriteAsync(html);
|
||||
}
|
||||
}
|
||||
|
||||
[AllowAnonymous]
|
||||
[HttpPost("handle")]
|
||||
public async Task<OAuth2HandleResponse> Handle(
|
||||
[FromForm(Name = "grant_type")] string grantType,
|
||||
[FromForm(Name = "code")] string code,
|
||||
[FromForm(Name = "redirect_uri")] string redirectUri,
|
||||
[FromForm(Name = "client_id")] string clientId
|
||||
)
|
||||
{
|
||||
// Check header
|
||||
if(!Request.Headers.ContainsKey("Authorization"))
|
||||
throw new HttpApiException("You are missing the Authorization header", 400);
|
||||
|
||||
var authorizationHeaderValue = Request.Headers["Authorization"].FirstOrDefault() ?? "";
|
||||
|
||||
if(authorizationHeaderValue != $"Basic {Configuration.Authentication.OAuth2.ClientSecret}")
|
||||
throw new HttpApiException("Invalid Authorization header value", 400);
|
||||
|
||||
// Check form
|
||||
if(grantType != "authorization_code")
|
||||
throw new HttpApiException("Invalid grant type provided", 400);
|
||||
|
||||
if(clientId != Configuration.Authentication.OAuth2.ClientId)
|
||||
throw new HttpApiException("Invalid client id provided", 400);
|
||||
|
||||
if(redirectUri != (Configuration.Authentication.OAuth2.AuthorizationRedirect ?? Configuration.PublicUrl))
|
||||
throw new HttpApiException("Invalid redirect uri provided", 400);
|
||||
|
||||
var jwtSecurityTokenHandler = new JwtSecurityTokenHandler();
|
||||
|
||||
ClaimsPrincipal? codeData;
|
||||
|
||||
try
|
||||
{
|
||||
codeData = jwtSecurityTokenHandler.ValidateToken(code, new TokenValidationParameters()
|
||||
{
|
||||
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(
|
||||
Configuration.Authentication.Secret
|
||||
)),
|
||||
ValidateIssuerSigningKey = true,
|
||||
ValidateLifetime = true,
|
||||
ClockSkew = TimeSpan.Zero,
|
||||
ValidateAudience = false,
|
||||
ValidateIssuer = false
|
||||
}, out _);
|
||||
}
|
||||
catch (SecurityTokenException)
|
||||
{
|
||||
throw new HttpApiException("Invalid code provided", 400);
|
||||
}
|
||||
|
||||
if (codeData == null)
|
||||
throw new HttpApiException("Invalid code provided", 400);
|
||||
|
||||
var userIdClaim = codeData.Claims.FirstOrDefault(x => x.Type == "id");
|
||||
|
||||
if (userIdClaim == null)
|
||||
throw new HttpApiException("Malformed code provided", 400);
|
||||
|
||||
if(!int.TryParse(userIdClaim.Value, out var userId))
|
||||
throw new HttpApiException("Malformed code provided", 400);
|
||||
|
||||
var user = UserRepository
|
||||
.Get()
|
||||
.FirstOrDefault(x => x.Id == userId);
|
||||
|
||||
if (user == null)
|
||||
throw new HttpApiException("Malformed code provided", 400);
|
||||
|
||||
return new()
|
||||
{
|
||||
UserId = user.Id
|
||||
};
|
||||
}
|
||||
|
||||
private async Task<string> GenerateCode(User user)
|
||||
{
|
||||
var securityTokenDescriptor = new SecurityTokenDescriptor()
|
||||
{
|
||||
Expires = DateTime.Now.AddMinutes(1),
|
||||
IssuedAt = DateTime.Now,
|
||||
NotBefore = DateTime.Now.AddMinutes(-1),
|
||||
Claims = new Dictionary<string, object>()
|
||||
{
|
||||
{
|
||||
"id",
|
||||
user.Id
|
||||
}
|
||||
},
|
||||
SigningCredentials = new SigningCredentials(
|
||||
new SymmetricSecurityKey(
|
||||
Encoding.UTF8.GetBytes(Configuration.Authentication.Secret)
|
||||
),
|
||||
SecurityAlgorithms.HmacSha256
|
||||
)
|
||||
};
|
||||
|
||||
var jwtSecurityTokenHandler = new JwtSecurityTokenHandler();
|
||||
var securityToken = jwtSecurityTokenHandler.CreateToken(securityTokenDescriptor);
|
||||
|
||||
return jwtSecurityTokenHandler.WriteToken(securityToken);
|
||||
}
|
||||
|
||||
private async Task<User> Register(string username, string email, string password)
|
||||
{
|
||||
if (await UserRepository.Get().AnyAsync(x => x.Username == username))
|
||||
throw new HttpApiException("A account with that username already exists", 400);
|
||||
|
||||
if (await UserRepository.Get().AnyAsync(x => x.Email == email))
|
||||
throw new HttpApiException("A account with that email already exists", 400);
|
||||
|
||||
var user = new User()
|
||||
{
|
||||
Username = username,
|
||||
Email = email,
|
||||
Password = HashHelper.Hash(password)
|
||||
};
|
||||
|
||||
var finalUser = await UserRepository.Add(user);
|
||||
|
||||
return finalUser;
|
||||
}
|
||||
|
||||
private async Task<User> Login(string email, string password)
|
||||
{
|
||||
var user = await UserRepository
|
||||
.Get()
|
||||
.FirstOrDefaultAsync(x => x.Email == email);
|
||||
|
||||
if (user == null)
|
||||
throw new HttpApiException("Invalid combination of email and password", 400);
|
||||
|
||||
if (!HashHelper.Verify(password, user.Password))
|
||||
throw new HttpApiException("Invalid combination of email and password", 400);
|
||||
|
||||
return user;
|
||||
}
|
||||
}
|
||||
72
Moonlight.ApiServer/Http/Controllers/OAuth2/Register.razor
Normal file
72
Moonlight.ApiServer/Http/Controllers/OAuth2/Register.razor
Normal file
@@ -0,0 +1,72 @@
|
||||
<html lang="en" class="h-full bg-gray-900">
|
||||
<head>
|
||||
<title>Register a new account</title>
|
||||
<meta charset="UTF-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<script src="https://cdn.tailwindcss.com?plugins=forms"></script>
|
||||
</head>
|
||||
<body class="h-full">
|
||||
|
||||
<div class="flex min-h-full flex-col justify-center py-12 sm:px-6 lg:px-8">
|
||||
<div class="sm:mx-auto sm:w-full sm:max-w-md">
|
||||
<img class="mx-auto h-14 w-auto" src="https://help.moonlightpanel.xyz/images/logo.svg" alt="Your Company">
|
||||
<h2 class="mt-6 text-center text-2xl font-bold leading-9 tracking-tight text-gray-100">Create your account</h2>
|
||||
</div>
|
||||
|
||||
<div class="mt-10 sm:mx-auto sm:w-full sm:max-w-[480px]">
|
||||
<div class="bg-gray-800 px-6 py-12 shadow sm:rounded-lg sm:px-12">
|
||||
|
||||
@if (!string.IsNullOrEmpty(ErrorMessage))
|
||||
{
|
||||
<div class="rounded-lg bg-red-500 p-5 text-center text-white mb-8">
|
||||
@ErrorMessage
|
||||
</div>
|
||||
}
|
||||
|
||||
<form class="space-y-6" method="POST">
|
||||
<div>
|
||||
<label for="email" class="block text-sm font-medium leading-6 text-gray-100">Username</label>
|
||||
<div class="mt-2">
|
||||
<input id="username" name="username" type="text" required class="block bg-white/5 w-full rounded-md border-0 py-1.5 text-gray-100 shadow-sm ring-1 ring-inset ring-gray-700 placeholder:text-gray-600 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="email" class="block text-sm font-medium leading-6 text-gray-100">Email address</label>
|
||||
<div class="mt-2">
|
||||
<input id="email" name="email" type="email" autocomplete="email" required class="block bg-white/5 w-full rounded-md border-0 py-1.5 text-gray-100 shadow-sm ring-1 ring-inset ring-gray-700 placeholder:text-gray-600 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="password" class="block text-sm font-medium leading-6 text-gray-100">Password</label>
|
||||
<div class="mt-2">
|
||||
<input id="password" name="password" type="password" autocomplete="current-password" required class="block bg-white/5 w-full rounded-md border-0 py-1.5 text-gray-100 shadow-sm ring-1 ring-inset ring-gray-700 placeholder:text-gray-600 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<button type="submit" class="flex w-full justify-center rounded-md bg-indigo-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600">
|
||||
Create your account
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<p class="mt-5 text-center text-sm text-gray-500">
|
||||
Already registered?
|
||||
<a href="?client_id=@(ClientId)&redirect_uri=@(RedirectUri)&response_type=@(ResponseType)&view=login" class="font-semibold leading-6 text-indigo-600 hover:text-indigo-500">Login</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@code
|
||||
{
|
||||
[Parameter] public string ClientId { get; set; }
|
||||
[Parameter] public string RedirectUri { get; set; }
|
||||
[Parameter] public string ResponseType { get; set; }
|
||||
[Parameter] public string? ErrorMessage { get; set; }
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using MoonCore.Exceptions;
|
||||
using MoonCore.Extended.Abstractions;
|
||||
using MoonCore.Extended.Helpers;
|
||||
using MoonCore.Extended.OAuth2.LocalProvider;
|
||||
using Moonlight.ApiServer.Database.Entities;
|
||||
|
||||
namespace Moonlight.ApiServer.Implementations.OAuth2;
|
||||
|
||||
public class LocalOAuth2Provider : ILocalProviderImplementation<User>
|
||||
{
|
||||
private readonly DatabaseRepository<User> UserRepository;
|
||||
|
||||
public LocalOAuth2Provider(DatabaseRepository<User> userRepository)
|
||||
{
|
||||
UserRepository = userRepository;
|
||||
}
|
||||
|
||||
public async Task SaveChanges(User model)
|
||||
{
|
||||
await UserRepository.Update(model);
|
||||
}
|
||||
|
||||
public async Task<User?> LoadById(int id)
|
||||
{
|
||||
var res = await UserRepository
|
||||
.Get()
|
||||
.FirstOrDefaultAsync(x => x.Id == id);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
public Task<User> Login(string email, string password)
|
||||
{
|
||||
var user = UserRepository.Get().FirstOrDefault(x => x.Email == email);
|
||||
|
||||
if (user == null)
|
||||
throw new HttpApiException("Invalid email or password", 400);
|
||||
|
||||
if(!HashHelper.Verify(password, user.Password))
|
||||
throw new HttpApiException("Invalid email or password", 400);
|
||||
|
||||
return Task.FromResult(user);
|
||||
}
|
||||
|
||||
public async Task<User> Register(string username, string email, string password)
|
||||
{
|
||||
if (UserRepository.Get().Any(x => x.Username == username))
|
||||
throw new HttpApiException("A user with that username already exists", 400);
|
||||
|
||||
if (UserRepository.Get().Any(x => x.Email == email))
|
||||
throw new HttpApiException("A user with that email address already exists", 400);
|
||||
|
||||
var user = new User()
|
||||
{
|
||||
Username = username,
|
||||
Email = email,
|
||||
Password = HashHelper.Hash(password)
|
||||
};
|
||||
|
||||
var finalUser = await UserRepository.Add(user);
|
||||
|
||||
return finalUser;
|
||||
}
|
||||
}
|
||||
@@ -24,8 +24,8 @@
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="MoonCore" Version="1.8.1" />
|
||||
<PackageReference Include="MoonCore.Extended" Version="1.2.6" />
|
||||
<PackageReference Include="MoonCore" Version="1.8.2" />
|
||||
<PackageReference Include="MoonCore.Extended" Version="1.2.7" />
|
||||
<PackageReference Include="MoonCore.PluginFramework" Version="1.0.5" />
|
||||
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="8.0.2" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0"/>
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using MoonCore.Configuration;
|
||||
using MoonCore.Extended.Abstractions;
|
||||
using MoonCore.Extended.Extensions;
|
||||
using MoonCore.Extended.Helpers;
|
||||
using MoonCore.Extended.OAuth2.Consumer;
|
||||
using MoonCore.Extended.OAuth2.Consumer.Extensions;
|
||||
using MoonCore.Extended.OAuth2.LocalProvider;
|
||||
using MoonCore.Extended.OAuth2.LocalProvider.Extensions;
|
||||
using MoonCore.Extended.OAuth2.LocalProvider.Implementations;
|
||||
using MoonCore.Extended.JwtInvalidation;
|
||||
using MoonCore.Extensions;
|
||||
using MoonCore.Helpers;
|
||||
using MoonCore.PluginFramework.Extensions;
|
||||
@@ -17,8 +17,6 @@ using MoonCore.Services;
|
||||
using Moonlight.ApiServer.Configuration;
|
||||
using Moonlight.ApiServer.Database.Entities;
|
||||
using Moonlight.ApiServer.Helpers;
|
||||
using Moonlight.ApiServer.Http.Middleware;
|
||||
using Moonlight.ApiServer.Implementations.OAuth2;
|
||||
using Moonlight.ApiServer.Interfaces.Auth;
|
||||
using Moonlight.ApiServer.Interfaces.OAuth2;
|
||||
using Moonlight.ApiServer.Interfaces.Startup;
|
||||
@@ -78,7 +76,7 @@ public class Startup
|
||||
await RegisterLogging();
|
||||
await RegisterBase();
|
||||
await RegisterDatabase();
|
||||
await RegisterOAuth2();
|
||||
await RegisterAuth();
|
||||
await RegisterCaching();
|
||||
await HookPluginBuild();
|
||||
await HandleConfigureArguments();
|
||||
@@ -90,13 +88,11 @@ public class Startup
|
||||
await PrepareDatabase();
|
||||
|
||||
await UseBase();
|
||||
await UseOAuth2();
|
||||
await UseBaseMiddleware();
|
||||
await UseAuth();
|
||||
await HookPluginConfigure();
|
||||
await UsePluginAssets();
|
||||
|
||||
await MapBase();
|
||||
await MapOAuth2();
|
||||
await HookPluginEndpoints();
|
||||
|
||||
await WebApplication.RunAsync();
|
||||
@@ -240,14 +236,6 @@ public class Startup
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private Task UseBaseMiddleware()
|
||||
{
|
||||
WebApplication.UseMiddleware<AuthorizationMiddleware>();
|
||||
WebApplication.UseMiddleware<ApiAuthenticationMiddleware>();
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private Task MapBase()
|
||||
{
|
||||
WebApplication.MapControllers();
|
||||
@@ -593,50 +581,56 @@ public class Startup
|
||||
|
||||
#endregion
|
||||
|
||||
#region OAuth2
|
||||
#region Authentication & Authorisation
|
||||
|
||||
private Task RegisterOAuth2()
|
||||
private Task RegisterAuth()
|
||||
{
|
||||
WebApplicationBuilder.Services.AddOAuth2Authentication<User>(configuration =>
|
||||
WebApplicationBuilder.Services
|
||||
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
||||
.AddJwtBearer(options =>
|
||||
{
|
||||
options.TokenValidationParameters = new()
|
||||
{
|
||||
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(
|
||||
Configuration.Authentication.Secret
|
||||
)),
|
||||
ValidateIssuerSigningKey = true,
|
||||
ValidateLifetime = true,
|
||||
ClockSkew = TimeSpan.Zero,
|
||||
ValidateAudience = true,
|
||||
ValidAudience = Configuration.PublicUrl,
|
||||
ValidateIssuer = true,
|
||||
ValidIssuer = Configuration.PublicUrl
|
||||
};
|
||||
});
|
||||
|
||||
WebApplicationBuilder.Services.AddJwtInvalidation(options =>
|
||||
{
|
||||
configuration.AccessSecret = Configuration.Authentication.AccessSecret;
|
||||
configuration.RefreshSecret = Configuration.Authentication.RefreshSecret;
|
||||
configuration.RefreshDuration = TimeSpan.FromSeconds(Configuration.Authentication.RefreshDuration);
|
||||
configuration.RefreshInterval = TimeSpan.FromSeconds(Configuration.Authentication.AccessDuration);
|
||||
configuration.ClientId = Configuration.Authentication.OAuth2.ClientId;
|
||||
configuration.ClientSecret = Configuration.Authentication.OAuth2.ClientSecret;
|
||||
configuration.AuthorizeEndpoint = Configuration.PublicUrl + "/api/_auth/oauth2/authorize";
|
||||
configuration.RedirectUri = Configuration.PublicUrl;
|
||||
options.InvalidateTimeProvider = async (provider, principal) =>
|
||||
{
|
||||
var userIdClaim = principal.Claims.First(x => x.Type == "userId");
|
||||
var userId = int.Parse(userIdClaim.Value);
|
||||
|
||||
var userRepository = provider.GetRequiredService<DatabaseRepository<User>>();
|
||||
var user = await userRepository.Get().FirstAsync(x => x.Id == userId);
|
||||
|
||||
return user.TokenValidTimestamp;
|
||||
};
|
||||
});
|
||||
|
||||
WebApplicationBuilder.Services.AddScoped<IDataProvider<User>, LocalOAuth2Provider>();
|
||||
|
||||
if (!Configuration.Authentication.UseLocalOAuth2)
|
||||
return Task.CompletedTask;
|
||||
|
||||
WebApplicationBuilder.Services.AddLocalOAuth2Provider<User>(Configuration.PublicUrl);
|
||||
WebApplicationBuilder.Services.AddScoped<ILocalProviderImplementation<User>, LocalOAuth2Provider>();
|
||||
WebApplicationBuilder.Services.AddScoped<IOAuth2Provider<User>, LocalOAuth2Provider<User>>();
|
||||
|
||||
WebApplicationBuilder.Services.AddAuthorization();
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private Task UseOAuth2()
|
||||
private Task UseAuth()
|
||||
{
|
||||
WebApplication.UseOAuth2Authentication<User>();
|
||||
WebApplication.UseMiddleware<PermissionLoaderMiddleware>();
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private Task MapOAuth2()
|
||||
{
|
||||
WebApplication.MapOAuth2Authentication<User>();
|
||||
|
||||
if (!Configuration.Authentication.UseLocalOAuth2)
|
||||
return Task.CompletedTask;
|
||||
|
||||
WebApplication.MapLocalOAuth2Provider<User>();
|
||||
WebApplication.UseAuthentication();
|
||||
|
||||
WebApplication.UseJwtInvalidation();
|
||||
|
||||
WebApplication.UseAuthorization();
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user