Back to Blog
critical SEVERITY6 min read

How Missing API Authentication Happens in Node.js and How to Fix It

The GitHub API integration in `src/github.mjs` was making unauthenticated requests, subjecting the application to GitHub's strict 60 requests/hour rate limit. This fix adds secure authentication token injection from environment variables using conditional header spreading, enabling authenticated requests with a much higher rate limit (5,000 requests/hour).

O
By Orbis AppSec
Published September 7, 2026Reviewed September 7, 2026

Answer Summary

This is a missing authentication vulnerability in Node.js GitHub API integration (CWE-306: Missing Authentication for Critical Function). The `src/github.mjs` file's `DEFAULT_HEADERS` object lacked any mechanism to inject GitHub authentication tokens, forcing all API calls into the unauthenticated rate limit tier. The fix uses conditional object spreading to inject a Bearer token from the `GITHUB_TOKEN` environment variable, enabling the application to authenticate with GitHub and bypass rate limiting constraints.

Vulnerability at a Glance

cweCWE-306 (Missing Authentication for Critical Function)
fixConditional environment variable token injection via object spreading syntax
riskAPI rate limiting, service degradation, denial of service
languageJavaScript (Node.js)
root causeDEFAULT_HEADERS object in src/github.mjs lacks token injection mechanism
vulnerabilityMissing Authentication for API Integration

Introduction

In the src/github.mjs file, a Node.js module handling GitHub API requests, the application was making API calls without any authentication mechanism. While no hardcoded tokens or obvious security flaws existed in the examined code, the DEFAULT_HEADERS object—which sets the standard headers for all GitHub API requests—was missing a critical piece: any way to inject authentication tokens.

This wasn't a complicated flaw. It was elegantly simple: when developers constructed HTTP headers for GitHub API integration, they specified the user agent, API version, and content type, but provided no path for authentication credentials to flow from the environment into the request headers. This forced every single API request through GitHub's unauthenticated tier, which allows only 60 requests per hour.

For any application making regular API calls, this becomes a bottleneck. Rate limits are hit quickly. Service degrades. And the fix? It turns out to be just as simple as the problem.

The Vulnerability Explained

The Problem

Let's look at the original vulnerable code from src/github.mjs:

const DEFAULT_HEADERS = {
    "user-agent": "GreedySearch/1.0",
    accept: "application/vnd.github+json",
    "x-github-api-version": "2022-11-28",
};

At first glance, this looks reasonable. Standard headers for API communication. But notice what's not there: an Authorization header.

When this application makes any request to the GitHub API using these headers, GitHub sees an unauthenticated request. The API endpoint processes it as a guest request, applying the unauthenticated rate limit of 60 requests per hour per IP address. For a production application, this is crippling.

Why This Matters

The GitHub API publicly documents two tiers:
- Unauthenticated requests: 60 requests/hour
- Authenticated requests: 5,000 requests/hour (for personal access tokens)

That's an 83x difference in capacity. An application that should be able to handle hundreds of API calls instead gets starved after a handful.

Real-World Attack Scenario

Consider how this vulnerability plays out in practice:

  1. An attacker (or even legitimate high-traffic conditions) triggers your application to make GitHub API calls—fetching repository metadata, checking user information, or listing issues.

  2. Your application makes 61 unauthenticated requests in rapid succession to GitHub.

  3. GitHub responds with HTTP 403 (rate limit exceeded) for requests 61 onward.

  4. Your application either fails, returns stale data, or crashes entirely.

  5. If this is a microservice, cascading failures occur. If it's a user-facing feature, the service becomes unavailable.

This isn't a data breach vulnerability, but it's a denial of service vulnerability—one that's trivially exploitable simply by using your own application normally under load.

The Chain Complexity

This vulnerability represents a 2-step weakness chain:
1. Missing authentication mechanism (no token injection)
2. Rate limit threshold exceeded (leading to service disruption)

While not exploitable independently through code injection, this pattern is exactly what automated exploit tools look for: a preventable failure point that creates a serviceable attack vector.

The Fix

The security team implemented a surgical fix using JavaScript's conditional object spreading syntax:

Before (Vulnerable)

const DEFAULT_HEADERS = {
    "user-agent": "GreedySearch/1.0",
    accept: "application/vnd.github+json",
    "x-github-api-version": "2022-11-28",
};

After (Fixed)

const DEFAULT_HEADERS = {
    "user-agent": "GreedySearch/1.0",
    accept: "application/vnd.github+json",
    "x-github-api-version": "2022-11-28",
    ...(process.env.GITHUB_TOKEN
        ? { authorization: `Bearer ${process.env.GITHUB_TOKEN}` }
        : {}),
};

How This Solves the Problem

The fix uses a ternary operator with object spreading to conditionally inject the authorization header:

  • Check: process.env.GITHUB_TOKEN — does the environment variable exist?
  • If yes: Spread { authorization:Bearer ${process.env.GITHUB_TOKEN}} into the headers object
  • If no: Spread an empty object {}, leaving headers unchanged

This pattern is elegant because:

  1. No code path changes — The application works identically whether the token is present or not
  2. Secure credential handling — The token comes from environment variables, not hardcoded strings or configuration files
  3. Standard authentication format — Uses the Bearer token scheme that GitHub's API expects
  4. Graceful degradation — If the environment variable isn't set, the application still works (but with rate limiting)

The Security Improvement

With this fix deployed:

  • Deployments that set GITHUB_TOKEN in their environment automatically authenticate with GitHub
  • Authenticated requests jump from 60 requests/hour to 5,000 requests/hour
  • The application gains resilience against rate-limit-based denial of service
  • No hardcoded credentials are embedded in the source code
  • The fix is backward compatible—systems without the token still function

Prevention & Best Practices

1. Always Use Environment Variables for Credentials

Never hardcode API tokens, API keys, or authentication credentials in source files:

// ❌ WRONG
const GITHUB_TOKEN = "ghp_1234567890abcdefghijklmnopqrstuvwxyz";
const DEFAULT_HEADERS = {
    authorization: `Bearer ${GITHUB_TOKEN}`
};

// ✅ RIGHT
const DEFAULT_HEADERS = {
    ...(process.env.GITHUB_TOKEN
        ? { authorization: `Bearer ${process.env.GITHUB_TOKEN}` }
        : {}),
};

2. Validate Authentication at Application Startup

For critical integrations, verify that required credentials are present before the application fully initializes:

function validateGitHubAuth() {
    if (!process.env.GITHUB_TOKEN) {
        console.warn('GITHUB_TOKEN not set. API requests will be rate-limited to 60/hour.');
    }
}

3. Use Conditional Header Injection

The conditional spread pattern (...(condition ? {...} : {})) is a proven way to inject optional headers without code duplication.

4. Document Authentication Requirements

Update your README and deployment guides to clarify:
- Which credentials are required
- Where to obtain them (e.g., GitHub personal access tokens)
- How to set environment variables in different deployment contexts

5. Leverage Static Analysis Tools

Use static analysis to detect missing authentication patterns:

  • ESLint with security plugins can flag code that makes API calls without token injection
  • Semgrep rules can detect when external API integrations lack credential handling
  • SonarQube can identify missing authentication in third-party API calls

Key Takeaways

  • Unauthenticated API requests silently degrade service: The DEFAULT_HEADERS in src/github.mjs had no auth mechanism, dropping your rate limit from 5,000 to 60 requests/hour—an 83x reduction.

  • Conditional object spreading securely injects credentials: The fix uses JavaScript's ...(condition ? {...} : {}) pattern to inject Bearer tokens from environment variables only when available.

  • Environment variables are the secure default: Never hardcode API credentials; always flow them from process.env at runtime.

  • Test both authenticated and unauthenticated code paths: Verify your application degrades gracefully when credentials are missing, but functions optimally when they're provided.

  • API rate limits are often a symptom, not a root cause: If your application hits rate limits unexpectedly, check whether authentication is actually being used.

How Orbis AppSec Detected This

Source: GitHub API integration code in src/github.mjs that constructs HTTP requests without credential injection

Sink: The DEFAULT_HEADERS object initialization, which hardcodes static headers without any mechanism to inject authentication tokens from environment variables

Missing control: Conditional header injection logic that would pull GITHUB_TOKEN from the environment and add it to the Authorization header

CWE: CWE-306: Missing Authentication for Critical Function

Fix: Added conditional object spreading (...(process.env.GITHUB_TOKEN ? { authorization:Bearer ${process.env.GITHUB_TOKEN}} : {})) to inject the Bearer token into DEFAULT_HEADERS when the environment variable is set.

Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.

Conclusion

API authentication isn't glamorous, but it's fundamental. The fix to src/github.mjs demonstrates that even small oversights—a missing conditional, a forgotten environment variable—can create measurable operational and security consequences.

By adopting the pattern shown here—conditional header injection from environment variables—you protect your applications from rate limiting, ensure resilience under load, and maintain secure separation between code and credentials.

The lesson applies beyond GitHub: any external API integration should flow authentication credentials from secure sources into request headers dynamically, never statically. With modern JavaScript, this is trivial to implement. Make it a standard practice.


References

Frequently Asked Questions

What is missing API authentication?

Missing API authentication occurs when an application integrates with external APIs but fails to provide authentication credentials, forcing requests into unauthenticated rate-limited tiers or exposing functionality to unauthorized access.

How do you prevent missing authentication in Node.js?

Always inject authentication tokens from secure sources (environment variables, credential managers) into API request headers, validate that tokens are present before making requests, and test both authenticated and unauthenticated code paths.

What CWE is missing API authentication?

CWE-306 (Missing Authentication for Critical Function) covers scenarios where authentication is not required or enforced for critical operations or integrations.

Is rate limiting enough to prevent missing authentication issues?

No. While rate limiting provides some protection, relying on it masks the underlying authentication problem. Proper authentication enables higher legitimate throughput and prevents service degradation.

Can static analysis detect missing API authentication?

Yes. Static analysis can identify API calls without corresponding authentication headers, detect environment variable usage patterns, and flag missing token injection logic through taint tracking.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #60

Related Articles

critical

How Authentication Bypass Happens in Node.js WebSocket Services and How to Fix It

The HousePanel push notification service exposed GET and POST endpoints without any authentication checks, allowing unauthenticated attackers to send arbitrary push notifications to connected smart devices. This critical vulnerability was fixed by implementing mandatory token validation on all protected endpoints, ensuring only authenticated requests can trigger push operations.

high

How OAuth 2.0 Authorization Code Interception happens in PHP and how to fix it

The Weibo OAuth login implementation in `trunk/web/login_weibo.php` was missing PKCE (Proof Key for Code Exchange), allowing attackers with network access to exchange intercepted authorization codes for access tokens. The fix adds cryptographic binding between the authorization request and token exchange using SHA256 code challenges.

high

How OAuth Token Binding Prevents Session Hijacking in Weibo Login Implementation

A critical vulnerability in the Weibo OAuth login implementation allowed attackers to replay stolen access tokens across different user sessions. By binding the OAuth access token to the session ID using cryptographic hashing, the fix ensures that intercepted tokens cannot be reused to hijack other sessions, even if compromised via MITM or XSS attacks.

high

How Unauthenticated Endpoint Exposure Happens in Node.js and How to Fix It

A high-severity unauthenticated endpoint exposure was discovered in `dep/src/server/index.js`, where the `/--ziko--` route served internal application state (`globalThis.Ziko`) to any network-connected client without any authentication or environment guard. The fix adds a single production environment check that returns a `404` before the sensitive data is ever sent. This kind of "debug route left in production" vulnerability is surprisingly common in Node.js applications and can silently leak c

high

How Missing Authentication on Sensitive Endpoints Happens in Node.js Express APIs and How to Fix It

Four critical endpoints in the Everclaw Key API — `/bootstrap/challenge`, `/bootstrap`, `/verify-xpost`, and `/forget` — lacked authentication checks, allowing any unauthenticated attacker to request bootstrap funds, claim codes, and even trigger GDPR data deletion. The fix adds `x-admin-secret` header validation to each endpoint, matching the pattern already used on the `/api/stats` route.

critical

How SQL Injection happens in PHP bulk email systems and how to fix it

A critical SQL injection vulnerability in `admin/utilities/bulkEmailSystem.php` allowed attackers to inject arbitrary SQL through unvalidated database names passed from user input. The fix implements strict input validation using regex pattern matching to ensure only safe database identifiers are processed, preventing exploitation of the bulk email functionality.