Kaynağa Gözat

Added refresh token

pull/3/head
Ermin Bronja 3 yıl önce
ebeveyn
işleme
5d9d6ab2fc

+ 6
- 0
Diligent.WebAPI.Business/Services/Interfaces/IUserService.cs Dosyayı Görüntüle

@@ -4,10 +4,16 @@
{
Task<AuthenticateResponseDto?> Authenticate(AuthenticateRequestDto model);

Task<RefreshTokenResultDto> RefreshTokenAsync(RefreshTokenRequestDto model);

Task<IEnumerable<User?>> GetAll();

Task<User?> GetById(int id);

Task CreateUser(CreateUserRequestDto model);

Task<RefreshToken?> GetRefreshTokenByUserId(int userId);

Task UpdateRefreshToken(RefreshToken refreshToken);
}
}

+ 165
- 7
Diligent.WebAPI.Business/Services/UserService.cs Dosyayı Görüntüle

@@ -7,12 +7,14 @@ namespace Diligent.WebAPI.Business.Services
private readonly AuthorizationSettings _authSettings;
private readonly UserManager<User> _userManager;
private readonly IMapper _mapper;
private readonly DatabaseContext _databaseContext;

public UserService(IOptions<AuthorizationSettings> authSettings, UserManager<User> userManager, IMapper mapper)
public UserService(IOptions<AuthorizationSettings> authSettings, UserManager<User> userManager, IMapper mapper, DatabaseContext databaseContext)
{
_authSettings = authSettings.Value;
_userManager = userManager;
_mapper = mapper;
_databaseContext = databaseContext;
}

public async Task<IEnumerable<User?>> GetAll() =>
@@ -42,7 +44,7 @@ namespace Diligent.WebAPI.Business.Services
return null;

// authentication successful so generate jwt token
var token = GenerateJwtToken(user);
var token = await GenerateJwtToken(user, true);

return new AuthenticateResponseDto
{
@@ -50,23 +52,179 @@ namespace Diligent.WebAPI.Business.Services
Username = user.UserName,
FirstName = user.FirstName,
LastName = user.LastName,
Token = token
Token = token,
RefreshToken = token
};
}

private string GenerateJwtToken(User user)
private async Task<string> GenerateJwtToken(User user, bool authenticate = false)
{
// generate token that is valid for 7 days
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes(_authSettings.Secret);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new[] { new Claim("id", user.Id.ToString()) }),
Expires = DateTime.UtcNow.AddMinutes(2),
Subject = new ClaimsIdentity(new[] {
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
new Claim("id", user.Id.ToString())
}),
Expires = DateTime.UtcNow.AddMinutes(_authSettings.JwtExpiredTime),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
};
var token = tokenHandler.CreateToken(tokenDescriptor);
return tokenHandler.WriteToken(token);

var writedToken = tokenHandler.WriteToken(token);

var refreshToken = new RefreshToken
{
Token = writedToken,
JwtId = token.Id,
UserId = user.Id,
User = user,
CreationDate = DateTime.UtcNow,
ExpiryDate = DateTime.UtcNow.AddMinutes(_authSettings.JwtRefreshExpiredTime)
};

var existRefreshToken = await _databaseContext.RefreshTokens.Where(x => x.Id == user.Id).FirstOrDefaultAsync();

if(existRefreshToken != null)
{
existRefreshToken.Token = writedToken;
existRefreshToken.JwtId = token.Id;
existRefreshToken.CreationDate = DateTime.UtcNow;
existRefreshToken.ExpiryDate = DateTime.UtcNow.AddMinutes(_authSettings.JwtRefreshExpiredTime);

if (authenticate)
{
existRefreshToken.Used = false;
existRefreshToken.Invalidated = false;
}

//_databaseContext.RefreshTokens.Update(existRefreshToken);
await UpdateRefreshToken(existRefreshToken);
}
else
{
await _databaseContext.RefreshTokens.AddAsync(refreshToken);
}

await _databaseContext.SaveChangesAsync();

return writedToken;
}

public async Task<RefreshTokenResultDto> RefreshTokenAsync(RefreshTokenRequestDto model)
{
var validatedToken = GetPrincipalFromToken(model.Token);

if (validatedToken == null)
{
return new RefreshTokenResultDto { Error = "Invalid token" };
}

var expiryDateUnix = long.Parse(validatedToken.Claims.Single(x => x.Type == JwtRegisteredClaimNames.Exp).Value);

var expiryDateTimeUtc = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)
.AddSeconds(expiryDateUnix);

if (expiryDateTimeUtc > DateTime.UtcNow)
{
return new RefreshTokenResultDto { Error = "This token hasn't expired yet" };
}

var jti = validatedToken.Claims.Single(x => x.Type == JwtRegisteredClaimNames.Jti).Value;

var storedRefreshToken = await _databaseContext.RefreshTokens.SingleOrDefaultAsync(x => x.Token == model.RefreshToken);

if (storedRefreshToken == null)
{
return new RefreshTokenResultDto { Error = "This refresh token does not exist" };
}

if (DateTime.UtcNow > storedRefreshToken.ExpiryDate)
{
return new RefreshTokenResultDto { Error = "This refresh token has expired" };
}

if (storedRefreshToken.Invalidated)
{
return new RefreshTokenResultDto { Error = "This refresh token has been invalidated" };
}

if (storedRefreshToken.Used)
{
return new RefreshTokenResultDto { Error = "This refresh token has been used" };
}

if (storedRefreshToken.JwtId != jti)
{
return new RefreshTokenResultDto { Error = "This refresh token does not match this JWT" };
}

storedRefreshToken.Used = true;
_databaseContext.RefreshTokens.Update(storedRefreshToken);
await _databaseContext.SaveChangesAsync();

var user = await _userManager.FindByIdAsync(validatedToken.Claims.Single(x => x.Type == "id").Value);

var token = await GenerateJwtToken(user);

return new RefreshTokenResultDto
{
Token = token
};
}

private ClaimsPrincipal GetPrincipalFromToken(string token)
{
var tokenHandler = new JwtSecurityTokenHandler();

var key = Encoding.ASCII.GetBytes(_authSettings.Secret);
var tokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false,
ValidateAudience = false,
RequireExpirationTime = false,
ValidateLifetime = true,
// set clockskew to zero so tokens expire exactly at token expiration time (instead of 5 minutes later)
//ClockSkew = TimeSpan.Zero
};

try
{
var principal = tokenHandler.ValidateToken(token, tokenValidationParameters, out var validatedToken);
if (!IsJwtWithValidSecurityAlgorithm(validatedToken))
{
return null;
}

return principal;
}
catch (Exception ex)
{
return null;
}
}

private bool IsJwtWithValidSecurityAlgorithm(SecurityToken validatedToken)
{
return (validatedToken is JwtSecurityToken jwtSecurityToken) &&
jwtSecurityToken.Header.Alg.Equals(SecurityAlgorithms.HmacSha256,
StringComparison.InvariantCultureIgnoreCase);
}

public async Task<RefreshToken?> GetRefreshTokenByUserId(int userId)
{
return await _databaseContext.RefreshTokens.Where(x => x.UserId == userId).FirstOrDefaultAsync();
}

public async Task UpdateRefreshToken(RefreshToken refreshToken)
{
_databaseContext.RefreshTokens.Update(refreshToken);

await _databaseContext.SaveChangesAsync();
}
}
}

+ 4
- 0
Diligent.WebAPI.Business/Settings/AuthorizationSettings.cs Dosyayı Görüntüle

@@ -3,5 +3,9 @@
public class AuthorizationSettings
{
public string Secret { get; set; }

public int JwtExpiredTime { get; set; }

public int JwtRefreshExpiredTime { get; set; }
}
}

+ 13
- 0
Diligent.WebAPI.Contracts/DTOs/Auth/AuthFailedResponse.cs Dosyayı Görüntüle

@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Diligent.WebAPI.Contracts.DTOs.Auth
{
public class AuthFailedResponse
{
public string Error { get; set; }
}
}

+ 1
- 0
Diligent.WebAPI.Contracts/DTOs/Auth/AuthenticateResponseDto.cs Dosyayı Görüntüle

@@ -7,5 +7,6 @@
public string LastName { get; set; }
public string Username { get; set; }
public string Token { get; set; }
public string RefreshToken { get; set; }
}
}

+ 15
- 0
Diligent.WebAPI.Contracts/DTOs/Auth/RefreshTokenRequestDto.cs Dosyayı Görüntüle

@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Diligent.WebAPI.Contracts.DTOs.Auth
{
public class RefreshTokenRequestDto
{
public string Token { get; set; }

public string RefreshToken { get; set; }
}
}

+ 22
- 0
Diligent.WebAPI.Contracts/DTOs/Auth/RefreshTokenResponseDto.cs Dosyayı Görüntüle

@@ -0,0 +1,22 @@

namespace Diligent.WebAPI.Contracts.DTOs.Auth
{
public class RefreshTokenResponseDto
{
public int Id { get; set; }

public string Token { get; set; }

public string JwtId { get; set; }

public DateTime CreationDate { get; set; }

public DateTime ExpiryDate { get; set; }

public bool Used { get; set; }

public bool Invalidated { get; set; }

public int UserId { get; set; }
}
}

+ 15
- 0
Diligent.WebAPI.Contracts/DTOs/Auth/RefreshTokenResultDto.cs Dosyayı Görüntüle

@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Diligent.WebAPI.Contracts.DTOs.Auth
{
public class RefreshTokenResultDto
{
public string? Token { get; set; }

public string? Error { get; set; }
}
}

+ 10
- 0
Diligent.WebAPI.Contracts/DTOs/Auth/RefreshedTokenResponseDto.cs Dosyayı Görüntüle

@@ -0,0 +1,10 @@

namespace Diligent.WebAPI.Contracts.DTOs.Auth
{
public class RefreshedTokenResponseDto
{
public string Token { get; set; }

public string RefreshToken { get; set; }
}
}

+ 1
- 0
Diligent.WebAPI.Data/DatabaseContext.cs Dosyayı Görüntüle

@@ -7,6 +7,7 @@ public class DatabaseContext : IdentityDbContext<User, AppRole, int>
public DbSet<InsurancePolicy> InsurancePolicies { get; set; }
public DbSet<WebhookSubscription> WebhookSubscriptions { get; set; }
public DbSet<WebhookDefinition> WebhookDefinitions { get; set; }
public DbSet<RefreshToken> RefreshTokens { get; set; }

public DatabaseContext(DbContextOptions<DatabaseContext> options) : base(options) { }


+ 32
- 0
Diligent.WebAPI.Data/Entities/RefreshToken.cs Dosyayı Görüntüle

@@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Diligent.WebAPI.Data.Entities
{
public class RefreshToken
{
[Key]
public int Id { get; set; }

public string Token { get; set; }

public string JwtId { get; set; }

public DateTime CreationDate { get; set; }

public DateTime ExpiryDate { get; set; }

public bool Used { get; set; }

public bool Invalidated { get; set; }

public int UserId { get; set; }

[ForeignKey(nameof(UserId))]
public User User { get; set; }
}
}

+ 597
- 0
Diligent.WebAPI.Data/Migrations/20221024112939_AddedRefreshToken.Designer.cs Dosyayı Görüntüle

@@ -0,0 +1,597 @@
// <auto-generated />
using System;
using Diligent.WebAPI.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;

#nullable disable

namespace Diligent.WebAPI.Data.Migrations
{
[DbContext(typeof(DatabaseContext))]
[Migration("20221024112939_AddedRefreshToken")]
partial class AddedRefreshToken
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "6.0.10")
.HasAnnotation("Relational:MaxIdentifierLength", 128);

SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder, 1L, 1);

modelBuilder.Entity("Diligent.WebAPI.Data.Entities.AppRole", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");

SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"), 1L, 1);

b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("nvarchar(max)");

b.Property<string>("Name")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");

b.Property<string>("NormalizedName")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");

b.HasKey("Id");

b.HasIndex("NormalizedName")
.IsUnique()
.HasDatabaseName("RoleNameIndex")
.HasFilter("[NormalizedName] IS NOT NULL");

b.ToTable("AspNetRoles", (string)null);
});

modelBuilder.Entity("Diligent.WebAPI.Data.Entities.InsuranceCompany", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");

SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"), 1L, 1);

b.Property<string>("City")
.IsRequired()
.HasColumnType("nvarchar(max)");

b.Property<string>("Country")
.IsRequired()
.HasColumnType("nvarchar(max)");

b.Property<DateTime>("CreatedAtUtc")
.HasColumnType("datetime2");

b.Property<DateTime?>("DeletedAtUtc")
.HasColumnType("datetime2");

b.Property<string>("Fax")
.IsRequired()
.HasColumnType("nvarchar(max)");

b.Property<string>("LegalAddress")
.IsRequired()
.HasColumnType("nvarchar(max)");

b.Property<string>("LegalEmail")
.IsRequired()
.HasColumnType("nvarchar(max)");

b.Property<string>("Name")
.IsRequired()
.HasColumnType("nvarchar(max)");

b.Property<string>("PhoneNumber")
.IsRequired()
.HasColumnType("nvarchar(max)");

b.Property<string>("PostalCode")
.IsRequired()
.HasColumnType("nvarchar(max)");

b.Property<DateTime?>("UpdatedAtUtc")
.HasColumnType("datetime2");

b.HasKey("Id");

b.ToTable("InsuranceCompanies");
});

modelBuilder.Entity("Diligent.WebAPI.Data.Entities.InsurancePolicy", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");

SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"), 1L, 1);

b.Property<DateTime>("CreatedAtUtc")
.HasColumnType("datetime2");

b.Property<DateTime?>("DeletedAtUtc")
.HasColumnType("datetime2");

b.Property<DateTime>("EndDate")
.HasColumnType("datetime2");

b.Property<long>("InsurerId")
.HasColumnType("bigint");

b.Property<decimal>("Premium")
.HasColumnType("decimal(18,2)");

b.Property<DateTime>("StartDate")
.HasColumnType("datetime2");

b.Property<string>("Type")
.IsRequired()
.HasColumnType("nvarchar(max)");

b.Property<DateTime?>("UpdatedAtUtc")
.HasColumnType("datetime2");

b.HasKey("Id");

b.HasIndex("InsurerId");

b.ToTable("InsurancePolicies");
});

modelBuilder.Entity("Diligent.WebAPI.Data.Entities.Insurer", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");

SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"), 1L, 1);

b.Property<string>("Address")
.IsRequired()
.HasColumnType("nvarchar(max)");

b.Property<string>("City")
.IsRequired()
.HasColumnType("nvarchar(max)");

b.Property<string>("Country")
.IsRequired()
.HasColumnType("nvarchar(max)");

b.Property<DateTime>("CreatedAtUtc")
.HasColumnType("datetime2");

b.Property<DateTime>("DateOfBirth")
.HasColumnType("datetime2");

b.Property<DateTime?>("DeletedAtUtc")
.HasColumnType("datetime2");

b.Property<string>("Email")
.IsRequired()
.HasColumnType("nvarchar(max)");

b.Property<string>("FirstName")
.IsRequired()
.HasColumnType("nvarchar(max)");

b.Property<long>("InsuranceCompanyId")
.HasColumnType("bigint");

b.Property<string>("LastName")
.IsRequired()
.HasColumnType("nvarchar(max)");

b.Property<string>("PhoneNumber")
.IsRequired()
.HasColumnType("nvarchar(max)");

b.Property<string>("PostalCode")
.IsRequired()
.HasColumnType("nvarchar(max)");

b.Property<DateTime?>("UpdatedAtUtc")
.HasColumnType("datetime2");

b.HasKey("Id");

b.HasIndex("InsuranceCompanyId");

b.ToTable("Insurers");
});

modelBuilder.Entity("Diligent.WebAPI.Data.Entities.RefreshToken", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");

SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"), 1L, 1);

b.Property<DateTime>("CreationDate")
.HasColumnType("datetime2");

b.Property<DateTime>("ExpiryDate")
.HasColumnType("datetime2");

b.Property<bool>("Invalidated")
.HasColumnType("bit");

b.Property<string>("JwtId")
.IsRequired()
.HasColumnType("nvarchar(max)");

b.Property<string>("Token")
.IsRequired()
.HasColumnType("nvarchar(max)");

b.Property<bool>("Used")
.HasColumnType("bit");

b.Property<int>("UserId")
.HasColumnType("int");

b.HasKey("Id");

b.HasIndex("UserId");

b.ToTable("RefreshTokens");
});

modelBuilder.Entity("Diligent.WebAPI.Data.Entities.User", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");

SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"), 1L, 1);

b.Property<int>("AccessFailedCount")
.HasColumnType("int");

b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("nvarchar(max)");

b.Property<string>("Email")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");

b.Property<bool>("EmailConfirmed")
.HasColumnType("bit");

b.Property<string>("FirstName")
.IsRequired()
.HasColumnType("nvarchar(max)");

b.Property<string>("LastName")
.IsRequired()
.HasColumnType("nvarchar(max)");

b.Property<bool>("LockoutEnabled")
.HasColumnType("bit");

b.Property<DateTimeOffset?>("LockoutEnd")
.HasColumnType("datetimeoffset");

b.Property<string>("NormalizedEmail")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");

b.Property<string>("NormalizedUserName")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");

b.Property<string>("PasswordHash")
.HasColumnType("nvarchar(max)");

b.Property<string>("PhoneNumber")
.HasColumnType("nvarchar(max)");

b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("bit");

b.Property<string>("SecurityStamp")
.HasColumnType("nvarchar(max)");

b.Property<bool>("TwoFactorEnabled")
.HasColumnType("bit");

b.Property<string>("UserName")
.HasMaxLength(256)
.HasColumnType("nvarchar(256)");

b.HasKey("Id");

b.HasIndex("NormalizedEmail")
.HasDatabaseName("EmailIndex");

b.HasIndex("NormalizedUserName")
.IsUnique()
.HasDatabaseName("UserNameIndex")
.HasFilter("[NormalizedUserName] IS NOT NULL");

b.ToTable("AspNetUsers", (string)null);
});

modelBuilder.Entity("Diligent.WebAPI.Data.Entities.WebhookDefinition", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");

SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"), 1L, 1);

b.Property<DateTime>("CreatedAtUtc")
.HasColumnType("datetime2");

b.Property<DateTime?>("DeletedAtUtc")
.HasColumnType("datetime2");

b.Property<string>("Description")
.IsRequired()
.HasColumnType("nvarchar(max)");

b.Property<string>("DisplayName")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");

b.Property<string>("Name")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");

b.Property<DateTime?>("UpdatedAtUtc")
.HasColumnType("datetime2");

b.HasKey("Id");

b.ToTable("WebhookDefinitions");
});

modelBuilder.Entity("Diligent.WebAPI.Data.Entities.WebhookSubscription", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");

SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"), 1L, 1);

b.Property<DateTime>("CreatedAtUtc")
.HasColumnType("datetime2");

b.Property<DateTime?>("DeletedAtUtc")
.HasColumnType("datetime2");

b.Property<bool>("IsActive")
.HasColumnType("bit");

b.Property<DateTime?>("UpdatedAtUtc")
.HasColumnType("datetime2");

b.Property<long>("WebhookDefinitionId")
.HasColumnType("bigint");

b.Property<string>("WebhookURL")
.IsRequired()
.HasColumnType("nvarchar(max)");

b.HasKey("Id");

b.HasIndex("WebhookDefinitionId");

b.ToTable("WebhookSubscriptions");
});

modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<int>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");

SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"), 1L, 1);

b.Property<string>("ClaimType")
.HasColumnType("nvarchar(max)");

b.Property<string>("ClaimValue")
.HasColumnType("nvarchar(max)");

b.Property<int>("RoleId")
.HasColumnType("int");

b.HasKey("Id");

b.HasIndex("RoleId");

b.ToTable("AspNetRoleClaims", (string)null);
});

modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<int>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");

SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"), 1L, 1);

b.Property<string>("ClaimType")
.HasColumnType("nvarchar(max)");

b.Property<string>("ClaimValue")
.HasColumnType("nvarchar(max)");

b.Property<int>("UserId")
.HasColumnType("int");

b.HasKey("Id");

b.HasIndex("UserId");

b.ToTable("AspNetUserClaims", (string)null);
});

modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<int>", b =>
{
b.Property<string>("LoginProvider")
.HasColumnType("nvarchar(450)");

b.Property<string>("ProviderKey")
.HasColumnType("nvarchar(450)");

b.Property<string>("ProviderDisplayName")
.HasColumnType("nvarchar(max)");

b.Property<int>("UserId")
.HasColumnType("int");

b.HasKey("LoginProvider", "ProviderKey");

b.HasIndex("UserId");

b.ToTable("AspNetUserLogins", (string)null);
});

modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<int>", b =>
{
b.Property<int>("UserId")
.HasColumnType("int");

b.Property<int>("RoleId")
.HasColumnType("int");

b.HasKey("UserId", "RoleId");

b.HasIndex("RoleId");

b.ToTable("AspNetUserRoles", (string)null);
});

modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<int>", b =>
{
b.Property<int>("UserId")
.HasColumnType("int");

b.Property<string>("LoginProvider")
.HasColumnType("nvarchar(450)");

b.Property<string>("Name")
.HasColumnType("nvarchar(450)");

b.Property<string>("Value")
.HasColumnType("nvarchar(max)");

b.HasKey("UserId", "LoginProvider", "Name");

b.ToTable("AspNetUserTokens", (string)null);
});

modelBuilder.Entity("Diligent.WebAPI.Data.Entities.InsurancePolicy", b =>
{
b.HasOne("Diligent.WebAPI.Data.Entities.Insurer", "Insurer")
.WithMany()
.HasForeignKey("InsurerId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();

b.Navigation("Insurer");
});

modelBuilder.Entity("Diligent.WebAPI.Data.Entities.Insurer", b =>
{
b.HasOne("Diligent.WebAPI.Data.Entities.InsuranceCompany", "InsuranceCompany")
.WithMany()
.HasForeignKey("InsuranceCompanyId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();

b.Navigation("InsuranceCompany");
});

modelBuilder.Entity("Diligent.WebAPI.Data.Entities.RefreshToken", b =>
{
b.HasOne("Diligent.WebAPI.Data.Entities.User", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();

b.Navigation("User");
});

modelBuilder.Entity("Diligent.WebAPI.Data.Entities.WebhookSubscription", b =>
{
b.HasOne("Diligent.WebAPI.Data.Entities.WebhookDefinition", "WebhookDefinition")
.WithMany()
.HasForeignKey("WebhookDefinitionId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();

b.Navigation("WebhookDefinition");
});

modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<int>", b =>
{
b.HasOne("Diligent.WebAPI.Data.Entities.AppRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});

modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<int>", b =>
{
b.HasOne("Diligent.WebAPI.Data.Entities.User", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});

modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<int>", b =>
{
b.HasOne("Diligent.WebAPI.Data.Entities.User", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});

modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<int>", b =>
{
b.HasOne("Diligent.WebAPI.Data.Entities.AppRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();

b.HasOne("Diligent.WebAPI.Data.Entities.User", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});

modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<int>", b =>
{
b.HasOne("Diligent.WebAPI.Data.Entities.User", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
#pragma warning restore 612, 618
}
}
}

+ 49
- 0
Diligent.WebAPI.Data/Migrations/20221024112939_AddedRefreshToken.cs Dosyayı Görüntüle

@@ -0,0 +1,49 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;

#nullable disable

namespace Diligent.WebAPI.Data.Migrations
{
public partial class AddedRefreshToken : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "RefreshTokens",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Token = table.Column<string>(type: "nvarchar(max)", nullable: false),
JwtId = table.Column<string>(type: "nvarchar(max)", nullable: false),
CreationDate = table.Column<DateTime>(type: "datetime2", nullable: false),
ExpiryDate = table.Column<DateTime>(type: "datetime2", nullable: false),
Used = table.Column<bool>(type: "bit", nullable: false),
Invalidated = table.Column<bool>(type: "bit", nullable: false),
UserId = table.Column<int>(type: "int", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_RefreshTokens", x => x.Id);
table.ForeignKey(
name: "FK_RefreshTokens_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});

migrationBuilder.CreateIndex(
name: "IX_RefreshTokens_UserId",
table: "RefreshTokens",
column: "UserId");
}

protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "RefreshTokens");
}
}
}

+ 49
- 0
Diligent.WebAPI.Data/Migrations/DatabaseContextModelSnapshot.cs Dosyayı Görüntüle

@@ -208,6 +208,44 @@ namespace Diligent.WebAPI.Data.Migrations
b.ToTable("Insurers");
});

modelBuilder.Entity("Diligent.WebAPI.Data.Entities.RefreshToken", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");

SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"), 1L, 1);

b.Property<DateTime>("CreationDate")
.HasColumnType("datetime2");

b.Property<DateTime>("ExpiryDate")
.HasColumnType("datetime2");

b.Property<bool>("Invalidated")
.HasColumnType("bit");

b.Property<string>("JwtId")
.IsRequired()
.HasColumnType("nvarchar(max)");

b.Property<string>("Token")
.IsRequired()
.HasColumnType("nvarchar(max)");

b.Property<bool>("Used")
.HasColumnType("bit");

b.Property<int>("UserId")
.HasColumnType("int");

b.HasKey("Id");

b.HasIndex("UserId");

b.ToTable("RefreshTokens");
});

modelBuilder.Entity("Diligent.WebAPI.Data.Entities.User", b =>
{
b.Property<int>("Id")
@@ -479,6 +517,17 @@ namespace Diligent.WebAPI.Data.Migrations
b.Navigation("InsuranceCompany");
});

modelBuilder.Entity("Diligent.WebAPI.Data.Entities.RefreshToken", b =>
{
b.HasOne("Diligent.WebAPI.Data.Entities.User", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();

b.Navigation("User");
});

modelBuilder.Entity("Diligent.WebAPI.Data.Entities.WebhookSubscription", b =>
{
b.HasOne("Diligent.WebAPI.Data.Entities.WebhookDefinition", "WebhookDefinition")

+ 13
- 0
Diligent.WebAPI.Host/Controllers/V1/UsersController.cs Dosyayı Görüntüle

@@ -37,5 +37,18 @@

return Ok(response);
}

[HttpPost("refresh")]
public async Task<IActionResult> RefreshToken([FromBody] RefreshTokenRequestDto model)
{
var response = await _userService.RefreshTokenAsync(model);

if (response.Error != null)
{
return BadRequest(new AuthFailedResponse { Error = response.Error });
}

return Ok(new RefreshedTokenResponseDto { Token = response.Token, RefreshToken = response.Token });
}
}
}

+ 21
- 4
Diligent.WebAPI.Host/Middlewares/JwtMiddleware.cs Dosyayı Görüntüle

@@ -16,12 +16,12 @@
var token = context.Request.Headers["Authorization"].FirstOrDefault()?.Split(" ").Last();

if (token != null)
AttachUserToContext(context, userService, token);
await AttachUserToContext(context, userService, token);

await _next(context);
}

private void AttachUserToContext(HttpContext context, IUserService userService, string token)
private async Task AttachUserToContext(HttpContext context, IUserService userService, string token)
{
try
{
@@ -33,15 +33,19 @@
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false,
ValidateAudience = false,
RequireExpirationTime = false,
ValidateLifetime = true,
// set clockskew to zero so tokens expire exactly at token expiration time (instead of 5 minutes later)
ClockSkew = TimeSpan.Zero
//ClockSkew = TimeSpan.Zero
}, out SecurityToken validatedToken);

var jwtToken = (JwtSecurityToken)validatedToken;
var userId = int.Parse(jwtToken.Claims.First(x => x.Type == "id").Value);

// attach user to context on successful jwt validation
context.Items["User"] = userService.GetById(userId);
context.Items["User"] = await userService.GetById(userId);

await UpdateRefreshToken(context, userService, userId);
}
catch
{
@@ -49,5 +53,18 @@
// user is not attached to context so request won't have access to secure routes
}
}

private async Task UpdateRefreshToken(HttpContext context, IUserService userService, int userId)
{
var refreshToken = await userService.GetRefreshTokenByUserId(userId);

if (refreshToken == null)
return;

refreshToken.ExpiryDate = DateTime.UtcNow.AddMinutes(30);

await userService.UpdateRefreshToken(refreshToken);
}
}
}

+ 2
- 0
Diligent.WebAPI.Host/appsettings.Development.json Dosyayı Görüntüle

@@ -3,6 +3,8 @@
"WebApi": "Server=.;Database=HRCenter;Trusted_Connection=True;MultipleActiveResultSets=true"
},
"Authorization": {
"JwtExpiredTime": "5",
"JwtRefreshExpiredTime": "30",
"Secret": "SECRET_ASKGFH#$_#((Y)#I%EWJGDSJTGKEOS@$SAF"
}
}

Loading…
İptal
Kaydet