MCPcopy
hub / github.com/coder/mux / startDesktopFlow

Method startDesktopFlow

src/node/services/codexOauthService.ts:121–199  ·  view source on GitHub ↗
()

Source from the content-addressed store, hash-verified

119 }
120
121 async startDesktopFlow(): Promise<Result<{ flowId: string; authorizeUrl: string }, string>> {
122 const flowId = randomBase64Url();
123
124 const codeVerifier = randomBase64Url();
125 const codeChallenge = sha256Base64Url(codeVerifier);
126 const redirectUri = CODEX_OAUTH_BROWSER_REDIRECT_URI;
127
128 let loopback: Awaited<ReturnType<typeof startLoopbackServer>>;
129 try {
130 loopback = await startLoopbackServer({
131 port: 1455,
132 host: "localhost",
133 callbackPath: "/auth/callback",
134 validateLoopback: true,
135 expectedState: flowId,
136 deferSuccessResponse: true,
137 });
138 } catch (error) {
139 const message = getErrorMessage(error);
140 return Err(`Failed to start OAuth callback listener: ${message}`);
141 }
142
143 const resultDeferred = createDeferred<Result<void, string>>();
144
145 this.desktopFlows.register(flowId, {
146 server: loopback.server,
147 resultDeferred,
148 // Keep server-side timeout tied to flow lifetime so abandoned flows
149 // (e.g. callers that never invoke waitForDesktopFlow) still self-clean.
150 timeoutHandle: setTimeout(() => {
151 void this.desktopFlows.finish(flowId, Err("Timed out waiting for OAuth callback"));
152 }, DEFAULT_DESKTOP_TIMEOUT_MS),
153 });
154
155 const authorizeUrl = buildCodexAuthorizeUrl({
156 redirectUri,
157 state: flowId,
158 codeChallenge,
159 });
160
161 // Background task: wait for the loopback callback, exchange code for tokens,
162 // then finish the flow. Races against resultDeferred (which resolves on
163 // cancel/timeout) so the task exits cleanly if the flow is cancelled.
164 void (async () => {
165 const callbackResult = await Promise.race([
166 loopback.result,
167 resultDeferred.promise.then(() => null),
168 ]);
169
170 // null means the flow was finished externally (cancel/timeout).
171 if (!callbackResult) return;
172
173 if (!callbackResult.success) {
174 await this.desktopFlows.finish(flowId, Err(callbackResult.error));
175 return;
176 }
177
178 const exchangeResult = await this.handleDesktopCallbackAndExchange({

Callers

nothing calls this directly

Calls 12

startLoopbackServerFunction · 0.90
getErrorMessageFunction · 0.90
ErrFunction · 0.90
createDeferredFunction · 0.90
buildCodexAuthorizeUrlFunction · 0.90
OkFunction · 0.90
randomBase64UrlFunction · 0.85
sha256Base64UrlFunction · 0.85
debugMethod · 0.80
registerMethod · 0.45
finishMethod · 0.45

Tested by

no test coverage detected