Мобильное приложение через IdentityServer подключается к микросервису и затем через API(s) к базе данных. Все работает. Кроме бага, который обнаружен (иногда), мобильное приложение по истечении валидности токена или при перезапуске Identity выдает ошибку или просит зайти заново. В следствии чего, понятно, что токен кешируется локально и иногда теряется. Я новичок, и так я это вижу
Решение: Нужно сохранять токен в базе данных, чтобы при надобности IdentityServer мог запросить и тем самым баг решен✔ ВСЕ РАБОТАЕТ.
В данном вопросе хотелось бы рассмотреть возможное решение для элегантности кода. Ссылки откуда я брала информацию :
Документация
Изменения двух моделей, которые не имеют по умолчанию ID
modelBuilder.Entity<DeviceFlowCodes>().HasNoKey();
modelBuilder.Entity<PersistedGrant>().HasNoKey();
//в моем случае не позволяло сохранить токен, поля были readOnly
над приложением я не одна работаю, и ниже прикладываю код, который я добавила уже к рабочему IdentityServer
добавила NuGet:
IdentityServer4 3.1.4
IdentityServer4.EntityFramework.Storage 3.1.4
Npgsql.EntityFrameworkCore.PostgreSQL 3.1.4
IdentityServer4.EntityFramework 3.1.4
public class IdentityServer4DbContext : IPersistedGrantDbContext, IConfigurationDbContext ...
{
public DbSet<PersistedGrant> PersistedGrants { get; set; }
public DbSet<DeviceFlowCodes> DeviceFlowCodes { get; set; }
public DbSet<Client> Clients { get; set; }
public DbSet<IdentityResource> IdentityResources { get; set; }
public DbSet<ApiResource> ApiResources { get; set; }
...
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
base.OnConfiguring(optionsBuilder);
optionsBuilder.UseNpgsql(configuration.ConnectionString, o => o.MigrationsAssembly(typeof(Program).Assembly.GetName().Name));
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
if (modelBuilder is null)
throw new ArgumentNullException(nameof(modelBuilder));
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.DeviceFlowCodes", b =>
{
b.Property<string>("UserCode")
.ValueGeneratedOnAdd()
.HasMaxLength(200);
b.Property<string>("ClientId")
.IsRequired()
.HasMaxLength(200);
b.Property<DateTime>("CreationTime");
b.Property<string>("Data")
.IsRequired()
.HasMaxLength(50000);
b.Property<string>("DeviceCode")
.IsRequired()
.HasMaxLength(200);
b.Property<DateTime?>("Expiration")
.IsRequired();
b.Property<string>("SubjectId")
.HasMaxLength(200);
b.HasKey("UserCode");
b.HasIndex("DeviceCode")
.IsUnique();
b.HasIndex("UserCode")
.IsUnique();
b.ToTable("DeviceCodes");
});
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.PersistedGrant", b =>
{
b.Property<string>("Key")
.HasMaxLength(200);
b.Property<string>("ClientId")
.IsRequired()
.HasMaxLength(200);
b.Property<DateTime>("CreationTime");
b.Property<string>("Data")
.IsRequired()
.HasMaxLength(50000);
b.Property<DateTime?>("Expiration");
b.Property<string>("SubjectId")
.HasMaxLength(200);
b.Property<string>("Type")
.IsRequired()
.HasMaxLength(50);
b.HasKey("Key");
b.HasIndex("SubjectId", "ClientId", "Type");
b.ToTable("PersistedGrants");
});
}
Task<int> IPersistedGrantDbContext.SaveChangesAsync() => base.SaveChangesAsync();
public Task<int> SaveChangesAsync() => base.SaveChangesAsync();
public void ConfigureServices(IServiceCollection services)
{
...
services.AddConfigurationStore<IdentityServer4DbContext>()
.AddOperationalStore<IdentityServer4DbContext>(options => {
// this enables automatic token cleanup.
options.EnableTokenCleanup = true;
options.TokenCleanupInterval = 43200; // 43200 = 12 hours. interval in seconds (default is 3600)
});
...
}
private async void InitializeDatabase(IApplicationBuilder app)
{
.....
IdentityServer4DbContext dbContext = serviceScope.ServiceProvider.GetRequiredService<IdentityServer4DbContext>();
dbContext.Database.Migrate();
.....
if (!dbContext.Clients.Any())
{
foreach (var client in Config.Clients)
{
dbContext.Clients.Add(client.ToEntity());
}
await dbContext.SaveChangesAsync().ConfigureAwait(false);
}
if (!dbContext.IdentityResources.Any())
{
foreach (var resource in Config.Ids)
{
dbContext.IdentityResources.Add(resource.ToEntity());
}
await dbContext.SaveChangesAsync().ConfigureAwait(false);
}
if (!dbContext.ApiResources.Any())
{
foreach (var resource in Config.Apis)
{
dbContext.ApiResources.Add(resource.ToEntity());
}
await dbContext.SaveChangesAsync().ConfigureAwait(false);
}
}
затем миграция и в результате созданы таблицы с данными и токенами, при вхождении с мобильного и никаких ошибок, работает и тестировала много, перезагружала Identity пока мобильное приложение работало и наоборот, и дургими способами, которые раньше выдавали ошибку. То есть код рабочий, но вот в чем мой вопрос профессионалам: Та часть, где вручную set-ирую две модели (DeviceFlowCodes, PersistedGrant
), есть ли какой то вариант создать класс отдельно как модель, так как я пыталась, но у меня не вышло, и пока я не вижу как вытащить этот код наружу из OnModelCreating
Хотелось бы услышать различные точки зрения и возможно замечания.
Статические методы, которые позволяют проделать то же самое, и вуаля, эллегантное решение кода. Проверено, работает также и проведенно немало тестов ✔
При нажатии на объект - объект должен "исчезать", а на его месте должен появляться эффект