Getting things setup to support token refresh
Some checks failed
continuous-integration/drone/pr Build is failing
Some checks failed
continuous-integration/drone/pr Build is failing
This commit is contained in:
parent
226a4876ff
commit
4cacc03fb8
@ -2,6 +2,7 @@ using Microsoft.AspNetCore.Identity;
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Newsbot.Collector.Api.Domain.Requests;
|
using Newsbot.Collector.Api.Domain.Requests;
|
||||||
using Newsbot.Collector.Api.Domain.Response;
|
using Newsbot.Collector.Api.Domain.Response;
|
||||||
|
using Newsbot.Collector.Api.Domain.Results;
|
||||||
using Newsbot.Collector.Api.Services;
|
using Newsbot.Collector.Api.Services;
|
||||||
using Newsbot.Collector.Domain.Dto;
|
using Newsbot.Collector.Domain.Dto;
|
||||||
using Newsbot.Collector.Domain.Entities;
|
using Newsbot.Collector.Domain.Entities;
|
||||||
@ -43,19 +44,7 @@ public class AccountController : ControllerBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
var response = _identityService.Register(user.Email, user.Password);
|
var response = _identityService.Register(user.Email, user.Password);
|
||||||
|
return CheckIfSuccessful(response);
|
||||||
if (!response.IsSuccessful)
|
|
||||||
{
|
|
||||||
return new BadRequestObjectResult( new AuthFailedResponse
|
|
||||||
{
|
|
||||||
Errors = response.ErrorMessage
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return new OkObjectResult(new AuthSuccessfulResponse
|
|
||||||
{
|
|
||||||
Token = response.Token
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("login")]
|
[HttpPost("login")]
|
||||||
@ -72,18 +61,30 @@ public class AccountController : ControllerBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
var response = _identityService.Login(request.Email, request.Password);
|
var response = _identityService.Login(request.Email, request.Password);
|
||||||
|
return CheckIfSuccessful(response);
|
||||||
|
}
|
||||||
|
|
||||||
if (!response.IsSuccessful)
|
[HttpPost("refresh")]
|
||||||
|
public ActionResult RefreshToken([FromBody] UserRefreshTokenRequest request)
|
||||||
|
{
|
||||||
|
var response = _identityService.RefreshToken(request.Token ?? "", request.RefreshToken ?? "");
|
||||||
|
return CheckIfSuccessful(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ActionResult CheckIfSuccessful(AuthenticationResult result)
|
||||||
|
{
|
||||||
|
if (!result.IsSuccessful)
|
||||||
{
|
{
|
||||||
return new BadRequestObjectResult( new AuthFailedResponse
|
return new BadRequestObjectResult( new AuthFailedResponse
|
||||||
{
|
{
|
||||||
Errors = response.ErrorMessage
|
Errors = result.ErrorMessage
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return new OkObjectResult(new AuthSuccessfulResponse
|
return new OkObjectResult(new AuthSuccessfulResponse
|
||||||
{
|
{
|
||||||
Token = response.Token
|
Token = result.Token,
|
||||||
|
RefreshToken = result.RefreshToken
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
namespace Newsbot.Collector.Api.Domain.Requests;
|
||||||
|
|
||||||
|
public class UserRefreshTokenRequest
|
||||||
|
{
|
||||||
|
public string? Token { get; set; }
|
||||||
|
public string? RefreshToken { get; set; }
|
||||||
|
}
|
@ -5,4 +5,5 @@ public class AuthSuccessfulResponse
|
|||||||
// might want to validate the user before we return the token
|
// might want to validate the user before we return the token
|
||||||
|
|
||||||
public string? Token { get; set; }
|
public string? Token { get; set; }
|
||||||
|
public string? RefreshToken { get; set; }
|
||||||
}
|
}
|
@ -5,6 +5,7 @@ namespace Newsbot.Collector.Api.Domain.Results;
|
|||||||
public class AuthenticationResult
|
public class AuthenticationResult
|
||||||
{
|
{
|
||||||
public string? Token { get; set; }
|
public string? Token { get; set; }
|
||||||
|
public string? RefreshToken { get; set; }
|
||||||
|
|
||||||
public bool IsSuccessful { get; set; }
|
public bool IsSuccessful { get; set; }
|
||||||
public IEnumerable<string>? ErrorMessage { get; set; }
|
public IEnumerable<string>? ErrorMessage { get; set; }
|
||||||
|
@ -50,6 +50,7 @@ builder.Services.AddScoped<IIconsRepository, IconsTable>();
|
|||||||
builder.Services.AddScoped<ISourcesRepository, SourcesTable>();
|
builder.Services.AddScoped<ISourcesRepository, SourcesTable>();
|
||||||
builder.Services.AddScoped<IDiscordNotificationRepository, DiscordNotificationTable>();
|
builder.Services.AddScoped<IDiscordNotificationRepository, DiscordNotificationTable>();
|
||||||
builder.Services.AddScoped<IUserSourceSubscription, UserSourceSubscriptionTable>();
|
builder.Services.AddScoped<IUserSourceSubscription, UserSourceSubscriptionTable>();
|
||||||
|
builder.Services.AddScoped<IRefreshTokenRepository, RefreshTokenTable>();
|
||||||
|
|
||||||
// Configure Identity
|
// Configure Identity
|
||||||
builder.Services.AddScoped<IIdentityService, IdentityService>();
|
builder.Services.AddScoped<IIdentityService, IdentityService>();
|
||||||
@ -81,6 +82,16 @@ var jwtSettings = new JwtSettings();
|
|||||||
config.Bind(nameof(jwtSettings), jwtSettings);
|
config.Bind(nameof(jwtSettings), jwtSettings);
|
||||||
builder.Services.AddSingleton(jwtSettings);
|
builder.Services.AddSingleton(jwtSettings);
|
||||||
|
|
||||||
|
var tokenValidationParameters = new TokenValidationParameters
|
||||||
|
{
|
||||||
|
ValidateIssuerSigningKey = true,
|
||||||
|
IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(jwtSettings.Secret ?? "")),
|
||||||
|
ValidateIssuer = false,
|
||||||
|
ValidateAudience = false,
|
||||||
|
RequireExpirationTime = false,
|
||||||
|
ValidateLifetime = true
|
||||||
|
};
|
||||||
|
builder.Services.AddSingleton(tokenValidationParameters);
|
||||||
|
|
||||||
builder.Services.AddAuthentication(x =>
|
builder.Services.AddAuthentication(x =>
|
||||||
{
|
{
|
||||||
@ -90,17 +101,9 @@ builder.Services.AddAuthentication(x =>
|
|||||||
}).AddJwtBearer(x =>
|
}).AddJwtBearer(x =>
|
||||||
{
|
{
|
||||||
x.SaveToken = true;
|
x.SaveToken = true;
|
||||||
x.TokenValidationParameters = new TokenValidationParameters
|
x.TokenValidationParameters = tokenValidationParameters;
|
||||||
{
|
|
||||||
ValidateIssuerSigningKey = true,
|
|
||||||
IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(jwtSettings.Secret ?? "")),
|
|
||||||
ValidateIssuer = false,
|
|
||||||
ValidateAudience = false,
|
|
||||||
RequireExpirationTime = false,
|
|
||||||
ValidateLifetime = true
|
|
||||||
};
|
|
||||||
});
|
});
|
||||||
|
|
||||||
builder.Services.AddSwaggerGen(cfg =>
|
builder.Services.AddSwaggerGen(cfg =>
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -4,6 +4,9 @@ using System.Text;
|
|||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Microsoft.IdentityModel.Tokens;
|
using Microsoft.IdentityModel.Tokens;
|
||||||
using Newsbot.Collector.Api.Domain.Results;
|
using Newsbot.Collector.Api.Domain.Results;
|
||||||
|
using Newsbot.Collector.Database;
|
||||||
|
using Newsbot.Collector.Domain.Entities;
|
||||||
|
using Newsbot.Collector.Domain.Interfaces;
|
||||||
using Newsbot.Collector.Domain.Models.Config;
|
using Newsbot.Collector.Domain.Models.Config;
|
||||||
|
|
||||||
namespace Newsbot.Collector.Api.Services;
|
namespace Newsbot.Collector.Api.Services;
|
||||||
@ -12,17 +15,22 @@ public interface IIdentityService
|
|||||||
{
|
{
|
||||||
AuthenticationResult Register(string email, string password);
|
AuthenticationResult Register(string email, string password);
|
||||||
AuthenticationResult Login(string email, string password);
|
AuthenticationResult Login(string email, string password);
|
||||||
|
AuthenticationResult RefreshToken(string token, string refreshToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class IdentityService : IIdentityService
|
public class IdentityService : IIdentityService
|
||||||
{
|
{
|
||||||
private readonly UserManager<IdentityUser> _userManager;
|
private readonly UserManager<IdentityUser> _userManager;
|
||||||
private readonly JwtSettings _jwtSettings;
|
private readonly JwtSettings _jwtSettings;
|
||||||
|
private readonly TokenValidationParameters _tokenValidationParameters;
|
||||||
|
private readonly IRefreshTokenRepository _refreshTokenRepository;
|
||||||
|
|
||||||
public IdentityService(UserManager<IdentityUser> userManager, JwtSettings jwtSettings)
|
public IdentityService(UserManager<IdentityUser> userManager, JwtSettings jwtSettings, TokenValidationParameters tokenValidationParameters, IRefreshTokenRepository refreshTokenRepository)
|
||||||
{
|
{
|
||||||
_userManager = userManager;
|
_userManager = userManager;
|
||||||
_jwtSettings = jwtSettings;
|
_jwtSettings = jwtSettings;
|
||||||
|
_tokenValidationParameters = tokenValidationParameters;
|
||||||
|
_refreshTokenRepository = refreshTokenRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AuthenticationResult Register(string email, string password)
|
public AuthenticationResult Register(string email, string password)
|
||||||
@ -85,6 +93,116 @@ public class IdentityService : IIdentityService
|
|||||||
return GenerateJwtToken(user.Result ?? new IdentityUser());
|
return GenerateJwtToken(user.Result ?? new IdentityUser());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public AuthenticationResult RefreshToken(string token, string refreshToken)
|
||||||
|
{
|
||||||
|
var validatedToken = CheckTokenSigner(token);
|
||||||
|
if (validatedToken is null)
|
||||||
|
{
|
||||||
|
return new AuthenticationResult
|
||||||
|
{
|
||||||
|
ErrorMessage = new List<string> { "Invalid Token" }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the expire datetime of the token
|
||||||
|
var expiryDateUnix = long.Parse(validatedToken.Claims.Single(x => x.Type == JwtRegisteredClaimNames.Exp).Value);
|
||||||
|
|
||||||
|
// generate the unix epoc, add expiry time
|
||||||
|
var expiryDateTimeUtc = new DateTime(1970, 0, 0, 0, 0, 0, DateTimeKind.Utc)
|
||||||
|
.AddSeconds(expiryDateUnix);
|
||||||
|
|
||||||
|
// if it expires in the future
|
||||||
|
if (expiryDateTimeUtc > DateTime.Now)
|
||||||
|
{
|
||||||
|
return new AuthenticationResult
|
||||||
|
{
|
||||||
|
ErrorMessage = new List<string> { "The token has not expired yet" }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var jti = validatedToken.Claims.Single(x => x.Type == JwtRegisteredClaimNames.Jti).Value;
|
||||||
|
|
||||||
|
var storedToken = _refreshTokenRepository.Get(token);
|
||||||
|
if (storedToken is null)
|
||||||
|
{
|
||||||
|
return new AuthenticationResult
|
||||||
|
{
|
||||||
|
ErrorMessage = new List<string> { "The refresh token does not exist" }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DateTime.UtcNow > storedToken.ExpiryDate)
|
||||||
|
{
|
||||||
|
return new AuthenticationResult
|
||||||
|
{
|
||||||
|
ErrorMessage = new List<string> { "The refresh token has expired" }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (storedToken.Invalidated)
|
||||||
|
{
|
||||||
|
return new AuthenticationResult
|
||||||
|
{
|
||||||
|
ErrorMessage = new List<string> { "The token is not valid" }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (storedToken.Used)
|
||||||
|
{
|
||||||
|
return new AuthenticationResult
|
||||||
|
{
|
||||||
|
ErrorMessage = new List<string> { "The token has been used" }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (storedToken.JwtId != jti)
|
||||||
|
{
|
||||||
|
return new AuthenticationResult
|
||||||
|
{
|
||||||
|
ErrorMessage = new List<string> { "The token does not match this JWT" }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
_refreshTokenRepository.UpdateTokenIsUsed(token);
|
||||||
|
|
||||||
|
var user = _userManager.FindByIdAsync(validatedToken.Claims.Single(x => x.Type == "id").Value);
|
||||||
|
user.Wait();
|
||||||
|
if (user.Result is null)
|
||||||
|
{
|
||||||
|
return new AuthenticationResult
|
||||||
|
{
|
||||||
|
ErrorMessage = new List<string> { "Unable to find user" }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return GenerateJwtToken(user.Result);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ClaimsPrincipal? CheckTokenSigner(string token)
|
||||||
|
{
|
||||||
|
var tokenHandler = new JwtSecurityTokenHandler();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var principal = tokenHandler.ValidateToken(token, _tokenValidationParameters, out var validatedToken);
|
||||||
|
if (IsSecurityTokenValidSecurity(validatedToken))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return principal;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsSecurityTokenValidSecurity(SecurityToken token)
|
||||||
|
{
|
||||||
|
return (token is JwtSecurityToken jwtSecurityToken) && jwtSecurityToken.Header.Alg.Equals(SecurityAlgorithms.HmacSha512, StringComparison.InvariantCultureIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
private AuthenticationResult GenerateJwtToken(IdentityUser user)
|
private AuthenticationResult GenerateJwtToken(IdentityUser user)
|
||||||
{
|
{
|
||||||
var tokenHandler = new JwtSecurityTokenHandler();
|
var tokenHandler = new JwtSecurityTokenHandler();
|
||||||
@ -105,10 +223,21 @@ public class IdentityService : IIdentityService
|
|||||||
|
|
||||||
var token = tokenHandler.CreateToken(tokenDescriptor);
|
var token = tokenHandler.CreateToken(tokenDescriptor);
|
||||||
|
|
||||||
|
var refreshToken = new RefreshTokenEntity
|
||||||
|
{
|
||||||
|
JwtId = token.Id,
|
||||||
|
UserId = user.Id,
|
||||||
|
CreatedDate = DateTime.UtcNow,
|
||||||
|
ExpiryDate = DateTime.UtcNow.AddMonths(6)
|
||||||
|
};
|
||||||
|
|
||||||
|
_refreshTokenRepository.Add(refreshToken);
|
||||||
|
|
||||||
return new AuthenticationResult
|
return new AuthenticationResult
|
||||||
{
|
{
|
||||||
IsSuccessful = true,
|
IsSuccessful = true,
|
||||||
Token = tokenHandler.WriteToken(token)
|
Token = tokenHandler.WriteToken(token),
|
||||||
|
RefreshToken = refreshToken.Token
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -10,7 +10,6 @@ namespace Newsbot.Collector.Database;
|
|||||||
public class DatabaseContext : IdentityDbContext
|
public class DatabaseContext : IdentityDbContext
|
||||||
{
|
{
|
||||||
public DbSet<ArticlesEntity> Articles { get; set; } = null!;
|
public DbSet<ArticlesEntity> Articles { get; set; } = null!;
|
||||||
|
|
||||||
public DbSet<DiscordNotificationEntity> DiscordNotification { get; set; } = null!;
|
public DbSet<DiscordNotificationEntity> DiscordNotification { get; set; } = null!;
|
||||||
public DbSet<DiscordQueueEntity> DiscordQueue { get; set; } = null!;
|
public DbSet<DiscordQueueEntity> DiscordQueue { get; set; } = null!;
|
||||||
public DbSet<DiscordWebhookEntity> DiscordWebhooks { get; set; } = null!;
|
public DbSet<DiscordWebhookEntity> DiscordWebhooks { get; set; } = null!;
|
||||||
@ -18,6 +17,8 @@ public class DatabaseContext : IdentityDbContext
|
|||||||
public DbSet<SourceEntity> Sources { get; set; } = null!;
|
public DbSet<SourceEntity> Sources { get; set; } = null!;
|
||||||
|
|
||||||
public DbSet<UserSourceSubscriptionEntity> UserSourceSubscription { get; set; } = null!;
|
public DbSet<UserSourceSubscriptionEntity> UserSourceSubscription { get; set; } = null!;
|
||||||
|
|
||||||
|
public DbSet<RefreshTokenEntity> RefreshTokens { get; set; }
|
||||||
|
|
||||||
private string ConnectionString { get; set; } = "";
|
private string ConnectionString { get; set; } = "";
|
||||||
|
|
||||||
|
543
Newsbot.Collector.Database/Migrations/20230711033558_add_jwt_token_refresh_table.Designer.cs
generated
Normal file
543
Newsbot.Collector.Database/Migrations/20230711033558_add_jwt_token_refresh_table.Designer.cs
generated
Normal file
@ -0,0 +1,543 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using Newsbot.Collector.Database;
|
||||||
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Newsbot.Collector.Database.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(DatabaseContext))]
|
||||||
|
[Migration("20230711033558_add_jwt_token_refresh_table")]
|
||||||
|
partial class add_jwt_token_refresh_table
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "7.0.8")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||||
|
|
||||||
|
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("ConcurrencyStamp")
|
||||||
|
.IsConcurrencyToken()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("character varying(256)");
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedName")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("character varying(256)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedName")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("RoleNameIndex");
|
||||||
|
|
||||||
|
b.ToTable("AspNetRoles", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("ClaimType")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("ClaimValue")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("RoleId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("RoleId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetRoleClaims", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<int>("AccessFailedCount")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<string>("ConcurrencyStamp")
|
||||||
|
.IsConcurrencyToken()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("character varying(256)");
|
||||||
|
|
||||||
|
b.Property<bool>("EmailConfirmed")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<bool>("LockoutEnabled")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset?>("LockoutEnd")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedEmail")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("character varying(256)");
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedUserName")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("character varying(256)");
|
||||||
|
|
||||||
|
b.Property<string>("PasswordHash")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("PhoneNumber")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<bool>("PhoneNumberConfirmed")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<string>("SecurityStamp")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<bool>("TwoFactorEnabled")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<string>("UserName")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("character varying(256)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedEmail")
|
||||||
|
.HasDatabaseName("EmailIndex");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedUserName")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("UserNameIndex");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUsers", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("ClaimType")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("ClaimValue")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserClaims", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("LoginProvider")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderKey")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderDisplayName")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.HasKey("LoginProvider", "ProviderKey");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserLogins", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("RoleId")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.HasKey("UserId", "RoleId");
|
||||||
|
|
||||||
|
b.HasIndex("RoleId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserRoles", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("LoginProvider")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Value")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.HasKey("UserId", "LoginProvider", "Name");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserTokens", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Newsbot.Collector.Domain.Entities.ArticlesEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<string>("AuthorImage")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("AuthorName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<bool>("CodeIsCommit")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<bool>("CodeIsRelease")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<DateTime>("PubDate")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<Guid>("SourceId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<string>("Tags")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Thumbnail")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Title")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Url")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Video")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<int>("VideoHeight")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<int>("VideoWidth")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Articles");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Newsbot.Collector.Domain.Entities.DiscordNotificationEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<bool>("CodeAllowCommits")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<bool>("CodeAllowReleases")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<Guid>("DiscordWebHookId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<Guid>("SourceId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("DiscordNotification");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Newsbot.Collector.Domain.Entities.DiscordQueueEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<Guid>("ArticleId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("DiscordQueue");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Newsbot.Collector.Domain.Entities.DiscordWebhookEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<string>("Channel")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<bool>("Enabled")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<string>("Server")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Url")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("DiscordWebhooks");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Newsbot.Collector.Domain.Entities.IconEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<string>("FileName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Site")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<Guid>("SourceId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Icons");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Newsbot.Collector.Domain.Entities.RefreshTokenEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Token")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedDate")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<DateTime>("ExpiryDate")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<bool>("Invalidated")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<string>("JwtId")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<bool>("Used")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.HasKey("Token");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("RefreshTokens");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Newsbot.Collector.Domain.Entities.SourceEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<bool>("Deleted")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<bool>("Enabled")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Site")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Source")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Tags")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Type")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Url")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Value")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("YoutubeId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Sources");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Newsbot.Collector.Domain.Entities.UserSourceSubscriptionEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("DateAdded")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<Guid>("SourceId")
|
||||||
|
.HasColumnType("uuid");
|
||||||
|
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("UserSourceSubscription");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("RoleId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("RoleId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Newsbot.Collector.Domain.Entities.RefreshTokenEntity", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", "User")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId");
|
||||||
|
|
||||||
|
b.Navigation("User");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Newsbot.Collector.Domain.Entities.UserSourceSubscriptionEntity", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", "User")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId");
|
||||||
|
|
||||||
|
b.Navigation("User");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,49 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Newsbot.Collector.Database.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class add_jwt_token_refresh_table : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "RefreshTokens",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Token = table.Column<string>(type: "text", nullable: false),
|
||||||
|
JwtId = table.Column<string>(type: "text", nullable: true),
|
||||||
|
CreatedDate = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
||||||
|
ExpiryDate = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
||||||
|
Used = table.Column<bool>(type: "boolean", nullable: false),
|
||||||
|
Invalidated = table.Column<bool>(type: "boolean", nullable: false),
|
||||||
|
UserId = table.Column<string>(type: "text", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_RefreshTokens", x => x.Token);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_RefreshTokens_AspNetUsers_UserId",
|
||||||
|
column: x => x.UserId,
|
||||||
|
principalTable: "AspNetUsers",
|
||||||
|
principalColumn: "Id");
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_RefreshTokens_UserId",
|
||||||
|
table: "RefreshTokens",
|
||||||
|
column: "UserId");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "RefreshTokens");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -365,6 +365,36 @@ namespace Newsbot.Collector.Database.Migrations
|
|||||||
b.ToTable("Icons");
|
b.ToTable("Icons");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Newsbot.Collector.Domain.Entities.RefreshTokenEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Token")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedDate")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<DateTime>("ExpiryDate")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<bool>("Invalidated")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<string>("JwtId")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<bool>("Used")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.HasKey("Token");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("RefreshTokens");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Newsbot.Collector.Domain.Entities.SourceEntity", b =>
|
modelBuilder.Entity("Newsbot.Collector.Domain.Entities.SourceEntity", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("Id")
|
b.Property<Guid>("Id")
|
||||||
@ -487,6 +517,15 @@ namespace Newsbot.Collector.Database.Migrations
|
|||||||
.IsRequired();
|
.IsRequired();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Newsbot.Collector.Domain.Entities.RefreshTokenEntity", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", "User")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId");
|
||||||
|
|
||||||
|
b.Navigation("User");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Newsbot.Collector.Domain.Entities.UserSourceSubscriptionEntity", b =>
|
modelBuilder.Entity("Newsbot.Collector.Domain.Entities.UserSourceSubscriptionEntity", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", "User")
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", "User")
|
||||||
|
42
Newsbot.Collector.Database/Repositories/RefreshTokenTable.cs
Normal file
42
Newsbot.Collector.Database/Repositories/RefreshTokenTable.cs
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
using Newsbot.Collector.Domain.Entities;
|
||||||
|
using Newsbot.Collector.Domain.Interfaces;
|
||||||
|
|
||||||
|
namespace Newsbot.Collector.Database.Repositories;
|
||||||
|
|
||||||
|
public class RefreshTokenTable : IRefreshTokenRepository
|
||||||
|
{
|
||||||
|
private readonly DatabaseContext _context;
|
||||||
|
|
||||||
|
public RefreshTokenTable(string connectionString)
|
||||||
|
{
|
||||||
|
_context = new DatabaseContext(connectionString);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RefreshTokenTable(DatabaseContext context)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Add(RefreshTokenEntity entity)
|
||||||
|
{
|
||||||
|
_context.RefreshTokens.Add(entity);
|
||||||
|
_context.SaveChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
public RefreshTokenEntity? Get(string token)
|
||||||
|
{
|
||||||
|
return _context.RefreshTokens.SingleOrDefault(x => x.Token == token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateTokenIsUsed(string token)
|
||||||
|
{
|
||||||
|
var entity = Get(token);
|
||||||
|
if (entity is null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
entity.Used = true;
|
||||||
|
_context.RefreshTokens.Update(entity);
|
||||||
|
_context.SaveChanges();
|
||||||
|
}
|
||||||
|
}
|
21
Newsbot.Collector.Domain/Entities/RefreshTokenEntity.cs
Normal file
21
Newsbot.Collector.Domain/Entities/RefreshTokenEntity.cs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
|
||||||
|
namespace Newsbot.Collector.Domain.Entities;
|
||||||
|
|
||||||
|
public class RefreshTokenEntity
|
||||||
|
{
|
||||||
|
|
||||||
|
[Key]
|
||||||
|
public string? Token { get; set; }
|
||||||
|
public string? JwtId { get; set; }
|
||||||
|
public DateTime CreatedDate { get; set; }
|
||||||
|
public DateTime ExpiryDate { get; set; }
|
||||||
|
public bool Used { get; set; }
|
||||||
|
public bool Invalidated { get; set; }
|
||||||
|
|
||||||
|
public string? UserId { get; set; }
|
||||||
|
[ForeignKey(nameof(UserId))]
|
||||||
|
public IdentityUser? User { get; set; }
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
using Newsbot.Collector.Domain.Entities;
|
||||||
|
|
||||||
|
namespace Newsbot.Collector.Domain.Interfaces;
|
||||||
|
|
||||||
|
public interface IRefreshTokenRepository
|
||||||
|
{
|
||||||
|
void Add(RefreshTokenEntity entity);
|
||||||
|
public RefreshTokenEntity? Get(string token);
|
||||||
|
void UpdateTokenIsUsed(string token);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user