Created JWT endpoints
This commit is contained in:
@@ -27,7 +27,6 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Folder Include="DTO\Auth\" />
|
|
||||||
<Folder Include="DTO\Friends\" />
|
<Folder Include="DTO\Friends\" />
|
||||||
<Folder Include="DTO\Groups\" />
|
<Folder Include="DTO\Groups\" />
|
||||||
<Folder Include="DTO\Messages\" />
|
<Folder Include="DTO\Messages\" />
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
namespace BeReadyBackend.DTO.Auth;
|
||||||
|
|
||||||
|
public class GetTokenDto
|
||||||
|
{
|
||||||
|
public string? Token { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
namespace BeReadyBackend.DTO.Auth;
|
||||||
|
|
||||||
|
public class LoginDto
|
||||||
|
{
|
||||||
|
public string? Username { get; set; }
|
||||||
|
public string? Password { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
namespace BeReadyBackend.DTO.Auth;
|
||||||
|
|
||||||
|
public class RefreshTokenDto
|
||||||
|
{
|
||||||
|
public string? Token { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
using BeReadyBackend.DTO.Auth;
|
||||||
|
using BeReadyBackend.Models;
|
||||||
|
using BeReadyBackend.Repositories;
|
||||||
|
using BeReadyBackend.Specifications.Users;
|
||||||
|
using FastEndpoints;
|
||||||
|
using FastEndpoints.Security;
|
||||||
|
|
||||||
|
namespace BeReadyBackend.Endpoints.Auth;
|
||||||
|
|
||||||
|
public class LoginEndpoint(UsersRepository usersRepository, AutoMapper.IMapper mapper) : Endpoint<LoginDto, GetTokenDto>
|
||||||
|
{
|
||||||
|
public override void Configure()
|
||||||
|
{
|
||||||
|
Post("/Auth/Login");
|
||||||
|
AllowAnonymous();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task HandleAsync(LoginDto req, CancellationToken ct)
|
||||||
|
{
|
||||||
|
User? user = await usersRepository.SingleOrDefaultAsync(new GetUserByUsernameSpec(req.Username!), ct);
|
||||||
|
|
||||||
|
if (user == null)
|
||||||
|
{
|
||||||
|
await Send.UnauthorizedAsync(ct);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BCrypt.Net.BCrypt.Verify(req.Password + user.Salt, user.Password))
|
||||||
|
{
|
||||||
|
string jwtToken = JwtBearer.CreateToken(o =>
|
||||||
|
{
|
||||||
|
o.SigningKey = "ThisIsASuperSecretJwtKeyThatIsAtLeast32CharsLong";
|
||||||
|
o.ExpireAt = DateTime.UtcNow.AddDays(15);
|
||||||
|
o.User.Claims.Add(("Username", user.Username)!);
|
||||||
|
o.User.Claims.Add(("FullName", user.FirstName + user.Name));
|
||||||
|
o.User.Claims.Add(("UserId", user.Id.ToString()));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
await Send.OkAsync(new GetTokenDto { Token = jwtToken }, ct);
|
||||||
|
}
|
||||||
|
else await Send.UnauthorizedAsync(ct);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
using System.IdentityModel.Tokens.Jwt;
|
||||||
|
using BeReadyBackend.DTO.Auth;
|
||||||
|
using BeReadyBackend.Models;
|
||||||
|
using BeReadyBackend.Repositories;
|
||||||
|
using BeReadyBackend.Specifications.Users;
|
||||||
|
using FastEndpoints;
|
||||||
|
using FastEndpoints.Security;
|
||||||
|
|
||||||
|
|
||||||
|
namespace BeReadyBackend.Endpoints.Auth;
|
||||||
|
|
||||||
|
public class RefreshTokenEndpoint(UsersRepository usersRepository, AutoMapper.IMapper mapper) : Endpoint<RefreshTokenDto, GetTokenDto>
|
||||||
|
{
|
||||||
|
public override void Configure()
|
||||||
|
{
|
||||||
|
Post("/Auth/RefreshToken");
|
||||||
|
AllowAnonymous();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task HandleAsync(RefreshTokenDto req, CancellationToken ct)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
JwtSecurityTokenHandler handler = new();
|
||||||
|
JwtSecurityToken? token = handler.ReadJwtToken(req.Token);
|
||||||
|
string? username = token.Claims.FirstOrDefault(c => c.Type == "Username")?.Value;
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(username))
|
||||||
|
{
|
||||||
|
await Send.UnauthorizedAsync(ct);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
User? user = await usersRepository.FirstOrDefaultAsync(new GetUserByUsernameSpec(username), ct);
|
||||||
|
if (user is null)
|
||||||
|
{
|
||||||
|
await Send.UnauthorizedAsync(ct);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
string jwtToken = JwtBearer.CreateToken(o =>
|
||||||
|
{
|
||||||
|
o.SigningKey = "ThisIsASuperSecretJwtKeyThatIsAtLeast32CharsLong";
|
||||||
|
o.ExpireAt = DateTime.UtcNow.AddDays(15);
|
||||||
|
o.User.Claims.Add(("Username", user.Username)!);
|
||||||
|
o.User.Claims.Add(("FullName", user.FirstName + user.Name));
|
||||||
|
o.User.Claims.Add(("UserId", user.Id.ToString()));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
await Send.OkAsync(new GetTokenDto { Token = jwtToken }, ct);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
await Send.UnauthorizedAsync(ct);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,10 +15,6 @@ builder.Services
|
|||||||
.AddAuthenticationJwtBearer(s => s.SigningKey = "ThisIsASuperSecretJwtKeyThatIsAtLeast32CharsLong")
|
.AddAuthenticationJwtBearer(s => s.SigningKey = "ThisIsASuperSecretJwtKeyThatIsAtLeast32CharsLong")
|
||||||
.AddAuthorization()
|
.AddAuthorization()
|
||||||
.AddFastEndpoints()
|
.AddFastEndpoints()
|
||||||
.SwaggerDocument(options =>
|
|
||||||
{
|
|
||||||
options.ShortSchemaNames = true;
|
|
||||||
})
|
|
||||||
.AddCors(options =>
|
.AddCors(options =>
|
||||||
{
|
{
|
||||||
options.AddDefaultPolicy(policyBuilder =>
|
options.AddDefaultPolicy(policyBuilder =>
|
||||||
@@ -29,6 +25,10 @@ builder.Services
|
|||||||
.AllowAnyHeader()
|
.AllowAnyHeader()
|
||||||
.WithExposedHeaders(HeaderNames.ContentDisposition);
|
.WithExposedHeaders(HeaderNames.ContentDisposition);
|
||||||
});
|
});
|
||||||
|
})
|
||||||
|
.SwaggerDocument(options =>
|
||||||
|
{
|
||||||
|
options.ShortSchemaNames = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
builder.Services.AddDbContext<BeReadyDbContext>();
|
builder.Services.AddDbContext<BeReadyDbContext>();
|
||||||
@@ -65,8 +65,8 @@ app.UseAuthentication()
|
|||||||
})
|
})
|
||||||
.UseSwaggerGen();
|
.UseSwaggerGen();
|
||||||
|
|
||||||
app.UseHttpsRedirection();
|
// app.UseHttpsRedirection();
|
||||||
|
|
||||||
app.UseCors();
|
// app.UseCors();
|
||||||
|
|
||||||
app.Run();
|
app.Run();
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
using Ardalis.Specification;
|
||||||
|
using BeReadyBackend.Models;
|
||||||
|
|
||||||
|
namespace BeReadyBackend.Specifications.Users;
|
||||||
|
|
||||||
|
public class GetUserByUsernameSpec : SingleResultSpecification<User>
|
||||||
|
{
|
||||||
|
public GetUserByUsernameSpec(string username)
|
||||||
|
{
|
||||||
|
Query
|
||||||
|
.Where(x => x.Username == username);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user