Deepen PracticeOrdered learning track

Dependency and SDK Security

Learn React Authentication, Authorization, Identity & Permission/ACL - Part 087

Dependency dan Auth SDK security untuk React authenticated apps: supply-chain boundary, third-party JavaScript, SDK trust, dependency pinning, SBOM, SCA, CSP/SRI, vendor risk, update strategy, dan incident response.

15 min read2851 words
PrevNext
Lesson 87130 lesson track72–107 Deepen Practice
#react#authentication#authorization#identity+5 more

Part 087 — Dependency and SDK Security

React auth sering dianggap selesai setelah kita memilih provider dan memasang SDK:

npm install @auth/provider-react

Itu terlalu dangkal.

Dalam authenticated React app, dependency dan SDK bukan sekadar library. Mereka menjadi bagian dari trusted computing base aplikasi. Auth SDK bisa melihat redirect callback, session state, token response, user profile, organization context, feature entitlement, dan kadang melakukan silent refresh. UI component library bisa menjalankan JavaScript di halaman authenticated. Analytics SDK bisa menerima route, user id, email, organization, dan error payload. Markdown renderer bisa mengubah content user menjadi DOM.

Mental model utama:

Every dependency that executes in the browser is code running inside your user's authenticated origin.

Jika dependency itu compromised, buggy, over-permissive, atau salah dikonfigurasi, risiko bukan hanya UI crash. Risikonya bisa berupa session theft, token exfiltration, policy bypass, open redirect, data exposure, clickjacking helper, telemetry leak, dan persistence di browser cache.


1. Dependency security di React auth berbeda dari backend dependency security

Backend dependency biasanya hidup di server boundary. Ia bisa mengakses secrets server, database, token vault, dan internal network. Frontend dependency hidup di browser boundary. Ia tidak punya server secret, tetapi punya sesuatu yang sangat sensitif:

  • authenticated DOM
  • cookies yang otomatis ikut request
  • in-memory access token jika app memakai bearer token
  • route state
  • user profile projection
  • permission projection
  • query cache
  • local/session storage
  • IndexedDB
  • service worker cache
  • analytics context
  • clipboard interactions
  • file input/output
  • WebSocket/SSE connection

Jadi pertanyaannya bukan hanya:

Is this package vulnerable?

Tetapi:

What can this package observe, mutate, intercept, persist, or exfiltrate inside our authenticated origin?

2. Trust boundary map

Pink nodes are not automatically bad. They are high-trust nodes. They need explicit review.

A dependency becomes auth-sensitive when it can do at least one of these:

CapabilityExampleAuth risk
Read route/URLAnalytics SDKPII/return URL/token leakage
Read DOMSupport replay widgetSensitive data capture
Execute scriptThird-party widgetXSS-equivalent control
Intercept networkAPI client, service worker helperToken/request leakage
Persist stateCache/persistence libraryData remains after logout
Render untrusted inputMarkdown/rich textDOM XSS
Control redirectAuth SDK/router helperOpen redirect/session confusion
Control feature accessFlag SDKFeature flag mistaken as permission
Decode/parse tokenJWT helperConfused validation/authorization
Manage sessionAuth provider SDKRefresh/logout/callback bugs

3. Dependency classes in authenticated React apps

Not every dependency needs the same review. Classify dependencies by security impact.

3.1 Auth-critical dependencies

Examples:

  • Auth SDK
  • OIDC/OAuth client
  • JWT/JWK helper
  • WebAuthn/passkey helper
  • cookie/session helper
  • CSRF helper
  • BFF client

Failure impact:

  • broken login/callback
  • token stored in unsafe location
  • missing PKCE/state/nonce
  • wrong token audience
  • incorrect refresh rotation behavior
  • logout not clearing session
  • open redirect
  • tenant/account linking bug

Review level: highest.

3.2 Data-boundary dependencies

Examples:

  • TanStack Query
  • Apollo Client/urql/Relay
  • Axios/fetch wrapper
  • persisted cache library
  • WebSocket/SSE helper
  • file upload SDK

Failure impact:

  • stale authenticated data after logout
  • request replay after refresh failure
  • cache crossing tenant/user boundary
  • sensitive data persistence
  • optimistic UI masking authorization denial

Review level: high.

3.3 DOM/rendering dependencies

Examples:

  • markdown renderer
  • rich text editor
  • syntax highlighter
  • charting library
  • HTML sanitizer
  • design system components

Failure impact:

  • XSS
  • hidden data leak
  • unsafe link target
  • SVG/script injection
  • CSS/data URL abuse

Review level: high when rendering user-controlled or admin-configurable content.

3.4 Third-party runtime dependencies

Examples:

  • analytics
  • error monitoring
  • support chat
  • session replay
  • A/B testing
  • feature flag client
  • fraud/bot detection
  • payment widget

Failure impact:

  • PII exfiltration
  • session replay capture
  • sensitive route/URL capture
  • DOM observation
  • supply-chain script compromise
  • user identification outside policy

Review level: high + privacy review.

3.5 Build-time dependencies

Examples:

  • Vite/Webpack/Rspack plugin
  • Babel/SWC plugin
  • ESLint plugin
  • codegen tool
  • MDX plugin
  • test runner plugin

Failure impact:

  • malicious build artifact
  • credential exfiltration from CI
  • source tampering
  • hidden injected code
  • supply-chain persistence

Review level: high in CI/CD threat model.


4. Auth SDK is an integration boundary, not your architecture

A common mistake:

<AuthProvider>
  <App />
</AuthProvider>

Then the whole auth architecture becomes whatever the SDK exposes.

Top-tier architecture instead builds a thin adapter:

Auth Provider SDK
        ↓
Provider Adapter
        ↓
Internal Auth Client Contract
        ↓
React Auth Boundary / Router / API Client / Permission UI

Why?

Because your application needs stable internal semantics:

  • anonymous
  • authenticated
  • refreshing
  • expired
  • revoked
  • forbidden
  • requires_step_up
  • tenant_mismatch
  • degraded

A provider SDK may use different naming, different cache behavior, and different lifecycle assumptions. Do not let that leak everywhere.

4.1 Bad pattern: SDK everywhere

import { useAuth0 } from '@auth0/auth0-react';
import { useUser } from '@clerk/clerk-react';
import { useAuthenticator } from '@aws-amplify/ui-react';

function DeleteCaseButton({ caseId }: { caseId: string }) {
  const { user } = useAuth0();

  if (user?.role !== 'admin') return null;

  return <button>Delete</button>;
}

Problems:

  • component knows provider-specific API
  • role check is app-level authorization leakage
  • migration becomes expensive
  • test fixtures need provider mocks
  • token/session semantics are hidden
  • no consistent decision reason
  • no tenant-aware permission contract

4.2 Better pattern: internal contract

export type AuthStatus =
  | 'unknown'
  | 'anonymous'
  | 'authenticating'
  | 'authenticated'
  | 'refreshing'
  | 'expired'
  | 'revoked'
  | 'degraded';

export type SessionProjection = {
  status: AuthStatus;
  subject?: {
    id: string;
    displayName: string;
    email?: string;
  };
  tenant?: {
    id: string;
    slug: string;
  };
  assurance?: {
    level: 'low' | 'medium' | 'high';
    authenticatedAt?: string;
    methods?: string[];
  };
  authEpoch: number;
  permissionEpoch?: number;
};

export interface AuthClient {
  getSession(): Promise<SessionProjection>;
  subscribe(listener: (session: SessionProjection) => void): () => void;
  login(options?: { returnTo?: string; tenantHint?: string }): Promise<void>;
  logout(options?: { everywhere?: boolean }): Promise<void>;
  refresh(reason: 'startup' | 'expiry' | 'focus' | 'manual'): Promise<SessionProjection>;
}

React app imports AuthClient, not SDK-specific internals.


5. SDK review checklist

Before adopting an auth SDK, answer these questions.

5.1 Protocol correctness

  • Does it support Authorization Code with PKCE?
  • Does it avoid Implicit Flow for browser apps?
  • Does it bind state to the login transaction?
  • Does it support OIDC nonce when ID Token is involved?
  • Does it validate token audience/issuer where appropriate?
  • Does it clearly distinguish ID Token, Access Token, and Refresh Token?
  • Does it support refresh token rotation semantics safely?
  • Does it support logout and session revocation semantics?
  • Does it prevent open redirect via returnTo/callback configuration?

5.2 Storage behavior

Find exactly where the SDK stores data.

Do not accept “secure storage” as an answer.

Ask:

  • Does it store tokens in memory?
  • Does it store tokens in localStorage?
  • Does it store transaction state in sessionStorage?
  • Does it use cookies?
  • Are cookies Secure, HttpOnly, SameSite, and scoped correctly?
  • Does it persist user profile or claims?
  • Does it clear state on logout?
  • Does it support multi-tab logout propagation?
  • Can storage mode be configured?

5.3 Runtime behavior

  • Does it refresh tokens automatically?
  • Does it coordinate refresh across tabs?
  • What happens when refresh fails?
  • Does it retry failed requests?
  • Does it create hidden iframes?
  • Does it depend on third-party cookies?
  • Does it write to URL/history during callback?
  • Does it expose tokens to application code?
  • Does it emit typed lifecycle events?

5.4 Operational behavior

  • Is there a changelog?
  • Are security advisories published?
  • Is the package actively maintained?
  • Are releases signed/provenanced?
  • Does it have a clear support policy?
  • Does it document breaking changes?
  • Does it support your SSR/BFF/runtime model?
  • Does it support your browser support matrix?

6. Dependency pinning and update strategy

Security maturity is not “always update everything immediately”. That can break auth in production.

Security maturity is:

reproducible builds + monitored vulnerabilities + controlled rollout + fast emergency patch path

6.1 Lockfiles are mandatory

For npm/pnpm/yarn apps:

  • commit lockfile
  • enforce frozen install in CI
  • do not run floating install in release builds
  • avoid unreviewed lockfile churn
  • diff transitive dependency changes

Example CI guard:

pnpm install --frozen-lockfile
pnpm audit --prod
pnpm test
pnpm build

6.2 Pin high-risk dependencies more strictly

High-risk dependencies:

  • auth SDK
  • router
  • query/data library
  • markdown/rich text renderer
  • sanitizer
  • analytics/session replay SDK
  • build plugins

For these, prefer controlled update PRs:

{
  "dependencies": {
    "@auth/provider-react": "1.14.3",
    "dompurify": "3.2.4",
    "@tanstack/react-query": "5.90.1"
  }
}

Do not blindly use loose ranges for auth-critical packages:

{
  "dependencies": {
    "@auth/provider-react": "^1.0.0"
  }
}

The lockfile may freeze today, but broad ranges still increase accidental drift when lockfile is regenerated.

6.3 Use update lanes

A useful policy:

LaneScopeCadenceRequired checks
Emergency security patchCritical/high CVE, exploited issueimmediatefocused tests + staged rollout
Auth-critical updateAuth SDK, sanitizer, router, query clientweekly/monthlyfull auth regression suite
Routine dependency updatenormal librariesweekly/monthlystandard CI
Major version updatebreaking changesplannedADR + migration tests
Vendor SDK replacementarchitecture boundaryprojectdesign review + incident plan

7. Software Composition Analysis and SBOM

For serious systems, npm audit is not enough. It is useful, but incomplete.

You need a pipeline that can answer:

Which deployed artifact contains vulnerable package X?
Which user-facing auth surface does it affect?
Which environment is affected?
Can we disable the risky feature while patching?

7.1 Minimum SCA pipeline

7.2 SBOM fields that matter

For each frontend artifact, track:

  • package name
  • version
  • package manager
  • direct/transitive dependency
  • license
  • package URL/purl
  • source repository
  • integrity/hash
  • vulnerability mapping
  • artifact/build id
  • deployment environment
  • affected route/app/module if possible

7.3 Vulnerability triage questions

When a vulnerability alert appears:

  1. Is the vulnerable code included in the shipped browser bundle?
  2. Is the vulnerable function reachable?
  3. Is the route authenticated or public?
  4. Does the route process user-controlled input?
  5. Does the dependency run in browser, server, edge, or CI?
  6. Can CSP or feature kill switch reduce exposure?
  7. Is there a patched version?
  8. Does the patch change auth/session behavior?
  9. What regression suite must run before release?
  10. Do we need user/session revocation after patch?

8. Third-party JavaScript in authenticated pages

Third-party JavaScript deserves special treatment because it executes with your origin privileges when embedded as normal script.

Examples:

<script src="https://analytics.vendor.example/sdk.js"></script>
<script src="https://chat.vendor.example/widget.js"></script>
<script src="https://flags.vendor.example/client.js"></script>

If a vendor script is compromised, it can often:

  • read the DOM
  • observe route and URL
  • register event listeners
  • capture form inputs
  • exfiltrate data through network calls
  • mutate UI
  • trigger authenticated actions if CSRF/XSS defenses allow it
  • inspect non-HttpOnly storage

8.1 Preferred mitigations

RiskMitigation
Script compromiseavoid loading on sensitive routes
DOM observationreduce rendered data; mask sensitive fields
Network exfiltrationstrict connect-src CSP
Script injectionstrict CSP nonce/hash
Unexpected script changeSRI where feasible for static scripts
Vendor overcollectionexplicit data allowlist
Session replay leakprivacy masking and route exclusions
Auth callback exposurenever load third-party scripts on callback/logout/error routes

8.2 Route-specific vendor policy

Do not use one global rule.

type VendorPolicy = {
  analytics: boolean;
  sessionReplay: boolean;
  supportChat: boolean;
  featureFlagClient: boolean;
};

export function vendorPolicyForRoute(routeId: string): VendorPolicy {
  if (routeId.startsWith('auth.callback')) {
    return {
      analytics: false,
      sessionReplay: false,
      supportChat: false,
      featureFlagClient: false,
    };
  }

  if (routeId.startsWith('admin.audit') || routeId.startsWith('case.evidence')) {
    return {
      analytics: true,
      sessionReplay: false,
      supportChat: false,
      featureFlagClient: true,
    };
  }

  return {
    analytics: true,
    sessionReplay: false,
    supportChat: true,
    featureFlagClient: true,
  };
}

8.3 Never load third-party scripts on auth callback route

Auth callback routes may contain:

  • authorization code
  • state
  • error details
  • tenant hint
  • return path
  • correlation id

Even if the authorization code is short-lived and one-time-use, it does not belong in analytics/session replay/referrer logs.


9. Subresource Integrity is useful but limited

SRI lets the browser verify that a fetched script/stylesheet matches an expected hash.

Example:

<script
  src="https://cdn.example.com/sdk/v1.2.3/sdk.min.js"
  integrity="sha384-..."
  crossorigin="anonymous"
></script>

SRI helps when:

  • script URL is static
  • content hash is known at build/deploy time
  • vendor does not change the file under the same URL

SRI does not solve:

  • malicious dependency installed into your bundle
  • compromised npm package included at build time
  • dynamic script loader that fetches more scripts
  • allowed script that is intentionally overprivileged
  • auth SDK logic bugs
  • unsafe configuration

Use SRI where it fits. Do not treat it as supply-chain security by itself.


10. CSP as dependency blast-radius limiter

CSP is not a dependency scanner. It is a runtime blast-radius limiter.

A dependency-aware CSP asks:

Which scripts can execute?
Which origins can receive data?
Which frames can be embedded?
Which images/fonts/styles are allowed?
Which routes need stricter policy?

Example baseline for authenticated shell:

Content-Security-Policy:
  default-src 'self';
  script-src 'self' 'nonce-{requestNonce}' 'strict-dynamic';
  object-src 'none';
  base-uri 'none';
  frame-ancestors 'none';
  connect-src 'self' https://api.example.com https://telemetry.example.com;
  img-src 'self' data: https://cdn.example.com;
  style-src 'self' 'nonce-{requestNonce}';
  report-to csp-endpoint;

For auth callback:

Content-Security-Policy:
  default-src 'self';
  script-src 'self' 'nonce-{requestNonce}';
  object-src 'none';
  base-uri 'none';
  frame-ancestors 'none';
  connect-src 'self';
  form-action 'self';
  report-to csp-endpoint;

The callback route should not need analytics, support chat, session replay, or arbitrary vendor connections.


11. Auth SDK token exposure review

Some SDKs expose token getters:

const token = await authClient.getAccessTokenSilently();

That is not inherently wrong. But once the app can get a bearer token in JavaScript, any XSS or compromised dependency in the same origin may be able to get it too.

Ask:

  • Do we need raw access token in React?
  • Can BFF hide token from browser?
  • Can the token be audience-limited?
  • Can the token be short-lived?
  • Can refresh token stay server-side?
  • Does SDK support memory-only token cache?
  • Does SDK ever write token to local storage?
  • Does the app log token accidentally?
  • Does error monitoring scrub Authorization headers?

Safe wrapper:

export interface ApiCredentialProvider {
  applyAuth(input: RequestInit): Promise<RequestInit>;
}

export class BrowserBearerCredentialProvider implements ApiCredentialProvider {
  constructor(private readonly auth: AuthTokenSource) {}

  async applyAuth(input: RequestInit): Promise<RequestInit> {
    const token = await this.auth.getAccessToken({ audience: 'app-api' });

    return {
      ...input,
      headers: {
        ...input.headers,
        Authorization: `Bearer ${token}`,
      },
    };
  }
}

Do not pass token around arbitrary components.

Bad:

<UserProfile accessToken={token} />
<AnalyticsContext token={token} />

Better:

<UserProfile />

The profile component should call an API boundary that attaches credentials internally.


12. Dependency import boundary

Prevent high-risk APIs from being imported everywhere.

12.1 Folder boundary

src/
  auth/
    provider-adapter/
      auth0-adapter.ts
      clerk-adapter.ts
    auth-client.ts
    auth-context.tsx
  api/
    api-client.ts
  permissions/
    can.ts
  features/
    cases/
      components/

Feature components should import:

import { useSession } from '@/auth/auth-context';
import { useCan } from '@/permissions/use-can';
import { api } from '@/api/api-client';

They should not import:

import { useAuth0 } from '@auth0/auth0-react';
import { ClerkProvider, useUser } from '@clerk/clerk-react';
import jwtDecode from 'jwt-decode';

12.2 ESLint guardrail

// eslint.config.js
export default [
  {
    rules: {
      'no-restricted-imports': [
        'error',
        {
          paths: [
            {
              name: '@auth0/auth0-react',
              message: 'Use internal auth adapter from src/auth instead.',
            },
            {
              name: '@clerk/clerk-react',
              message: 'Use internal auth adapter from src/auth instead.',
            },
            {
              name: 'jwt-decode',
              message: 'Token decoding belongs in auth boundary only.',
            },
          ],
        },
      ],
    },
  },
];

12.3 Build-time import test

import { describe, expect, it } from 'vitest';
import { globby } from 'globby';
import fs from 'node:fs/promises';

const forbidden = [
  '@auth0/auth0-react',
  '@clerk/clerk-react',
  'jwt-decode',
];

describe('auth SDK imports', () => {
  it('are restricted to auth adapter boundary', async () => {
    const files = await globby(['src/**/*.ts', 'src/**/*.tsx']);

    const violations: string[] = [];

    for (const file of files) {
      if (file.includes('/src/auth/provider-adapter/')) continue;
      const content = await fs.readFile(file, 'utf8');

      for (const dep of forbidden) {
        if (content.includes(`from '${dep}'`) || content.includes(`from "${dep}"`)) {
          violations.push(`${file} imports ${dep}`);
        }
      }
    }

    expect(violations).toEqual([]);
  });
});

13. Analytics and error SDK hardening

Analytics/error SDK often becomes the silent data exfiltration layer.

13.1 Redaction policy

Define a redaction function before data leaves the browser.

const SENSITIVE_KEYS = [
  'authorization',
  'access_token',
  'refresh_token',
  'id_token',
  'password',
  'secret',
  'csrf',
  'session',
  'cookie',
  'ssn',
  'nationalId',
  'email',
];

export function redactTelemetry(value: unknown): unknown {
  if (Array.isArray(value)) return value.map(redactTelemetry);

  if (value && typeof value === 'object') {
    const result: Record<string, unknown> = {};

    for (const [key, child] of Object.entries(value)) {
      if (SENSITIVE_KEYS.some((k) => key.toLowerCase().includes(k))) {
        result[key] = '[REDACTED]';
      } else {
        result[key] = redactTelemetry(child);
      }
    }

    return result;
  }

  if (typeof value === 'string') {
    return value
      .replace(/Bearer\s+[A-Za-z0-9._~+/-]+=*/g, 'Bearer [REDACTED]')
      .replace(/(code|state|token)=([^&]+)/g, '$1=[REDACTED]');
  }

  return value;
}

13.2 Route exclusion

Exclude:

  • /auth/callback
  • /logout
  • /mfa/*
  • /admin/impersonation/*
  • /case/:id/evidence/*
  • /exports/*
  • any page with highly sensitive fields

13.3 Do not attach raw user object

Bad:

telemetry.identify(user.id, user);

Better:

telemetry.identify(user.id, {
  tenantId: session.tenant.id,
  roleCategory: session.subject.roleCategory,
  plan: session.entitlements.plan,
});

Even then, verify privacy policy and data residency.


14. Rich text, markdown, and sanitizer dependencies

A markdown renderer can become the route from stored content to XSS.

Bad pattern:

<div dangerouslySetInnerHTML={{ __html: markdownToHtml(caseNote.body) }} />

Better pattern:

const safeHtml = sanitizeHtml(markdownToHtml(caseNote.body), {
  allowedTags: ['p', 'strong', 'em', 'ul', 'ol', 'li', 'a', 'code', 'pre', 'blockquote'],
  allowedAttributes: {
    a: ['href', 'title', 'rel', 'target'],
  },
  allowedSchemes: ['http', 'https', 'mailto'],
});

return <div dangerouslySetInnerHTML={{ __html: safeHtml }} />;

But sanitizer choice and configuration are security-critical.

Review:

  • Does sanitizer handle SVG/MathML?
  • Are javascript: URLs blocked?
  • Are data URLs allowed?
  • Are event handlers removed?
  • Are dangerous CSS properties removed?
  • Does output run under Trusted Types policy?
  • Are links normalized and rel="noopener noreferrer" enforced?
  • Are sanitizer updates tracked like auth SDK updates?

15. Feature flag SDK is not permission SDK

A feature flag SDK may evaluate:

if (flags.enableCaseDeletion) {
  return <DeleteCaseButton />;
}

That is release control, not authorization.

Correct composition:

const deleteFlag = useFlag('case-delete-ui');
const decision = useCan({ action: 'case.delete', resource: { type: 'case', id: caseId } });

if (!deleteFlag.enabled) return null;

return (
  <AuthorizedButton
    decision={decision}
    onClick={deleteCase}
  >
    Delete case
  </AuthorizedButton>
);

Server still validates case.delete.


16. Package manager guardrails

16.1 Disallow install scripts where possible

Install scripts can execute code during dependency installation.

Policy:

  • disable scripts by default in CI if feasible
  • allowlist packages that require install scripts
  • review native build packages carefully
  • run install in isolated CI environment
  • do not expose production secrets to dependency install step

16.2 Enforce registry and provenance

Use:

  • internal registry/proxy
  • package provenance where supported
  • lockfile integrity checks
  • two-person review for dependency additions
  • automated dependency diff summary

16.3 Review package metadata

Before adding a dependency:

  • maintainer count
  • release history
  • repository activity
  • open security issues
  • package size
  • transitive dependencies
  • install scripts
  • permissions
  • typosquatting risk
  • license
  • funding/ownership changes

17. Dependency change review template

# Dependency Change Review

## Package
- Name:
- Version change:
- Direct or transitive:
- Runtime: browser / server / edge / CI
- Auth-sensitive: yes / no

## Why
- What capability is needed?
- Why not use existing dependency?
- Can this run server-side instead of browser-side?

## Browser access
- Can it read DOM?
- Can it read URL?
- Can it make network calls?
- Can it persist data?
- Can it render untrusted content?
- Can it access tokens/session/user profile/permissions?

## Security controls
- CSP change needed:
- SRI possible:
- Telemetry redaction needed:
- Route exclusions:
- Import boundary:
- Test coverage:

## Operational controls
- Changelog reviewed:
- Vulnerability status:
- License reviewed:
- Rollback plan:
- Kill switch:

18. Incident response: compromised dependency or auth SDK

When a dependency is compromised, do not only patch and redeploy.

18.1 First questions

  • Was the vulnerable version deployed?
  • Which environments?
  • Which users/routes were exposed?
  • Did it execute in authenticated pages?
  • Could it access token/session/user data?
  • Could it make network calls to attacker-controlled domains?
  • Did CSP block exfiltration?
  • Did telemetry show anomalous behavior?
  • Should sessions/tokens be revoked?
  • Should users be notified?

18.2 Response flow

18.3 Session revocation decision

Revoke sessions if:

  • dependency could read bearer tokens
  • dependency could read non-HttpOnly refresh token
  • dependency could trigger authenticated privileged actions
  • CSP/network controls were weak
  • compromise happened on sensitive authenticated routes
  • evidence is incomplete and impact is high

Session revocation is disruptive, but silent compromise is worse.


19. Production checklist

Before release:

  • Auth SDK isolated behind internal adapter.
  • Feature components do not import provider SDK directly.
  • Token getters are not passed through props/context unnecessarily.
  • Dependency lockfile is committed and frozen in CI.
  • SCA scan runs on dependency changes and release builds.
  • SBOM is generated for deployed artifacts.
  • Third-party scripts are route-scoped.
  • Auth callback/logout/MFA routes load no unnecessary third-party scripts.
  • CSP limits script and connect destinations.
  • Telemetry redaction covers tokens, cookies, auth codes, state, PII.
  • Session replay is disabled or masked on sensitive routes.
  • Markdown/rich-text sanitizer is reviewed and tested.
  • Feature flags are not used as authorization boundary.
  • Import restrictions prevent dependency sprawl.
  • Dependency update lanes exist.
  • Incident response runbook covers compromised frontend dependency.

20. Key takeaways

Dependency security in React auth is not a checklist afterthought. It is part of auth architecture.

A dependency is dangerous not because it is third-party, but because it may execute inside your authenticated origin with access to user context, route state, DOM, caches, telemetry, and sometimes tokens.

The practical target is not zero dependencies. The target is controlled trust:

minimize browser authority
isolate auth SDKs
constrain third-party scripts
pin and monitor dependencies
redact telemetry
test auth-sensitive update paths
prepare incident response

Top-tier React auth systems treat SDKs as replaceable adapters, not foundations. They treat dependencies as runtime actors with capabilities, not as passive code.


References

  • OWASP Software Supply Chain Security Cheat Sheet
  • OWASP Third Party JavaScript Management Cheat Sheet
  • OWASP Dependency-Track
  • OWASP Session Management Cheat Sheet
  • OWASP Authorization Cheat Sheet
  • OWASP Content Security Policy Cheat Sheet
  • RFC 9700 — Best Current Practice for OAuth 2.0 Security
  • MDN — Subresource Integrity
  • MDN — Content Security Policy
Lesson Recap

You just completed lesson 87 in deepen practice. Use the series map if you want to review the broader track, or continue directly into the next lesson while the context is still warm.

Continue The Track

Keep the momentum while the lesson is still fresh. Move backward for review or continue forward into the next concept.