authentication refresh fixed, movie creation page added
This commit is contained in:
@@ -29,5 +29,16 @@ public class AccountsController(IAccountsControllerService accountsControllerSer
|
||||
[ProducesResponseType(typeof(void), StatusCodes.Status401Unauthorized)]
|
||||
[ProducesResponseType(typeof(void), StatusCodes.Status403Forbidden)]
|
||||
public async Task<ActionResult> AuthenticateRefresh() => await accountsControllerService.AuthenticateRefresh();
|
||||
|
||||
|
||||
[HttpDelete("logout")]
|
||||
[Authorize(AuthenticationSchemes = "refresh")]
|
||||
[ProducesResponseType(typeof(void), StatusCodes.Status204NoContent)]
|
||||
public async Task<ActionResult> Logout() => await accountsControllerService.Logout();
|
||||
|
||||
[HttpGet("{id}/profile-picture")]
|
||||
[AllowAnonymous]
|
||||
[ProducesResponseType(typeof(AccountProfilePictureResponse), StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<ActionResult> GetAccountProfilePicture([FromRoute(Name = "id")]long id) => await accountsControllerService.GetAccountProfilePicture(id);
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using System.Net;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using WatchIt.Common.Model.Genres;
|
||||
@@ -23,7 +25,7 @@ public class MoviesController(IMoviesControllerService moviesControllerService)
|
||||
public async Task<ActionResult> Get([FromRoute]long id) => await moviesControllerService.Get(id);
|
||||
|
||||
[HttpPost]
|
||||
[Authorize]
|
||||
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
|
||||
[ProducesResponseType(typeof(MovieResponse), StatusCodes.Status201Created)]
|
||||
[ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(typeof(void), StatusCodes.Status401Unauthorized)]
|
||||
@@ -31,15 +33,16 @@ public class MoviesController(IMoviesControllerService moviesControllerService)
|
||||
public async Task<ActionResult> Post([FromBody]MovieRequest body) => await moviesControllerService.Post(body);
|
||||
|
||||
[HttpPut("{id}")]
|
||||
[Authorize]
|
||||
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
|
||||
[ProducesResponseType(typeof(void), StatusCodes.Status204NoContent)]
|
||||
[ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(typeof(void), StatusCodes.Status401Unauthorized)]
|
||||
[ProducesResponseType(typeof(void), StatusCodes.Status403Forbidden)]
|
||||
public async Task<ActionResult> Put([FromRoute]long id, [FromBody]MovieRequest body) => await moviesControllerService.Put(id, body);
|
||||
|
||||
[HttpDelete("{id}")]
|
||||
[Authorize]
|
||||
[ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)]
|
||||
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
|
||||
[ProducesResponseType(typeof(void), StatusCodes.Status204NoContent)]
|
||||
[ProducesResponseType(typeof(void), StatusCodes.Status401Unauthorized)]
|
||||
[ProducesResponseType(typeof(void), StatusCodes.Status403Forbidden)]
|
||||
public async Task<ActionResult> Delete([FromRoute] long id) => await moviesControllerService.Delete(id);
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.4" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ using WatchIt.WebAPI.Services.Controllers.Common;
|
||||
using WatchIt.WebAPI.Services.Utility.Tokens;
|
||||
using WatchIt.WebAPI.Services.Utility.Tokens.Exceptions;
|
||||
using WatchIt.WebAPI.Services.Utility.User;
|
||||
using AccountProfilePicture = WatchIt.Common.Model.Accounts.AccountProfilePicture;
|
||||
|
||||
namespace WatchIt.WebAPI.Services.Controllers.Accounts;
|
||||
|
||||
@@ -73,16 +74,10 @@ public class AccountsControllerService(
|
||||
return RequestResult.Unauthorized();
|
||||
}
|
||||
|
||||
AuthenticateResponse response;
|
||||
string refreshToken;
|
||||
try
|
||||
{
|
||||
Task<string> refreshTokenTask = tokensService.ExtendRefreshTokenAsync(token.Account, token.Id);
|
||||
Task<string> accessTokenTask = tokensService.CreateAccessTokenAsync(token.Account);
|
||||
response = new AuthenticateResponse
|
||||
{
|
||||
AccessToken = await accessTokenTask,
|
||||
RefreshToken = await refreshTokenTask,
|
||||
};
|
||||
refreshToken = await tokensService.ExtendRefreshTokenAsync(token.Account, token.Id);
|
||||
}
|
||||
catch (TokenNotFoundException)
|
||||
{
|
||||
@@ -90,11 +85,48 @@ public class AccountsControllerService(
|
||||
}
|
||||
catch (TokenNotExtendableException)
|
||||
{
|
||||
return RequestResult.Forbidden();
|
||||
refreshToken = userService.GetRawToken().Replace("Bearer ", string.Empty);
|
||||
}
|
||||
|
||||
string accessToken = await tokensService.CreateAccessTokenAsync(token.Account);
|
||||
|
||||
logger.LogInformation($"Account with ID {token.AccountId} was authenticated by token refreshing");
|
||||
return RequestResult.Ok(response);
|
||||
return RequestResult.Ok(new AuthenticateResponse
|
||||
{
|
||||
AccessToken = accessToken,
|
||||
RefreshToken = refreshToken,
|
||||
});
|
||||
}
|
||||
|
||||
public async Task<RequestResult> Logout()
|
||||
{
|
||||
Guid jti = userService.GetJti();
|
||||
AccountRefreshToken? token = await database.AccountRefreshTokens.FirstOrDefaultAsync(x => x.Id == jti);
|
||||
if (token is not null)
|
||||
{
|
||||
database.AccountRefreshTokens.Attach(token);
|
||||
database.AccountRefreshTokens.Remove(token);
|
||||
await database.SaveChangesAsync();
|
||||
}
|
||||
return RequestResult.NoContent();
|
||||
}
|
||||
|
||||
public async Task<RequestResult> GetAccountProfilePicture(long id)
|
||||
{
|
||||
Account? account = await database.Accounts.FirstOrDefaultAsync(x => x.Id == id);
|
||||
if (account is null)
|
||||
{
|
||||
return RequestResult.BadRequest()
|
||||
.AddValidationError("id", "Account with this id does not exists");
|
||||
}
|
||||
|
||||
if (account.ProfilePicture is null)
|
||||
{
|
||||
return RequestResult.NotFound();
|
||||
}
|
||||
|
||||
AccountProfilePictureResponse picture = new AccountProfilePictureResponse(account.ProfilePicture);
|
||||
return RequestResult.Ok(picture);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -8,4 +8,6 @@ public interface IAccountsControllerService
|
||||
Task<RequestResult> Register(RegisterRequest data);
|
||||
Task<RequestResult> Authenticate(AuthenticateRequest data);
|
||||
Task<RequestResult> AuthenticateRefresh();
|
||||
Task<RequestResult> Logout();
|
||||
Task<RequestResult> GetAccountProfilePicture(long id);
|
||||
}
|
||||
@@ -66,7 +66,7 @@ public class MoviesControllerService(DatabaseContext database, IUserService user
|
||||
data.UpdateMedia(item.Media);
|
||||
await database.SaveChangesAsync();
|
||||
|
||||
return RequestResult.Ok();
|
||||
return RequestResult.NoContent();
|
||||
}
|
||||
|
||||
public async Task<RequestResult> Delete(long id)
|
||||
@@ -105,7 +105,7 @@ public class MoviesControllerService(DatabaseContext database, IUserService user
|
||||
database.Media.Remove(item.Media);
|
||||
await database.SaveChangesAsync();
|
||||
|
||||
return RequestResult.Ok();
|
||||
return RequestResult.NoContent();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -85,21 +85,24 @@ public class TokensService(DatabaseContext database, IConfigurationService confi
|
||||
return TokenToString(tokenDescriptor);
|
||||
}
|
||||
|
||||
protected SecurityTokenDescriptor CreateBaseSecurityTokenDescriptor(Account account, Guid id, DateTime expirationTime) => new SecurityTokenDescriptor
|
||||
protected SecurityTokenDescriptor CreateBaseSecurityTokenDescriptor(Account account, Guid id, DateTime expirationTime)
|
||||
{
|
||||
Subject = new ClaimsIdentity(new List<Claim>
|
||||
return new SecurityTokenDescriptor
|
||||
{
|
||||
new Claim(JwtRegisteredClaimNames.Jti, id.ToString()),
|
||||
new Claim(JwtRegisteredClaimNames.Sub, account.Id.ToString()),
|
||||
new Claim(JwtRegisteredClaimNames.Email, account.Email),
|
||||
new Claim(JwtRegisteredClaimNames.UniqueName, account.Username),
|
||||
new Claim(JwtRegisteredClaimNames.Exp, expirationTime.Ticks.ToString()),
|
||||
new Claim("admin", account.IsAdmin.ToString()),
|
||||
}),
|
||||
Expires = expirationTime,
|
||||
Issuer = configurationService.Data.Authentication.Issuer,
|
||||
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configurationService.Data.Authentication.Key)), SecurityAlgorithms.HmacSha512)
|
||||
};
|
||||
Subject = new ClaimsIdentity(new List<Claim>
|
||||
{
|
||||
new Claim(JwtRegisteredClaimNames.Jti, id.ToString()),
|
||||
new Claim(JwtRegisteredClaimNames.Sub, account.Id.ToString()),
|
||||
new Claim(JwtRegisteredClaimNames.Email, account.Email),
|
||||
new Claim(JwtRegisteredClaimNames.UniqueName, account.Username),
|
||||
new Claim(JwtRegisteredClaimNames.Exp, expirationTime.Ticks.ToString()),
|
||||
new Claim("admin", account.IsAdmin.ToString()),
|
||||
}),
|
||||
Expires = expirationTime,
|
||||
Issuer = configurationService.Data.Authentication.Issuer,
|
||||
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configurationService.Data.Authentication.Key)), SecurityAlgorithms.HmacSha512)
|
||||
};
|
||||
}
|
||||
|
||||
protected string TokenToString(SecurityTokenDescriptor tokenDescriptor)
|
||||
{
|
||||
|
||||
@@ -18,6 +18,15 @@ public class UserService(DatabaseContext database, IHttpContextAccessor accessor
|
||||
return accessor.HttpContext.User;
|
||||
}
|
||||
|
||||
public string? GetRawToken()
|
||||
{
|
||||
if (accessor.HttpContext is null)
|
||||
{
|
||||
throw new NullReferenceException();
|
||||
}
|
||||
return accessor.HttpContext.Request.Headers.Authorization;
|
||||
}
|
||||
|
||||
public UserValidator GetValidator()
|
||||
{
|
||||
ClaimsPrincipal rawUser = GetRawUser();
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
using FluentValidation;
|
||||
using WatchIt.Common.Model.Movies;
|
||||
|
||||
namespace WatchIt.WebAPI.Validators.Movies;
|
||||
|
||||
public class MovieRequestValidator : AbstractValidator<MovieRequest>
|
||||
{
|
||||
public MovieRequestValidator()
|
||||
{
|
||||
RuleFor(x => x.Title).NotEmpty().MaximumLength(250);
|
||||
RuleFor(x => x.OriginalTitle).MaximumLength(250);
|
||||
RuleFor(x => x.Description).MaximumLength(1000);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user