Release v1.0.0 #16
.envDbInitializer.csDependencyInjection.cs
.gitea/workflows
.gitignoreApiDto
ApiDto.csproj
Backend.slnCommon
Requests
Responses
Endpoint
Backend.http
Common
Attributes
BadRequestResponseAttribute.csLocalhostAttribute.csMaintenanceModeIgnoreAttribute.csNotFoundResponseAttribute.csSwaggerDefaultAttribute.csTokenAuthenticationAttribute.cs
Exceptions
Interfaces
Model
Services
Configuration
AppConfig
ApiVersioningConfiguration.csCacheConfiguration.csEnvironmentConfiguration.csJwtConfiguration.csLoggerConfiguration.csSecureConfiguration.csSwaggerConfiguration.cs
General
Swagger
Controllers
BaseController.cs
Endpoint.csprojConfiguration
V1
AuthController.csCampusController.csDisciplineController.csFacultyController.csGroupController.csLectureHallController.csProfessorController.csScheduleController.cs
WeatherForecastController.csMiddleware
Program.csWeatherForecast.cswwwroot
css
swagger
Security
SqlData
Application
Application.csprojDependencyInjection.cs
Common
Cqrs
Campus
Queries
Discipline
Queries
Faculty
Queries
Group
Queries
LectureHall
Queries
Professor
Queries
Schedule
Interfaces
Domain
Domain.csproj
Schedule
Migrations
MysqlMigrations
Migrations
20240601023106_InitialMigration.Designer.cs20240601023106_InitialMigration.csUberDbContextModelSnapshot.cs
MysqlMigrations.csprojPsqlMigrations
Migrations
20240601021702_InitialMigration.Designer.cs20240601021702_InitialMigration.csUberDbContextModelSnapshot.cs
PsqlMigrations.csprojSqliteMigrations
Persistence
Common
BaseDbContext.csConfigurationResolver.csDatabaseProvider.csDbContextFactory.csModelBuilderExtensions.cs
Contexts
Schedule
EntityTypeConfigurations
Persistence.csprojUberDbContext.cs
140
Endpoint/Controllers/V1/AuthController.cs
Normal file
140
Endpoint/Controllers/V1/AuthController.cs
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using Mirea.Api.Dto.Common;
|
||||||
|
using Mirea.Api.Dto.Requests;
|
||||||
|
using Mirea.Api.Dto.Responses;
|
||||||
|
using Mirea.Api.Endpoint.Common.Model;
|
||||||
|
using Mirea.Api.Security.Common.Dto.Requests;
|
||||||
|
using Mirea.Api.Security.Services;
|
||||||
|
using System;
|
||||||
|
using System.Security;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Mirea.Api.Endpoint.Controllers.V1;
|
||||||
|
|
||||||
|
[ApiVersion("1.0")]
|
||||||
|
public class AuthController(IOptionsSnapshot<Admin> user, AuthService auth, PasswordHashService passwordService) : BaseController, IActionFilter
|
||||||
|
{
|
||||||
|
private string Fingerprint { get; set; } = string.Empty;
|
||||||
|
private string Ip { get; set; } = string.Empty;
|
||||||
|
private string UserAgent { get; set; } = string.Empty;
|
||||||
|
private string RefreshToken { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
private void SetCookie(string name, string value, DateTimeOffset? expires = null)
|
||||||
|
{
|
||||||
|
var cookieOptions = new CookieOptions
|
||||||
|
{
|
||||||
|
Expires = expires,
|
||||||
|
Path = "/api",
|
||||||
|
Domain = Request.Headers["X-Forwarded-Host"],
|
||||||
|
Secure = true,
|
||||||
|
HttpOnly = true
|
||||||
|
};
|
||||||
|
|
||||||
|
Response.Cookies.Append(name, value, cookieOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetRefreshToken(string value, DateTimeOffset? expires = null) =>
|
||||||
|
SetCookie("refresh_token", value, expires);
|
||||||
|
|
||||||
|
private void SetFirstToken(string value, DateTimeOffset? expires = null) =>
|
||||||
|
SetCookie("authentication_token", value, expires);
|
||||||
|
|
||||||
|
[ApiExplorerSettings(IgnoreApi = true)]
|
||||||
|
public void OnActionExecuting(ActionExecutingContext context)
|
||||||
|
{
|
||||||
|
Ip = context.HttpContext.Connection.RemoteIpAddress?.ToString()!;
|
||||||
|
UserAgent = context.HttpContext.Request.Headers.UserAgent.ToString();
|
||||||
|
Fingerprint = context.HttpContext.Request.Cookies["user_key"] ?? string.Empty;
|
||||||
|
RefreshToken = Request.Cookies["refresh_token"] ?? string.Empty;
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(Fingerprint)) return;
|
||||||
|
|
||||||
|
Fingerprint = Guid.NewGuid().ToString().Replace("-", "");
|
||||||
|
SetCookie("user_key", Fingerprint);
|
||||||
|
}
|
||||||
|
|
||||||
|
[ApiExplorerSettings(IgnoreApi = true)]
|
||||||
|
public void OnActionExecuted(ActionExecutedContext context) { }
|
||||||
|
|
||||||
|
[HttpPost("Login")]
|
||||||
|
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||||
|
public async Task<ActionResult<TokenResponse>> Login([FromBody] LoginRequest request)
|
||||||
|
{
|
||||||
|
var userEntity = user.Value;
|
||||||
|
|
||||||
|
if (!userEntity.Username.Equals(request.Username, StringComparison.OrdinalIgnoreCase) &&
|
||||||
|
!userEntity.Email.Equals(request.Username, StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
!passwordService.VerifyPassword(request.Password, userEntity.Salt, userEntity.PasswordHash))
|
||||||
|
return Unauthorized("Invalid username/email or password");
|
||||||
|
|
||||||
|
var token = await auth.GenerateAuthTokensAsync(new TokenRequest
|
||||||
|
{
|
||||||
|
Fingerprint = Fingerprint,
|
||||||
|
Ip = Ip,
|
||||||
|
UserAgent = UserAgent
|
||||||
|
}, "1");
|
||||||
|
|
||||||
|
SetRefreshToken(token.RefreshToken, token.RefreshExpiresIn);
|
||||||
|
|
||||||
|
return Ok(new TokenResponse
|
||||||
|
{
|
||||||
|
AccessToken = token.AccessToken,
|
||||||
|
ExpiresIn = token.AccessExpiresIn
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("ReLogin")]
|
||||||
|
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||||
|
public async Task<ActionResult<TokenResponse>> ReLogin()
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(RefreshToken))
|
||||||
|
return Unauthorized();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var token = await auth.RefreshTokenAsync(
|
||||||
|
new TokenRequest
|
||||||
|
{
|
||||||
|
Ip = Ip,
|
||||||
|
UserAgent = UserAgent,
|
||||||
|
Fingerprint = Fingerprint
|
||||||
|
},
|
||||||
|
RefreshToken
|
||||||
|
);
|
||||||
|
|
||||||
|
SetRefreshToken(token.RefreshToken, token.RefreshExpiresIn);
|
||||||
|
|
||||||
|
return Ok(new TokenResponse
|
||||||
|
{
|
||||||
|
AccessToken = token.AccessToken,
|
||||||
|
ExpiresIn = token.AccessExpiresIn
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (SecurityException)
|
||||||
|
{
|
||||||
|
return Unauthorized();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("Logout")]
|
||||||
|
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||||
|
[Authorize]
|
||||||
|
public async Task<ActionResult> Logout()
|
||||||
|
{
|
||||||
|
SetRefreshToken("", DateTimeOffset.MinValue);
|
||||||
|
SetFirstToken("", DateTimeOffset.MinValue);
|
||||||
|
|
||||||
|
await auth.LogoutAsync(Fingerprint);
|
||||||
|
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("GetRole")]
|
||||||
|
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||||
|
[Authorize]
|
||||||
|
public ActionResult<AuthRoles> GetRole() => Ok(AuthRoles.Admin);
|
||||||
|
}
|
Reference in New Issue
Block a user