We're using Hangfire for recurring and background job management. However, we're receiving the following error. We added the following packages to the API host project from previous support tickets. Our ABP version is 9.1.0.
Eror detail Unauthorized BT! IDX10223: Lifetime validation failed. The token is expired. ValidTo (UTC): '21.07.2025 07:26:48', Current time (UTC): '21.07.2025 08:03:19
cs
<ItemGroup>
<PackageReference Include="Microsoft.IdentityModel.Protocols.OpenIdConnect" Version="8.12.1" />
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="8.12.1" />
<PackageReference Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.12.1" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.12.1" />
</ItemGroup>
9 Answer(s)
-
0
The error "IDX10223: Lifetime validation failed. The token is expired." indicates that the JWT access token used by your Hangfire job is expired at the time of execution. This is a common issue when background jobs are scheduled to run after the token's expiration, especially in distributed or tiered ABP applications.
To resolve this, you should ensure that your Hangfire jobs acquire a fresh access token before making authorized API calls. Do not cache or reuse tokens for long-running or recurring jobs.
In microservice or tiered ABP setups, the recommended approach is to use the client credentials flow to programmatically request a new token for each job execution. This ensures the token is always valid.
If you are using ABP's built-in remote service proxies, configure them to use the client credentials flow and set up the necessary client in your IdentityServer/OpenIddict configuration.
Avoid using user tokens for background jobs, as they will expire and cannot be refreshed without user interaction.
If you are using distributed cache for tokens, ensure the cache expiration is not longer than the token's lifetime.
This approach aligns with common ABP conventions, but specific official documentation for this exact scenario is not available.
This response was generated by AI. If it didn’t solve your problem, feel free to post a new answer — our support team will jump in to help you soon.
-
0
This answer cannot resolve my problem
-
0
hi
Why does your Hangfire need to use a JWT token?
Can you share some code?
Thanks.
-
0
We don't use JWT tokens in the job, but it gives an error.
namespace EnzimWeb._Jobs { public class EnzimOnlineVizitBransListesiWorker : HangfireBackgroundWorkerBase { private readonly IBackgroundJobManager _backgroundJobManager; private readonly IMedicanaCMSApiClientService _CMSclient; private readonly IRandevuServiceClient _randevuWS; private readonly Microsoft.Extensions.Configuration.IConfiguration _configuration; private readonly OnlineVizitBranslarManager _onlineVizitBranslarManager; protected IOnlineVizitBranslarRepository _onlineVizitBranslarRepository; protected ILanguageTextRepository _languageRepository; protected IOnlineVizitDoktorRepository _onlineVizitDoktorRepository; private int timeInterval = 30; private int controlRange = 30; public EnzimOnlineVizitBransListesiWorker(IBackgroundJobManager backgroundJobManager, IMedicanaCMSApiClientService CMSclient, IRandevuServiceClient randevuWS, Microsoft.Extensions.Configuration.IConfiguration configuration, OnlineVizitBranslarManager onlineVizitBranslarManager, IOnlineVizitBranslarRepository onlineVizitBranslarRepository, ILanguageTextRepository languageRepository, IOnlineVizitDoktorRepository onlineVizitDoktorRepository ) { RecurringJobId = nameof(EnzimOnlineVizitBransListesiWorker); CronExpression = $"*/{timeInterval} * * * *";//Cron.Minutely nin dakika verilebileni obselete. yerine methodu da anlamadım _CMSclient = CMSclient; _randevuWS = randevuWS; _configuration = configuration; _onlineVizitBranslarManager = onlineVizitBranslarManager; _backgroundJobManager = backgroundJobManager; _onlineVizitBranslarRepository = onlineVizitBranslarRepository; _languageRepository = languageRepository; _onlineVizitDoktorRepository = onlineVizitDoktorRepository; } public override async Task DoWorkAsync(CancellationToken cancellationToken = default) { bool isDevelopment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Development"; //bool isDevelopment = false; if (!isDevelopment) { ExceptionlessClient.Default .CreateLog("Info") .AddTags("startJob", "EnzimOnlineVizitBransListesiWorker") .Submit(); try { #region Enzimden branslar cekilerek kaydediliyor HastaneAraIstek hastaneAraIstek = new HastaneAraIstek { WSRandevuTipi = WSRandevuTipi.OnlineGorusme }; var hastaneler = await _randevuWS.HastaneAraAsync(hastaneAraIstek); foreach (var hastane in hastaneler.Hastaneler.Where(x => x.ID != 1000 && x.ID != 6 && x.ID != 99)) { PozisyonAraIstek input = new PozisyonAraIstek { HastaneId = hastane.ID, WSRandevuTipi = WSRandevuTipi.OnlineGorusme }; var pozisyonlar = await _randevuWS.PozisyonAraAsync(input); if (pozisyonlar != null && pozisyonlar.Pozisyonlar.Count() > 0) { foreach (var pozisyon in pozisyonlar.Pozisyonlar) { var DbBrans = await _onlineVizitBranslarRepository.GetListAsync( x => x.EnzimPozisyonId == pozisyon.ID.ToString() & x.EnzimHospitalId == hastane.ID.ToString() ); if (DbBrans.Count == 0) { var Sonuc = await _onlineVizitBranslarManager.CreateAsync(true, pozisyon.Adi, pozisyon.ID.ToString(), null, null, hastane.ID.ToString() , null, "Online." + Temizle(pozisyon.Adi) + "_" + hastane.ID.ToString() + "_" + pozisyon.ID.ToString()); var text = (await _languageRepository.GetListAsync()).FirstOrDefault(l => l.CultureName == "tr" && l.ResourceName == "EnzimWeb" && l.Name == Sonuc.AbpLanguageKey); if (text == null) { await _languageRepository.InsertAsync( new LanguageText(Guid.NewGuid(), "EnzimWeb", "tr", Sonuc.AbpLanguageKey, Sonuc.BransAdi, null) ); } else { text.Value = pozisyon.Adi; await _languageRepository.UpdateAsync(text); } // await _languageRepository.InsertAsync( //new LanguageText(Guid.NewGuid(), "EnzimWeb", "tr", Sonuc.AbpLanguageKey, Sonuc.BransAdi, null) //); } } } #region randevu serviste yoksa pasife cek var DbBranslar = await _onlineVizitBranslarRepository.GetListAsync(x=> x.EnzimHospitalId == hastane.ID.ToString()); foreach (var dbBrans in DbBranslar) { if(!pozisyonlar.Pozisyonlar.Any(x=>x.ID == Convert.ToInt32(dbBrans.EnzimPozisyonId))) { var Update = await _onlineVizitBranslarRepository.GetAsync(dbBrans.Id); Update.RandevuAlinabilirmi = false; var Updatesonuc = await _onlineVizitBranslarRepository.UpdateAsync(Update); var DBdoktorlar = await _onlineVizitDoktorRepository.GetListAsync( x => x.EnzimHospitalId == hastane.ID.ToString() && x.EnzimPozisyonId == dbBrans.EnzimPozisyonId.ToString() ); foreach (var DBdoktor in DBdoktorlar) { DBdoktor.RandevuAlinabilirmi = false; var Updatedoktorsonuc = await _onlineVizitDoktorRepository.UpdateAsync(DBdoktor); } } else { var Update = await _onlineVizitBranslarRepository.GetAsync(dbBrans.Id); Update.RandevuAlinabilirmi = true; var Updatesonuc = await _onlineVizitBranslarRepository.UpdateAsync(Update); } } #endregion } #endregion #region CMSten tüm branslar getirilecek ve enzimden gelenler ile eşleştirilerek db de update edilecek await _CMSclient.AuthenticateAsync(); var hospitals = await _CMSclient.GetHospitalsAsync(1); var langs = await _CMSclient.GetLanguagesAsync(); foreach (var hastane in hospitals) { foreach (var lang in langs.Where(x => x.Code != "tr")) { List<MedicalUnit> medicalUnit = await _CMSclient.GetMedicalUnitsByHospitalAsync(hastane.Id, lang.Id); string langCode = lang.Code; if (langCode == "de") langCode = "de-DE"; else if (langCode == "bosnah") langCode = "bs"; foreach (var unit in medicalUnit) { var DbBrans = await _onlineVizitBranslarRepository.GetListAsync( x => x.EnzimPozisyonId == unit.enzimCode & x.EnzimHospitalId == unit.HospitalEnzimCode ); if (DbBrans != null && DbBrans.Count == 1) { var text = (await _languageRepository.GetListAsync()).FirstOrDefault(l => l.CultureName == langCode && l.ResourceName == "EnzimWeb" && l.Name == DbBrans.First().AbpLanguageKey); if (text == null) { await _languageRepository.InsertAsync( new LanguageText(Guid.NewGuid(), "EnzimWeb", langCode, DbBrans.First().AbpLanguageKey, unit.MedicalUnitName, null) ); } else { text.Value = unit.MedicalUnitName; await _languageRepository.UpdateAsync(text); } var updateSonuc = await _onlineVizitBranslarManager.UpdateAsync(DbBrans.First().Id, DbBrans.First().RandevuAlinabilirmi, DbBrans.First().BransAdi, DbBrans.First().EnzimPozisyonId , unit.MedicalUnitID.ToString(), hastane.Id.ToString(), DbBrans.First().EnzimHospitalId, "https://www.medicana.com.tr/tibbi-birimler/" + unit.MedicalUnitURL , DbBrans.First().AbpLanguageKey); } } } } #endregion ExceptionlessClient.Default .CreateLog("Info") .AddTags("endJob", "EnzimOnlineVizitBransListesiWorker") .Submit(); } catch (Exception ex) { ExceptionlessClient.Default .CreateLog("Error") .AddObject(ex, "ex") .AddTags("response", "EnzimOnlineVizitBransListesiWorker") .Submit(); throw; } } } public static string Temizle(string input) { if (string.IsNullOrWhiteSpace(input)) return string.Empty; // Türkçe karakterleri İngilizce karşılıklarına çevir var karakterMap = new Dictionary<char, char> { { 'ç', 'c' }, { 'Ç', 'C' }, { 'ğ', 'g' }, { 'Ğ', 'G' }, { 'ı', 'i' }, { 'İ', 'I' }, { 'ö', 'o' }, { 'Ö', 'O' }, { 'ş', 's' }, { 'Ş', 'S' }, { 'ü', 'u' }, { 'Ü', 'U' } }; var sb = new StringBuilder(); foreach (char c in input) { if (karakterMap.ContainsKey(c)) sb.Append(karakterMap[c]); else sb.Append(c); } // Noktalama işaretlerini ve boşlukları temizle string sonuc = Regex.Replace(sb.ToString(), @"[\p{P}\p{S}\s]+", ""); return sonuc; } } }
-
0
hi
The error occur at
_randevuWS.HastaneAraAsync(hastaneAraIstek);
Please share the
HastaneAraAsync
method code.Thanks.
-
0
public async Task<HastaneAraCevap> HastaneAraAsync(HastaneAraIstek istek) { await EnsureTokenAsync(); return await _client.HastaneAraAsync(istek); } private async Task EnsureTokenAsync() { try { if (string.IsNullOrEmpty(_token)) { var loginIstek = new LoginIstek { KullaniciAdi = _username, Sifre = _password }; var loginSonuc = await _authClient.LoginAsync(loginIstek); _token = loginSonuc.Token; if (!_client.Endpoint.EndpointBehaviors.Any(b => b is AuthHeaderEndpointBehavior)) { _client.Endpoint.EndpointBehaviors.Add(new AuthHeaderEndpointBehavior(_token)); } } } catch (Exception ex) { string mesaj = ex.Message; throw; } }
-
0
hi
Does the
HastaneAraAsync
method use a token to request the API?Can you write the token to the logs?
I think it has expired. How did you obtain the token?
Thanks
-
0
This error is not related to abp.io. We've determined that it's related to the token expiration date of the service we're calling. The solution in the link below was misleading. Thank you for your help. Sorry. We'd appreciate it if you could refund the ticket.
-
0
I'm glad you solved the problem. 👍