This tutorial walks through building a production-ready OAuth callback server that works across Node.js, Deno, and Bun. We'll cover everything from the basic HTTP server setup to handling edge cases that trip up most implementations.This tutorial walks through building a production-ready OAuth callback server that works across Node.js, Deno, and Bun. We'll cover everything from the basic HTTP server setup to handling edge cases that trip up most implementations.

How to Capture OAuth Callbacks in CLI and Desktop Apps with Localhost Servers

7 min read

When building CLI tools or desktop applications that integrate with OAuth providers, you face a unique challenge: how do you capture the authorization code when there's no public-facing server to receive the callback? The answer lies in a clever technique that's been right under our noses — spinning up a temporary localhost server to catch the OAuth redirect.

This tutorial walks through building a production-ready OAuth callback server that works across Node.js, Deno, and Bun. We'll cover everything from the basic HTTP server setup to handling edge cases that trip up most implementations.

Understanding the OAuth Callback Flow

Before diving into code, let's clarify what we're building. In a typical OAuth 2.0 authorization code flow, your application redirects users to an authorization server (like GitHub or Google), where they grant permissions. The authorization server then redirects back to your application with an authorization code.

For web applications, this redirect goes to a public URL. But for CLI tools and desktop apps, we use a localhost UR — typically http://localhost:3000/callback. The OAuth provider redirects to this local address, and our temporary server captures the authorization code from the query parameters.

This approach is explicitly blessed by OAuth 2.0 for Native Apps (RFC 8252) and is used by major tools like the GitHub CLI and Google's OAuth libraries.

Setting Up the Basic HTTP Server

The first step is creating an HTTP server that can listen on localhost. Modern JavaScript runtimes provide different APIs for this, but we can abstract them behind a common interface using Web Standards Request and Response objects.

interface CallbackServer {   start(options: ServerOptions): Promise<void>;   waitForCallback(path: string, timeout: number): Promise<CallbackResult>;   stop(): Promise<void>; }  function createCallbackServer(): CallbackServer {   // Runtime detection   if (typeof Bun !== "undefined") return new BunCallbackServer();   if (typeof Deno !== "undefined") return new DenoCallbackServer();   return new NodeCallbackServer(); } 

Each runtime implementation follows the same pattern: create a server, listen for requests, and resolve a promise when the callback arrives. Here's the Node.js version that bridges between Node's http module and Web Standards:

class NodeCallbackServer implements CallbackServer {   private server?: http.Server;   private callbackPromise?: {     resolve: (result: CallbackResult) => void;     reject: (error: Error) => void;   };    async start(options: ServerOptions): Promise<void> {     const { createServer } = await import("node:http");      return new Promise((resolve, reject) => {       this.server = createServer(async (req, res) => {         const request = this.nodeToWebRequest(req, options.port);         const response = await this.handleRequest(request);          res.writeHead(           response.status,           Object.fromEntries(response.headers.entries()),         );         res.end(await response.text());       });        this.server.listen(options.port, options.hostname, resolve);       this.server.on("error", reject);     });   }    private nodeToWebRequest(req: http.IncomingMessage, port: number): Request {     const url = new URL(req.url!, `http://localhost:${port}`);     const headers = new Headers();      for (const [key, value] of Object.entries(req.headers)) {       if (typeof value === "string") {         headers.set(key, value);       }     }      return new Request(url.toString(), {       method: req.method,       headers,     });   } } 

}

The beauty of this approach is that once we convert to Web Standards, the actual request handling logic is identical across all runtimes.

Capturing the OAuth Callback

The heart of our server is the callback handler. When the OAuth provider redirects back, we need to extract the authorization code (or error) from the query parameters:

private async handleRequest(request: Request): Promise<Response> {   const url = new URL(request.url);    if (url.pathname === this.callbackPath) {     const params: CallbackResult = {};      // Extract all query parameters     for (const [key, value] of url.searchParams) {       params[key] = value;     }      // Resolve the waiting promise     if (this.callbackPromise) {       this.callbackPromise.resolve(params);     }      // Return success page to the browser     return new Response(this.generateSuccessHTML(), {       status: 200,       headers: { "Content-Type": "text/html" }     });   }    return new Response("Not Found", { status: 404 }); } 

Notice how we capture all query parameters, not just the authorization code. OAuth providers send additional information like state for CSRF protection, and error responses include error and error_description fields. Our implementation preserves everything for maximum flexibility.

Handling Timeouts and Cancellation

Real-world OAuth flows can fail in numerous ways. Users might close the browser, deny permissions, or simply walk away. Our server needs robust timeout and cancellation handling:

async waitForCallback(path: string, timeout: number): Promise<CallbackResult> {   this.callbackPath = path;    return new Promise((resolve, reject) => {     let isResolved = false;      // Set up timeout     const timer = setTimeout(() => {       if (!isResolved) {         isResolved = true;         reject(new Error(`OAuth callback timeout after ${timeout}ms`));       }     }, timeout);      // Wrap resolve/reject to handle cleanup     const wrappedResolve = (result: CallbackResult) => {       if (!isResolved) {         isResolved = true;         clearTimeout(timer);         resolve(result);       }     };      this.callbackPromise = {       resolve: wrappedResolve,       reject: (error) => {         if (!isResolved) {           isResolved = true;           clearTimeout(timer);           reject(error);         }       }     };   }); } 

Supporting AbortSignal enables programmatic cancellation, essential for GUI applications where users might close a window mid-flow:

if (signal) {   if (signal.aborted) {     throw new Error("Operation aborted");   }    const abortHandler = () => {     this.stop();     if (this.callbackPromise) {       this.callbackPromise.reject(new Error("Operation aborted"));     }   };    signal.addEventListener("abort", abortHandler); } 

Providing User Feedback

When users complete the OAuth flow, they see a browser page indicating success or failure. Instead of a blank page or cryptic message, provide clear feedback with custom HTML:

function generateCallbackHTML(   params: CallbackResult,   templates: Templates, ): string {   if (params.error) {     // OAuth error - show error page     return templates.errorHtml       .replace(/{{error}}/g, params.error)       .replace(/{{error_description}}/g, params.error_description || "");   }    // Success - show confirmation   return (     templates.successHtml ||     `     <html>       <body style="font-family: system-ui; padding: 2rem; text-align: center;">         <h1>✅ Authorization successful!</h1>         <p>You can now close this window and return to your terminal.</p>       </body>     </html>   `   ); } 

For production applications, consider adding CSS animations, auto-close functionality, or deep links back to your desktop application.

Security Considerations

While localhost servers are inherently more secure than public endpoints, several security measures are crucial:

  1. Bind to localhost only: Never bind to 0.0.0.0 or public interfaces. This prevents network-based attacks:
this.server.listen(port, "localhost"); // NOT "0.0.0.0" 

2. Validate the state parameter: OAuth's state parameter prevents CSRF attacks. Generate it before starting the flow and validate it in the callback:

const state = crypto.randomBytes(32).toString("base64url"); const authUrl = `${provider}/authorize?state=${state}&...`;  // In callback handler if (params.state !== expectedState) {   throw new Error("State mismatch - possible CSRF attack"); } 

3. Close the server immediately: Once you receive the callback, shut down the server to minimize the attack surface:

const result = await server.waitForCallback("/callback", 30000); await server.stop(); // Always cleanup 

4. Use unpredictable ports when possible: If your OAuth provider supports dynamic redirect URIs, use random high ports to prevent port-squatting attacks.

Putting It All Together

Here's a complete example that ties everything together:

import { createCallbackServer } from "./server"; import { spawn } from "child_process";  export async function getAuthCode(authUrl: string): Promise<string> {   const server = createCallbackServer();    try {     // Start the server     await server.start({       port: 3000,       hostname: "localhost",       successHtml: "<h1>Success! You can close this window.</h1>",       errorHtml: "<h1>Error: {{error_description}}</h1>",     });      // Open the browser     const opener =       process.platform === "darwin"         ? "open"         : process.platform === "win32"           ? "start"           : "xdg-open";     spawn(opener, [authUrl], { detached: true });      // Wait for callback     const result = await server.waitForCallback("/callback", 30000);      if (result.error) {       throw new Error(`OAuth error: ${result.error_description}`);     }      return result.code!;   } finally {     // Always cleanup     await server.stop();   } }  // Usage const code = await getAuthCode(   "https://github.com/login/oauth/authorize?" +     "client_id=xxx&redirect_uri=http://localhost:3000/callback", ); 

Best Practices and Next Steps

Building a robust OAuth callback server requires attention to detail, but the patterns are consistent across implementations. Key takeaways:

  • Use Web Standards APIs for cross-runtime compatibility
  • Handle all error cases including timeouts and user cancellation
  • Provide clear user feedback with custom success/error pages
  • Implement security measures like state validation and localhost binding
  • Clean up resources by always stopping the server after use

This localhost callback approach has become the de facto standard for OAuth in CLI tools. Libraries like oauth-callback provide production-ready implementations with additional features like automatic browser detection, token persistence, and PKCE support.

Modern OAuth is moving toward even better solutions like Device Code Flow for headless environments and Dynamic Client Registration for eliminating pre-shared secrets. But for now, the localhost callback server remains the most widely supported and user-friendly approach for bringing OAuth to command-line tools.


Ready to implement OAuth in your CLI tool? Check out the complete oauth-callback library for a battle-tested implementation that handles all the edge cases discussed here.

This tutorial is part of a series on modern authentication patterns. Follow @koistya for more insights on building secure, user-friendly developer tools.

Market Opportunity
READY Logo
READY Price(READY)
$0,008948
$0,008948$0,008948
-1,42%
USD
READY (READY) Live Price Chart
Disclaimer: The articles reposted on this site are sourced from public platforms and are provided for informational purposes only. They do not necessarily reflect the views of MEXC. All rights remain with the original authors. If you believe any content infringes on third-party rights, please contact service@support.mexc.com for removal. MEXC makes no guarantees regarding the accuracy, completeness, or timeliness of the content and is not responsible for any actions taken based on the information provided. The content does not constitute financial, legal, or other professional advice, nor should it be considered a recommendation or endorsement by MEXC.

You May Also Like

SEC greenlights new generic standards to expedite crypto ETP listings

SEC greenlights new generic standards to expedite crypto ETP listings

The post SEC greenlights new generic standards to expedite crypto ETP listings appeared on BitcoinEthereumNews.com. The U.S. Securities and Exchange Commission (SEC) has approved a new set of generic listing standards for commodity-based trust shares on Nasdaq, Cboe, and the New York Stock Exchange. The move is expected to streamline the approval process for exchange-traded products (ETPs) tied to digital assets, according to Fox Business reporter Eleanor Terret. However, she added that the Generic Listing Standards don’t open up every type of crypto ETP because threshold requirements remain in place, meaning not all products will immediately qualify. To add context, she quoted Tushar Jain of Multicoin Capital, who noted that the standards don’t apply to every type of crypto ETP and that threshold requirements remain. He expects the SEC will iterate further on these standards. The order, issued on Sept. 17, grants accelerated approval of proposed rule changes filed by the exchanges. By adopting the standards, the SEC aims to shorten the time it takes to bring new commodity-based ETPs to market, potentially clearing a path for broader crypto investment products. The regulator has been delaying the decision on several altcoin ETFs, most of which are set to reach their final deadlines in October. The move was rumored to be the SEC’s way of expediting approvals for crypto ETFs. The approval follows years of back-and-forth between the SEC and exchanges over how to handle crypto-based products, with past applications facing lengthy reviews. The new process is expected to reduce delays and provide more clarity for issuers, though the SEC signaled it may revisit and refine the standards as the market evolves. While the decision marks progress, experts emphasized that the so-called “floodgates” for crypto ETPs are not yet fully open. Future SEC actions will determine how broadly these standards can be applied across different digital asset products. Source: https://cryptoslate.com/sec-greenlights-new-generic-standards-to-expedite-crypto-etp-listings/
Share
BitcoinEthereumNews2025/09/18 08:43
Optimizely Named a Leader in the 2026 Gartner® Magic Quadrant™ for Personalization Engines

Optimizely Named a Leader in the 2026 Gartner® Magic Quadrant™ for Personalization Engines

Company recognized as a Leader for the second consecutive year NEW YORK, Feb. 5, 2026 /PRNewswire/ — Optimizely, the leading digital experience platform (DXP) provider
Share
AI Journal2026/02/06 00:47
Lovable AI’s Astonishing Rise: Anton Osika Reveals Startup Secrets at Bitcoin World Disrupt 2025

Lovable AI’s Astonishing Rise: Anton Osika Reveals Startup Secrets at Bitcoin World Disrupt 2025

BitcoinWorld Lovable AI’s Astonishing Rise: Anton Osika Reveals Startup Secrets at Bitcoin World Disrupt 2025 Are you ready to witness a phenomenon? The world of technology is abuzz with the incredible rise of Lovable AI, a startup that’s not just breaking records but rewriting the rulebook for rapid growth. Imagine creating powerful apps and websites just by speaking to an AI – that’s the magic Lovable brings to the masses. This groundbreaking approach has propelled the company into the spotlight, making it one of the fastest-growing software firms in history. And now, the visionary behind this sensation, co-founder and CEO Anton Osika, is set to share his invaluable insights on the Disrupt Stage at the highly anticipated Bitcoin World Disrupt 2025. If you’re a founder, investor, or tech enthusiast eager to understand the future of innovation, this is an event you cannot afford to miss. Lovable AI’s Meteoric Ascent: Redefining Software Creation In an era where digital transformation is paramount, Lovable AI has emerged as a true game-changer. Its core premise is deceptively simple yet profoundly impactful: democratize software creation. By enabling anyone to build applications and websites through intuitive AI conversations, Lovable is empowering the vast majority of individuals who lack coding skills to transform their ideas into tangible digital products. This mission has resonated globally, leading to unprecedented momentum. The numbers speak for themselves: Achieved an astonishing $100 million Annual Recurring Revenue (ARR) in less than a year. Successfully raised a $200 million Series A funding round, valuing the company at $1.8 billion, led by industry giant Accel. Is currently fielding unsolicited investor offers, pushing its valuation towards an incredible $4 billion. As industry reports suggest, investors are unequivocally “loving Lovable,” and it’s clear why. This isn’t just about impressive financial metrics; it’s about a company that has tapped into a fundamental need, offering a solution that is both innovative and accessible. The rapid scaling of Lovable AI provides a compelling case study for any entrepreneur aiming for similar exponential growth. The Visionary Behind the Hype: Anton Osika’s Journey to Innovation Every groundbreaking company has a driving force, and for Lovable, that force is co-founder and CEO Anton Osika. His journey is as fascinating as his company’s success. A physicist by training, Osika previously contributed to the cutting-edge research at CERN, the European Organization for Nuclear Research. This deep technical background, combined with his entrepreneurial spirit, has been instrumental in Lovable’s rapid ascent. Before Lovable, he honed his skills as a co-founder of Depict.ai and a Founding Engineer at Sana. Based in Stockholm, Osika has masterfully steered Lovable from a nascent idea to a global phenomenon in record time. His leadership embodies a unique blend of profound technical understanding and a keen, consumer-first vision. At Bitcoin World Disrupt 2025, attendees will have the rare opportunity to hear directly from Osika about what it truly takes to build a brand that not only scales at an incredible pace in a fiercely competitive market but also adeptly manages the intense cultural conversations that inevitably accompany such swift and significant success. His insights will be crucial for anyone looking to understand the dynamics of high-growth tech leadership. Unpacking Consumer Tech Innovation at Bitcoin World Disrupt 2025 The 20th anniversary of Bitcoin World is set to be marked by a truly special event: Bitcoin World Disrupt 2025. From October 27–29, Moscone West in San Francisco will transform into the epicenter of innovation, gathering over 10,000 founders, investors, and tech leaders. It’s the ideal platform to explore the future of consumer tech innovation, and Anton Osika’s presence on the Disrupt Stage is a highlight. His session will delve into how Lovable is not just participating in but actively shaping the next wave of consumer-facing technologies. Why is this session particularly relevant for those interested in the future of consumer experiences? Osika’s discussion will go beyond the superficial, offering a deep dive into the strategies that have allowed Lovable to carve out a unique category in a market long thought to be saturated. Attendees will gain a front-row seat to understanding how to identify unmet consumer needs, leverage advanced AI to meet those needs, and build a product that captivates users globally. The event itself promises a rich tapestry of ideas and networking opportunities: For Founders: Sharpen your pitch and connect with potential investors. For Investors: Discover the next breakout startup poised for massive growth. For Innovators: Claim your spot at the forefront of technological advancements. The insights shared regarding consumer tech innovation at this event will be invaluable for anyone looking to navigate the complexities and capitalize on the opportunities within this dynamic sector. Mastering Startup Growth Strategies: A Blueprint for the Future Lovable’s journey isn’t just another startup success story; it’s a meticulously crafted blueprint for effective startup growth strategies in the modern era. Anton Osika’s experience offers a rare glimpse into the practicalities of scaling a business at breakneck speed while maintaining product integrity and managing external pressures. For entrepreneurs and aspiring tech leaders, his talk will serve as a masterclass in several critical areas: Strategy Focus Key Takeaways from Lovable’s Journey Rapid Scaling How to build infrastructure and teams that support exponential user and revenue growth without compromising quality. Product-Market Fit Identifying a significant, underserved market (the 99% who can’t code) and developing a truly innovative solution (AI-powered app creation). Investor Relations Balancing intense investor interest and pressure with a steadfast focus on product development and long-term vision. Category Creation Carving out an entirely new niche by democratizing complex technologies, rather than competing in existing crowded markets. Understanding these startup growth strategies is essential for anyone aiming to build a resilient and impactful consumer experience. Osika’s session will provide actionable insights into how to replicate elements of Lovable’s success, offering guidance on navigating challenges from product development to market penetration and investor management. Conclusion: Seize the Future of Tech The story of Lovable, under the astute leadership of Anton Osika, is a testament to the power of innovative ideas meeting flawless execution. Their remarkable journey from concept to a multi-billion-dollar valuation in record time is a compelling narrative for anyone interested in the future of technology. By democratizing software creation through Lovable AI, they are not just building a company; they are fostering a new generation of creators. His appearance at Bitcoin World Disrupt 2025 is an unmissable opportunity to gain direct insights from a leader who is truly shaping the landscape of consumer tech innovation. Don’t miss this chance to learn about cutting-edge startup growth strategies and secure your front-row seat to the future. Register now and save up to $668 before Regular Bird rates end on September 26. To learn more about the latest AI market trends, explore our article on key developments shaping AI features. This post Lovable AI’s Astonishing Rise: Anton Osika Reveals Startup Secrets at Bitcoin World Disrupt 2025 first appeared on BitcoinWorld.
Share
Coinstats2025/09/17 23:40