Release v1.0.0 #16
.envDbInitializer.csDependencyInjection.csnuget.config
.gitea/workflows
.gitignoreApiDto
ApiDto.csproj
Backend.slnDockerfileCommon
AuthRoles.csCacheType.csDatabaseType.csOAuthProvider.csPairPeriodTime.csPasswordPolicy.csTwoFactorAuthentication.cs
Requests
Responses
AvailableOAuthProvidersResponse.csCampusBasicInfoResponse.csCampusDetailsResponse.cs
Configuration
DisciplineResponse.csErrorResponse.csFacultyResponse.csGroupDetailsResponse.csGroupResponse.csLectureHallDetailsResponse.csLectureHallResponse.csProfessorResponse.csScheduleResponse.csTotpKeyResponse.csUserResponse.csEndpoint
Backend.httpISaveSettings.cs
README.mdCommon
Attributes
BadRequestResponseAttribute.csCacheMaxAgeAttribute.csLocalhostAttribute.csMaintenanceModeIgnoreAttribute.csNotFoundResponseAttribute.csSwaggerDefaultAttribute.csTokenAuthenticationAttribute.cs
Exceptions
Interfaces
MapperDto
AvailableProvidersConverter.csPairPeriodTimeConverter.csPasswordPolicyConverter.csTwoFactorAuthenticationConverter.cs
Services
Configuration
Core
BackgroundTasks
Middleware
CacheMaxAgeMiddleware.csCookieAuthorizationMiddleware.csCustomExceptionHandlerMiddleware.csJwtRevocationMiddleware.csMaintenanceModeMiddleware.cs
Startup
Model
SwaggerOptions
Validation
Controllers
BaseController.cs
Endpoint.csprojProgram.csConfiguration
V1
AuthController.csCampusController.csDisciplineController.csFacultyController.csGroupController.csImportController.csLectureHallController.csProfessorController.csScheduleController.csSecurityController.cs
WeatherForecastController.csSync
WeatherForecast.cswwwroot
css
swagger
Security
Common
CookieNames.cs
DependencyInjection.csDomain
Caching
CookieOptionsParameters.csOAuth2
OAuthProvider.csOAuthUser.csPasswordPolicy.csRequestContextInfo.csTwoFactorAuthenticator.csUser.csInterfaces
Properties
Security.csprojServices
SqlData
Application
Application.csprojDependencyInjection.cs
Common
Cqrs
Campus
Queries
Discipline
Queries
Faculty
Queries
Group
Queries
LectureHall
Queries
Professor
Queries
GetProfessorDetails
GetProfessorDetailsBySearch
GetProfessorList
Schedule
Interfaces
Domain
Domain.csproj
Schedule
Migrations
MysqlMigrations
Migrations
20240601023106_InitialMigration.Designer.cs20240601023106_InitialMigration.cs20241027034820_RemoveUnusedRef.Designer.cs20241027034820_RemoveUnusedRef.csUberDbContextModelSnapshot.cs
MysqlMigrations.csprojPsqlMigrations
Migrations
20240601021702_InitialMigration.Designer.cs20240601021702_InitialMigration.cs20241027032753_RemoveUnusedRef.Designer.cs20241027032753_RemoveUnusedRef.csUberDbContextModelSnapshot.cs
PsqlMigrations.csprojSqliteMigrations
Persistence
Common
BaseDbContext.csConfigurationResolver.csDatabaseProvider.csDbContextFactory.csModelBuilderExtensions.cs
Contexts
Schedule
EntityTypeConfigurations
Persistence.csprojUberDbContext.cs@ -22,7 +22,7 @@ using OAuthProvider = Mirea.Api.Security.Common.Domain.OAuthProvider;
|
|||||||
namespace Mirea.Api.Endpoint.Controllers.V1;
|
namespace Mirea.Api.Endpoint.Controllers.V1;
|
||||||
|
|
||||||
[ApiVersion("1.0")]
|
[ApiVersion("1.0")]
|
||||||
public class AuthController(IOptionsSnapshot<Admin> user, AuthService auth, PasswordHashService passwordService) : BaseController
|
public class AuthController(IOptionsSnapshot<Admin> user, IOptionsSnapshot<GeneralConfig> generalConfig, AuthService auth, PasswordHashService passwordService, OAuthService oAuthService) : BaseController
|
||||||
{
|
{
|
||||||
private CookieOptionsParameters GetCookieParams() =>
|
private CookieOptionsParameters GetCookieParams() =>
|
||||||
new()
|
new()
|
||||||
@ -31,6 +31,95 @@ public class AuthController(IOptionsSnapshot<Admin> user, AuthService auth, Pass
|
|||||||
Path = UrlHelper.GetSubPathWithoutFirstApiName + "api"
|
Path = UrlHelper.GetSubPathWithoutFirstApiName + "api"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private static string GenerateHtmlResponse(string title, string message, OAuthProvider? provider, bool isError = false)
|
||||||
|
{
|
||||||
|
string messageColor = isError ? "red" : "white";
|
||||||
|
string script = "<script>setTimeout(()=>{if(window.opener){window.opener.postMessage(" +
|
||||||
|
"{success:" + !isError +
|
||||||
|
",provider:'" + (provider == null ? "null" : (int)provider) +
|
||||||
|
"',providerName:'" + (provider == null ? "null" : Enum.GetName(provider.Value)) +
|
||||||
|
"',message:'" + message.Replace("'", "\\'") +
|
||||||
|
"'},'*');}window.close();}, 5000);</script>";
|
||||||
|
|
||||||
|
return $"<!DOCTYPE html><html lang=ru><head><meta charset=UTF-8><meta content=\"width=device-width,initial-scale=1\"name=viewport><link href=\"https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap\"rel=stylesheet><style>body{{background-color:#121212;color:#fff;font-family:Roboto,sans-serif;display:flex;justify-content:center;align-items:center;height:100vh;margin:0;text-align:center}}.container{{max-width:600px;padding:20px;border-radius:8px;background-color:#1e1e1e;box-shadow:0 4px 20px rgba(0,0,0,.5)}}h1{{font-size:24px;margin-bottom:20px}}p{{font-size:16px;color:{messageColor}}}</style><title>{title}</title></head><body><div class=container><h1>{title}</h1><p>{message}<p style=font-size:14px;color:silver;>Это информационная страница. Вы можете закрыть её.</div>{script}</body></html>";
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("OAuth2")]
|
||||||
|
[BadRequestResponse]
|
||||||
|
[ProducesResponseType(typeof(string), StatusCodes.Status200OK)]
|
||||||
|
[Produces("text/html")]
|
||||||
|
[MaintenanceModeIgnore]
|
||||||
|
public async Task<ContentResult> OAuth2([FromQuery] string code, [FromQuery] string state)
|
||||||
|
{
|
||||||
|
var userId = HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||||
|
string title;
|
||||||
|
string message;
|
||||||
|
OAuthProvider provider;
|
||||||
|
OAuthUser oAuthUser;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
(provider, oAuthUser) = await oAuthService.LoginOAuth(HttpContext, GetCookieParams(),
|
||||||
|
HttpContext.GetApiUrl(Url.Action("OAuth2")!), code, state);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
title = "Произошла ошибка при общении с провайдером OAuth!";
|
||||||
|
message = e.Message;
|
||||||
|
return Content(GenerateHtmlResponse(title, message, null, true), "text/html");
|
||||||
|
}
|
||||||
|
|
||||||
|
var userEntity = user.Value;
|
||||||
|
|
||||||
|
if (userId != null)
|
||||||
|
{
|
||||||
|
userEntity.OAuthProviders ??= new Dictionary<OAuthProvider, OAuthUser>();
|
||||||
|
|
||||||
|
if (!userEntity.OAuthProviders.TryAdd(provider, oAuthUser))
|
||||||
|
{
|
||||||
|
title = "Ошибка связи аккаунта!";
|
||||||
|
message = "Этот OAuth провайдер уже связан с вашей учетной записью. Пожалуйста, используйте другого провайдера или удалите связь с аккаунтом.";
|
||||||
|
return Content(GenerateHtmlResponse(title, message, provider, true), "text/html");
|
||||||
|
}
|
||||||
|
|
||||||
|
userEntity.SaveSetting();
|
||||||
|
|
||||||
|
title = "Учетная запись успешно связана.";
|
||||||
|
message = "Вы успешно связали свою учетную запись с провайдером OAuth. Вы можете продолжить использовать приложение.";
|
||||||
|
return Content(GenerateHtmlResponse(title, message, provider), "text/html");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userEntity.OAuthProviders != null &&
|
||||||
|
userEntity.OAuthProviders.TryGetValue(provider, out var userOAuth) &&
|
||||||
|
userOAuth.Id == oAuthUser.Id)
|
||||||
|
{
|
||||||
|
await auth.LoginOAuthAsync(GetCookieParams(), HttpContext, new User
|
||||||
|
{
|
||||||
|
Id = 1.ToString(),
|
||||||
|
Username = userEntity.Username,
|
||||||
|
Email = userEntity.Email,
|
||||||
|
PasswordHash = userEntity.PasswordHash,
|
||||||
|
Salt = userEntity.Salt,
|
||||||
|
TwoFactorAuthenticator = userEntity.TwoFactorAuthenticator,
|
||||||
|
SecondFactorToken = userEntity.Secret,
|
||||||
|
OAuthProviders = userEntity.OAuthProviders
|
||||||
|
}, provider);
|
||||||
|
|
||||||
|
title = "Успешный вход в аккаунт.";
|
||||||
|
message = "Вы успешно вошли в свою учетную запись. Добро пожаловать!";
|
||||||
|
return Content(GenerateHtmlResponse(title, message, provider), "text/html");
|
||||||
|
}
|
||||||
|
|
||||||
|
title = "Вы успешно зарегистрированы.";
|
||||||
|
message = "Процесс завершен. Вы можете закрыть эту страницу.";
|
||||||
|
userEntity.Email = string.IsNullOrEmpty(oAuthUser.Email) ? string.Empty : oAuthUser.Email;
|
||||||
|
userEntity.Username = string.IsNullOrEmpty(oAuthUser.Username) ? string.Empty : oAuthUser.Username;
|
||||||
|
userEntity.OAuthProviders ??= [];
|
||||||
|
userEntity.OAuthProviders.Add(provider, oAuthUser);
|
||||||
|
userEntity.SaveSetting();
|
||||||
|
return Content(GenerateHtmlResponse(title, message, provider), "text/html");
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initiates the OAuth2 authorization process for the selected provider.
|
/// Initiates the OAuth2 authorization process for the selected provider.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
Reference in New Issue
Block a user