runCodeMieSsoCliFlow function
Runs the CodeMie SSO login: starts the callback server, opens (or prints) the organization's login URL, waits for the token, and decodes it into CodeMieSsoCredentials. Returns null on cancel/timeout/failure.
Implementation
Future<CodeMieSsoCredentials?> runCodeMieSsoCliFlow({
required String codeMieUrl,
required void Function(String) onStatus,
Future<bool> Function(String) openBrowserFn = openBrowser,
}) async {
final server = CodeMieSsoCallbackServer();
final port = await server.start();
final ssoUrl = buildCodeMieSsoUrl(codeMieUrl, port);
onStatus('listening for the CodeMie SSO callback on port $port');
if (await openBrowserFn(ssoUrl)) {
onStatus('browser opened; complete the CodeMie sign-in');
} else {
onStatus('could not open browser automatically');
onStatus('open this URL manually: $ssoUrl');
}
final token = await () async {
// The CodeMie page often signs in BY ITSELF (existing browser session →
// instant redirect) — wait patiently with a live status line, and bail
// only after a generous timeout instead of hanging forever.
onStatus(
'waiting for the CodeMie SSO callback (the page may sign in by '
'itself)…',
);
const wait = Duration(minutes: 5);
final statusTimer = Timer.periodic(const Duration(seconds: 15), (t) {
onStatus('still waiting for the SSO callback… (${t.tick * 15}s)');
});
try {
return await server.waitForToken().timeout(wait, onTimeout: () => null);
} finally {
statusTimer.cancel();
}
}();
if (token == null) {
onStatus('no SSO callback received (timeout or cancelled)');
return null;
}
try {
final cookies = decodeCodeMieSsoToken(token);
final credentials = CodeMieSsoCredentials(
cookies: cookies,
apiUrl: codeMieApiBase(codeMieUrl),
expiresAt: deriveCodeMieExpiresAt(cookies),
);
if (credentials.authToken.isEmpty) {
onStatus('CodeMie SSO callback carried no cookies');
return null;
}
onStatus('CodeMie authorized');
return credentials;
} on FormatException catch (error) {
onStatus('CodeMie SSO failed: ${error.message}');
return null;
}
}