All files / src/oauth token-context.ts

100% Statements 22/22
100% Branches 2/2
100% Functions 7/7
100% Lines 22/22

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119                    22x                 22x                                             22x       17x                     22x 9x                       22x 2x 2x 1x       1x               22x 2x 2x               22x 2x 2x               22x 7x 7x               22x 5x    
/**
 * Token Context for OAuth
 *
 * Uses AsyncLocalStorage to provide per-request access to the authenticated
 * user's GitLab token without passing it through function parameters.
 *
 * This pattern allows existing code (like enhancedFetch) to automatically
 * use the correct token for the current request context.
 */
 
import { AsyncLocalStorage } from "async_hooks";
import { TokenContext } from "./types";
 
/**
 * AsyncLocalStorage instance for token context
 *
 * Stores the current request's token context, making it accessible
 * from any code running within the request without explicit passing.
 */
const asyncLocalStorage = new AsyncLocalStorage<TokenContext>();
 
/**
 * Run a function with a specific token context
 *
 * All code executed within the callback (including async operations)
 * will have access to the provided token context via getTokenContext().
 *
 * @param context - Token context for this request
 * @param fn - Function to execute with the context
 * @returns The return value of the function
 *
 * @example
 * ```typescript
 * await runWithTokenContext(
 *   { gitlabToken: 'xxx', gitlabUserId: 123, gitlabUsername: 'user', sessionId: 'abc' },
 *   async () => {
 *     // All code here can call getTokenContext() or getGitLabTokenFromContext()
 *     const projects = await listProjects();
 *   }
 * );
 * ```
 */
export function runWithTokenContext<T>(
  context: TokenContext,
  fn: () => T | Promise<T>
): T | Promise<T> {
  return asyncLocalStorage.run(context, fn);
}
 
/**
 * Get the current token context
 *
 * Returns undefined if called outside of a runWithTokenContext() callback.
 * Use this when you need to optionally use the token context.
 *
 * @returns The current token context, or undefined if not in OAuth mode
 */
export function getTokenContext(): TokenContext | undefined {
  return asyncLocalStorage.getStore();
}
 
/**
 * Get the GitLab token from the current context
 *
 * Throws an error if called outside of a runWithTokenContext() callback.
 * Use this in OAuth mode when a token is required.
 *
 * @returns The GitLab access token for the current request
 * @throws Error if no token context is available
 */
export function getGitLabTokenFromContext(): string {
  const context = asyncLocalStorage.getStore();
  if (!context) {
    throw new Error(
      "No OAuth token context available - this code must be called within an authenticated request"
    );
  }
  return context.gitlabToken;
}
 
/**
 * Get the GitLab user ID from the current context
 *
 * @returns The GitLab user ID, or undefined if not in OAuth context
 */
export function getGitLabUserIdFromContext(): number | undefined {
  const context = asyncLocalStorage.getStore();
  return context?.gitlabUserId;
}
 
/**
 * Get the GitLab username from the current context
 *
 * @returns The GitLab username, or undefined if not in OAuth context
 */
export function getGitLabUsernameFromContext(): string | undefined {
  const context = asyncLocalStorage.getStore();
  return context?.gitlabUsername;
}
 
/**
 * Get the session ID from the current context
 *
 * @returns The session ID, or undefined if not in OAuth context
 */
export function getSessionIdFromContext(): string | undefined {
  const context = asyncLocalStorage.getStore();
  return context?.sessionId;
}
 
/**
 * Check if we're currently in an OAuth context
 *
 * @returns true if a token context is available
 */
export function isInOAuthContext(): boolean {
  return asyncLocalStorage.getStore() !== undefined;
}