Two Tabs, One Refresh Token, and a Logout Fifteen Minutes Later
About 15 minutes after a successful two-tab refresh, one credential triggered reuse handling. Our grace-window fix made both tabs converge on the same pair.
Technical reviewer: Dmytro Nasyrov, Founder and CTO of Pharos Production.
Both browser tabs refreshed successfully. About fifteen minutes later, the shared session was revoked. The failure began in the earlier refresh race, when our first implementation rotated the token pair twice instead of making the tabs converge on one result.
This case comes from the September 28 and 29 authentication work for our medical learning platform. The session design deliberately allowed a short overlap for concurrent refreshes. Review exposed that accepting both requests was an insufficient invariant: their returned credentials had to agree.
A successful race can leave one tab holding yesterday's state
Consider two tabs that share refresh token R0. The first request rotates R0 into a new pair containing R1. The second request arrives with R0 while the grace window is still open.
Our original implementation accepted that previous token and rotated again, producing R2. Both requests returned successfully. The session now considered R2 current and R1 previous, while the first tab still held R1.
When that tab later performed its ordinary refresh, the grace window had passed. Its validly signed R1 now resembled reuse of an old credential. Revoking the shared session then affected both tabs.
| Step | Original behavior | Required behavior |
|---|---|---|
| Tab A presents current R0. | Rotate and return R1. | Rotate and return R1. |
| Tab B presents R0 during grace. | Rotate again and return R2. | Return the same pair containing R1. |
| The race ends. | The tabs hold different refresh tokens. | The tabs hold the same refresh token. |
| Tab A refreshes after grace. | R1 can trigger old-token reuse handling. | R1 is the current credential until a legitimate later rotation. |
R0, R1 and R2 are explanatory labels, not captured tokens. The useful regression assertion is the state after the race and after the next refresh cycle.
Store the session separately from an individual access token
The design stored a session row with its identifier, user and role, expiration and revocation fields. It stored hashes of the current and previous refresh tokens. It did not store ready-to-use refresh-token strings in those columns.
Refreshing issued a new token pair with the same session identifier. Two tabs using that identifier were sharing a session. They were not independent sessions merely because they held different in-memory copies of a token.
An access-token check validated the signature and claims, then checked the live session. The design allowed an older access token to remain valid until its own expiration after a refresh. Explicit session revocation still took effect through the session lookup.
That distinction explained the delayed failure. Rotation did not immediately invalidate the first tab's access token. The problematic refresh credential stayed hidden until that tab needed to renew access.
The grace path must leave the session row unchanged
The corrected refresh operation locked the session row with FOR UPDATE. Presenting the current refresh token triggered one rotation. Presenting the previous token within the configured thirty-second grace window returned exactly the pair produced by the first request, byte for byte.
The previous-token path did not rotate again and did not update the session row. That was the convergence rule missing from the first version. Accepting a concurrent request and making it produce another successor had preserved availability while corrupting the clients' shared state.
Outside grace, a previous token with a valid signature followed reuse handling and revoked the session. This case does not propose accepting old refresh tokens indefinitely. It describes a narrow concurrency allowance with a recorded boundary.
The implementation reconstructed the same pair from stored session values and token-generation inputs. That choice needs its own cryptographic review, including claim consistency and signing behavior. Reproducing this application's session invariant does not establish a reusable cryptographic scheme.
Logout had a related expiration trap
A reader may press Logout after the access token has expired. Rejecting the logout operation solely because of that expiration can leave a still-live refresh session behind.
The recorded correction allowed logout to identify the session using an expired access token with a valid signature and the required token type, issuer and audience checks. It could then revoke the session. Ordinary authenticated operations retained their normal expiration checks.
The exception was tied to revocation. It was not a general instruction to accept expired tokens throughout the application. Tests needed to distinguish successful logout from successful access to protected resources.
Test beyond the response that looked successful
The September 29 record includes concurrent refresh tests and a gateway verification script reporting thirty-one successful checks on its final local images. Those checks covered a broader authentication flow; thirty-one is not the sample size of a long-running two-tab reliability study.
A later October 7 record identified a coverage gap: the grace-window test had been tried with a company session in a scratch copy, but that variant was not committed as regression coverage. A successful scratch run did not erase the gap in the repository's tests.
For a fresh verification of this fix, we would use a deterministic clock, race the first refresh, assert identical returned pairs and unchanged grace-path storage, then advance beyond grace and exercise the next refresh. We would separately test old-token reuse, explicit revocation and the company-session variant.
The streaming-answer case reached a similar concurrency boundary with a different resource: each caller needed a shared state transition, rather than an independent successful response. Here the decisive assertion was that two tabs ended the race holding one usable successor.
Evidence: the September 28 authentication design, September 29 implementation review and live verification record, plus the October 7 deferred-test note. The token labels are illustrative. This article reports the recorded defect and correction; it is not a current security audit or a recommendation to copy the signing construction.
FAQ
Quick answers to common questions about custom software development, pricing, process and technology.
Type to filter questions and answers. Use Topic to narrow the list.
Showing all 14
No matches
Try a different keyword, change the topic or clear filters
-
How long have you been in business?
Pharos Production has been in business since 2013, with over 13 years of experience in custom software development. During this time, we have delivered over 110 applications for 200+ clients across 28 industries, including FinTech, healthcare, crypto and e-commerce. We are rated 5/5 on Clutch based on 107 verified reviews (2026).
-
What services do you provide?
Pharos Production provides six core service categories: AI Development (LLM applications, AI agents, machine learning and computer vision), Blockchain and Web3 (smart contracts, DeFi, tokenization on Ethereum, Solana, TON and other chains), FinTech Development (payment platforms, digital banking, KYC and AML integration), Software Development (mobile apps, web platforms, database design, UI/UX), Security and QA (code audits, penetration testing, smart contract audits, manual and automated testing) and Consulting (architecture design, MVP validation, startup consulting).
-
Where is Pharos Production located?
Pharos Production is headquartered in Las Vegas, Nevada, USA (5348 Vegas Dr, Las Vegas, NV 89108), with an engineering office in Kyiv, Ukraine (44-B Eugene Konovalets Str. Suite 201, Kyiv 01133). We work with clients worldwide and provide remote collaboration across all time zones. Visit our contact page for directions and scheduling options.
-
How big is your team?
Pharos Production has a team of 90+ engineers, including software developers, blockchain specialists, QA engineers, DevOps experts, UI/UX designers, project managers and solution architects. Our founder, Dr. Dmytro Nasyrov, holds a PhD in Artificial Intelligence and leads the technical direction of all projects.
-
What types of customers do you serve?
We serve a wide range of clients, from startups and product companies to mid-sized enterprises and large institutions. Our clients include crypto exchanges, FinTech providers (like Pleenk), healthcare organizations, sportsbook operators (like Pro Gambling), e-commerce platforms and SaaS companies. Pharos Production has worked with 200+ clients across 28 industries since 2013, adapting engagement models to match each client's stage, whether it is MVP validation for a startup or enterprise-scale development for an established business.
-
How is a custom software development company different from an agency?
A custom software development company like Pharos Production assigns one dedicated engineering team that owns your product across multiple release cycles, instead of handing it off once a contract ends. Agencies more often rotate staff between clients and optimize for fast delivery over long-term product ownership.
Pharos Production keeps the same team on your codebase through 2-week sprints, so there is no re-onboarding cost each time you request a new feature.
-
How much does custom software development cost?
Custom software development costs vary based on project scope and complexity. At Pharos Production, typical project ranges are: MVP development ($10,000-$25,000), suitable for startups validating a product idea; full-fledged production ($25,000-$50,000), for established businesses scaling a proven concept; and full-cycle development ($50,000-$80,000+), for complex enterprise-grade systems. These ranges include architecture design, development, QA testing and deployment. Hourly rates range from $50 to $99 depending on role and seniority. Final pricing depends on technology stack, number of integrations and engagement model (staff augmentation, dedicated team or project outsourcing).
-
How long does it take to develop a custom software application?
Development timelines depend on scope and complexity. At Pharos Production, a typical MVP takes 2-4 months, a production-ready application takes 4-8 months and a complex enterprise system can take 8-12+ months. We use an agile methodology with 2-week sprints, delivering working increments after each sprint. Every sprint includes a retrospective, progress report and planning session for the next iteration. This approach ensures transparency and allows businesses to launch faster by prioritizing high-impact features first. Get a timeline estimate for your project.
-
What industries does Pharos Production serve?
Pharos Production serves 28 industries: FinTech, MiCA Compliance, Banking, Insurance, Crypto, Healthcare, Fitness, E-Commerce, Food & Restaurant, On-Demand, Manufacturing, Automotive, Energy, Oil & Gas, Supply Chain, Construction, Real Estate, Gaming, Streaming, Media, Telecom, Education, Aviation, Travel, Logistics, Marketing, Agriculture and Recommender Systems. Our deepest expertise is in FinTech, blockchain and healthcare, where we have delivered compliance-ready platforms (HIPAA, PCI DSS, GDPR) and high-load systems handling thousands of concurrent users. For the latest industry insights, read our guides on FinTech trends in 2026 and the Web3 technology stack.
-
Why hire a software development company instead of building in-house?
Hiring a software development company offers faster time-to-market, lower upfront costs and access to specialized expertise without long-term employment commitments. According to Deloitte's 2024 Global Outsourcing Survey, 57% of companies outsource software development to access skills they cannot hire internally.
Factor In-house team Software development company Time to assemble 3-6 months (recruiting + onboarding) 1-2 weeks Upfront cost High (salaries, benefits, equipment) Lower (project-based pricing) Specialized expertise Limited to who you can hire locally Access to 90+ engineers across blockchain, AI, FinTech Scalability Slow (each new hire takes months) Fast (scale up or down per sprint) Long-term commitment Full-time employment contracts Flexible engagement models Risk High if key engineers leave Company ensures continuity and knowledge transfer For businesses that need blockchain, AI or high-load architecture expertise, outsourcing to a specialized firm like Pharos Production reduces risk and accelerates delivery.
-
What projects does Pharos Production not take on?
Pharos Production focuses on mid-to-large custom software projects with budgets starting at $10,000. We do not take on template-based websites, WordPress theme customization or short-term contracts under one month. We also do not provide non-technical staffing (marketing, sales or design-only roles). Our strongest fit is blockchain, FinTech and healthcare projects where security, compliance and high-load architecture are critical. For smaller projects or MVPs under $10,000, we recommend exploring freelance platforms or no-code tools as a more cost-effective starting point.
-
Why does Pharos Production use agile methodology?
We use agile with 2-week sprints because it reduces the risk of building features that miss the mark. Each sprint ends with a working demo, a retrospective and a plan for the next iteration.
This means clients see progress every 14 days and can adjust priorities based on real results, not assumptions. We chose this approach after years of experience showing that rigid, fixed-scope contracts lead to scope creep, missed deadlines and products that do not match market needs by launch day.
-
When should you NOT hire a custom software development company?
Custom development is not the right choice in every situation. You should not hire a custom software company if: your problem is fully solved by an existing SaaS product (e.g. Shopify for e-commerce, Salesforce for CRM); your budget is under $10,000 and timeline is under 4 weeks; you need a simple landing page or marketing website (WordPress or Webflow is faster and cheaper); or you are still validating the idea and have not spoken to potential users yet.
In these cases, off-the-shelf tools or no-code platforms offer better ROI. Custom development makes sense when you need unique workflows, regulatory compliance, high-load architecture or competitive differentiation that packaged software cannot provide.
-
What kind of projects has Pharos Production delivered?
Here are three anonymized examples from our recent delivery history:
FinTech startup - payment platform (MVP)
Scope: mobile app + backend API with bank-grade encryption. Team: 4 engineers, 1 QA. Timeline: 10 weeks. Budget: $38,000. Result: launched on schedule, processed $2M+ in transactions within the first quarter.Healthcare provider - patient portal (Full product)
Scope: HIPAA-aligned web platform with EHR integration, appointment scheduling and telemedicine. Team: 6 engineers, 1 DevOps, 2 QA. Timeline: 6 months. Budget: $120,000. Result: 15,000+ active patients, zero compliance violations in two annual audits.Crypto exchange - trading engine (Complex)
Scope: high-load matching engine handling 50,000+ orders per second, multi-chain wallet infrastructure on Ethereum and Solana. Team: 8 engineers, 2 QA, 1 security auditor. Timeline: 11 months. Budget: $340,000. Result: 99.97% uptime, passed three independent security audits.See more projects: NoMoreBets, Pulse, Sagas, Gambit Stream and Pleenk. For the full portfolio, visit our case studies. Learn more about the technology behind these projects in our guide to stablecoins and crypto infrastructure.
Dmytro Ihnatov is the Lead Backend Developer at Pharos Production. He documents backend architecture and research experiments from the team's work on a professional learning platform for aesthetic medicine.