Completed adjustments for the new configuration of oauth2 and token auth
This commit is contained in:
@@ -1,65 +0,0 @@
|
||||
using MoonCore.Extended.Abstractions;
|
||||
using MoonCore.Extended.Interfaces;
|
||||
using MoonCore.Extended.OAuth2.Models;
|
||||
using MoonCore.Extensions;
|
||||
using Moonlight.ApiServer.Configuration;
|
||||
using Moonlight.ApiServer.Database.Entities;
|
||||
using Moonlight.Shared.Http.Responses.OAuth2;
|
||||
|
||||
namespace Moonlight.ApiServer.Implementations;
|
||||
|
||||
public class TestyOuth2Provider : IOAuth2Provider
|
||||
{
|
||||
public async Task<Dictionary<string, object>> Sync(IServiceProvider provider, AccessData accessData)
|
||||
{
|
||||
var logger = provider.GetRequiredService<ILogger<TestyOuth2Provider>>();
|
||||
|
||||
try
|
||||
{
|
||||
var configuration = provider.GetRequiredService<AppConfiguration>();
|
||||
|
||||
using var httpClient = new HttpClient();
|
||||
|
||||
httpClient.DefaultRequestHeaders.Add("Authorization", accessData.AccessToken);
|
||||
|
||||
var response = await httpClient.GetAsync($"{configuration.PublicUrl}/oauth2/info");
|
||||
await response.HandlePossibleApiError();
|
||||
var info = await response.ParseAsJson<InfoResponse>();
|
||||
|
||||
var userRepo = provider.GetRequiredService<DatabaseRepository<User>>();
|
||||
var user = userRepo.Get().FirstOrDefault(x => x.Email == info.Email);
|
||||
|
||||
if (user == null) // User not found, register a new one
|
||||
{
|
||||
user = userRepo.Add(new User()
|
||||
{
|
||||
Email = info.Email,
|
||||
Username = info.Username
|
||||
});
|
||||
}
|
||||
else if (user.Username != info.Username) // Username updated?
|
||||
{
|
||||
// Username not used by another user?
|
||||
if (!userRepo.Get().Any(x => x.Username == info.Username))
|
||||
{
|
||||
// Update username
|
||||
user.Username = info.Username;
|
||||
userRepo.Update(user);
|
||||
}
|
||||
}
|
||||
|
||||
return new()
|
||||
{
|
||||
{
|
||||
"userId",
|
||||
user.Id
|
||||
}
|
||||
};
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.LogCritical("Unable to sync user: {e}", e);
|
||||
return new();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -127,8 +127,6 @@ builder.Services.AddPlugins(configuration =>
|
||||
configuration.AddAssembly(Assembly.GetEntryAssembly()!);
|
||||
});
|
||||
|
||||
builder.Services.AddSingleton<MoonCore.Extended.Interfaces.IOAuth2Provider>(new TestyOuth2Provider());
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
await Startup.PrepareDatabase(app);
|
||||
|
||||
@@ -4,6 +4,7 @@ using MoonCore.Exceptions;
|
||||
using MoonCore.Extended.Abstractions;
|
||||
using MoonCore.Extended.Extensions;
|
||||
using MoonCore.Extended.Helpers;
|
||||
using MoonCore.Extended.OAuth2.Consumer;
|
||||
using MoonCore.Extensions;
|
||||
using MoonCore.Helpers;
|
||||
using Moonlight.ApiServer.Configuration;
|
||||
@@ -11,6 +12,7 @@ using Moonlight.ApiServer.Database.Entities;
|
||||
using Moonlight.ApiServer.Helpers;
|
||||
using Moonlight.ApiServer.Interfaces.OAuth2;
|
||||
using Moonlight.ApiServer.Interfaces.Startup;
|
||||
using Moonlight.Shared.Http.Responses.OAuth2;
|
||||
|
||||
namespace Moonlight.ApiServer;
|
||||
|
||||
@@ -127,7 +129,7 @@ public static class Startup
|
||||
return Task.FromResult(true);
|
||||
};
|
||||
|
||||
authenticationConfig.ProcessRefresh = (oldData, newData, serviceProvider) =>
|
||||
authenticationConfig.ProcessRefresh = async (oldData, newData, serviceProvider) =>
|
||||
{
|
||||
var oauth2Providers = serviceProvider.GetRequiredService<IOAuth2Provider[]>();
|
||||
|
||||
@@ -141,7 +143,7 @@ public static class Startup
|
||||
if (!oldData.TryGetValue("userId", out var userIdStr) ||
|
||||
!userIdStr.TryGetInt32(out var userId))
|
||||
{
|
||||
return Task.FromResult(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Load user from database if existent
|
||||
@@ -152,24 +154,24 @@ public static class Startup
|
||||
.FirstOrDefault(x => x.Id == userId);
|
||||
|
||||
if (user == null)
|
||||
return Task.FromResult(false);
|
||||
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)
|
||||
{
|
||||
var oAuth2Service = serviceProvider.GetRequiredService<OAuth2ConsumerService>();
|
||||
|
||||
try
|
||||
{
|
||||
// It's time to refresh the access to the external oauth2 provider
|
||||
var refreshData = OAuth2Service.RefreshAccess(user.RefreshToken).Result;
|
||||
var refreshData = oAuth2Service.RefreshAccess(user.RefreshToken).Result;
|
||||
|
||||
// Sync user with oauth2 provider
|
||||
var syncedUser = provider.Sync(serviceProvider, refreshData.AccessToken).Result;
|
||||
var syncedUser = await provider.Sync(serviceProvider, refreshData.AccessToken);
|
||||
|
||||
if (syncedUser == null) // User sync has failed. No refresh allowed
|
||||
return false;
|
||||
@@ -178,7 +180,7 @@ public static class Startup
|
||||
// Fetch user model in current db context, just in case the oauth2 provider
|
||||
// uses a different db context or smth
|
||||
|
||||
var userModel = UserRepository
|
||||
var userModel = userRepo
|
||||
.Get()
|
||||
.First(x => x.Id == syncedUser.Id);
|
||||
|
||||
@@ -186,19 +188,22 @@ public static class Startup
|
||||
userModel.RefreshToken = refreshData.RefreshToken;
|
||||
userModel.RefreshTimestamp = DateTime.UtcNow.AddSeconds(refreshData.ExpiresIn);
|
||||
|
||||
UserRepository.Update(userModel);
|
||||
userRepo.Update(userModel);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
var loggerFactory = serviceProvider.GetRequiredService<ILoggerFactory>();
|
||||
var logger = loggerFactory.CreateLogger("OAuth2 Refresh");
|
||||
|
||||
// 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);
|
||||
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 Task.FromResult(true);
|
||||
return true;
|
||||
};
|
||||
});
|
||||
|
||||
@@ -259,7 +264,7 @@ public static class Startup
|
||||
configuration.ClientId = config.Authentication.OAuth2.ClientId;
|
||||
configuration.ClientSecret = config.Authentication.OAuth2.ClientSecret;
|
||||
configuration.AuthorizationRedirect =
|
||||
config.Authentication.OAuth2.AuthorizationRedirect ?? $"{config.PublicUrl}/auth";
|
||||
config.Authentication.OAuth2.AuthorizationRedirect ?? $"{config.PublicUrl}/";
|
||||
|
||||
configuration.AccessEndpoint =
|
||||
config.Authentication.OAuth2.AccessEndpoint ?? $"{config.PublicUrl}/oauth2/access";
|
||||
@@ -279,41 +284,46 @@ public static class Startup
|
||||
|
||||
configuration.AuthorizationEndpoint = config.Authentication.OAuth2.AuthorizationUri!;
|
||||
}
|
||||
|
||||
// TODO: Make modular
|
||||
configuration.ProcessComplete = async (serviceProvider, accessData) =>
|
||||
{
|
||||
var loggerFactory = serviceProvider.GetRequiredService<ILoggerFactory>();
|
||||
var logger = loggerFactory.CreateLogger("OAuth2 Handler");
|
||||
|
||||
var oauth2Providers = serviceProvider.GetRequiredService<IOAuth2Provider[]>();
|
||||
|
||||
// Find oauth2 provider
|
||||
var provider = oauth2Providers.FirstOrDefault();
|
||||
|
||||
if (provider == null)
|
||||
throw new HttpApiException("No oauth2 provider has been registered", 500);
|
||||
|
||||
try
|
||||
{
|
||||
var user = await provider.Sync(serviceProvider, accessData.AccessToken);
|
||||
|
||||
if (user == null)
|
||||
throw new HttpApiException("OAuth2 provider returned empty user", 500);
|
||||
|
||||
return new Dictionary<string, object>()
|
||||
{
|
||||
{"userId", user.Id}
|
||||
};
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.LogTrace("An error occured while syncing user with oauth2 provider: {e}", e);
|
||||
throw new HttpApiException("Unable to synchronize with oauth2 provider", 400);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
/*
|
||||
builder.Services.AddOAuth2Consumer(configuration =>
|
||||
{
|
||||
configuration.ClientId = config.Authentication.OAuth2.ClientId;
|
||||
configuration.ClientSecret = config.Authentication.OAuth2.ClientSecret;
|
||||
configuration.AuthorizationRedirect =
|
||||
config.Authentication.OAuth2.AuthorizationRedirect ?? $"{config.PublicUrl}/auth";
|
||||
|
||||
configuration.AccessEndpoint =
|
||||
config.Authentication.OAuth2.AccessEndpoint ?? $"{config.PublicUrl}/oauth2/access";
|
||||
configuration.RefreshEndpoint =
|
||||
config.Authentication.OAuth2.RefreshEndpoint ?? $"{config.PublicUrl}/oauth2/refresh";
|
||||
|
||||
if (config.Authentication.UseLocalOAuth2)
|
||||
{
|
||||
configuration.AuthorizationEndpoint = config.Authentication.OAuth2.AuthorizationRedirect ??
|
||||
$"{config.PublicUrl}/oauth2/authorize";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (config.Authentication.OAuth2.AuthorizationUri == null)
|
||||
logger.LogWarning(
|
||||
"The 'AuthorizationUri' for the oauth2 client is not set. If you want to use an external oauth2 provider, you need to specify this url. If you want to use the local oauth2 service, set 'UseLocalOAuth2Service' to true");
|
||||
|
||||
configuration.AuthorizationEndpoint = config.Authentication.OAuth2.AuthorizationUri!;
|
||||
}
|
||||
});*/
|
||||
|
||||
if (!config.Authentication.UseLocalOAuth2) return Task.CompletedTask;
|
||||
|
||||
logger.LogInformation("Using local oauth2 provider");
|
||||
|
||||
builder.Services.AddOAuth2Provider(configuration =>
|
||||
builder.AddOAuth2Provider(configuration =>
|
||||
{
|
||||
configuration.AccessSecret = config.Authentication.LocalOAuth2.AccessSecret;
|
||||
configuration.RefreshSecret = config.Authentication.LocalOAuth2.RefreshSecret;
|
||||
@@ -322,7 +332,7 @@ public static class Startup
|
||||
configuration.ClientSecret = config.Authentication.OAuth2.ClientSecret;
|
||||
configuration.CodeSecret = config.Authentication.LocalOAuth2.CodeSecret;
|
||||
configuration.AuthorizationRedirect =
|
||||
config.Authentication.OAuth2.AuthorizationRedirect ?? $"{config.PublicUrl}/auth";
|
||||
config.Authentication.OAuth2.AuthorizationRedirect ?? $"{config.PublicUrl}/";
|
||||
configuration.AccessTokenDuration = 60;
|
||||
configuration.RefreshTokenDuration = 3600;
|
||||
});
|
||||
@@ -333,6 +343,7 @@ public static class Startup
|
||||
public static Task UseOAuth2(WebApplication application)
|
||||
{
|
||||
application.UseOAuth2Consumer();
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user