Merged via squash. Prepared head SHA: 42b5595edec71897b479b3bbaa94bcb4ac6fab17 Co-authored-by: sallyom <11166065+sallyom@users.noreply.github.com> Co-authored-by: BunsDev <68980965+BunsDev@users.noreply.github.com> Reviewed-by: @BunsDev
141 lines
5.6 KiB
TypeScript
141 lines
5.6 KiB
TypeScript
export const ConnectErrorDetailCodes = {
|
|
AUTH_REQUIRED: "AUTH_REQUIRED",
|
|
AUTH_UNAUTHORIZED: "AUTH_UNAUTHORIZED",
|
|
AUTH_TOKEN_MISSING: "AUTH_TOKEN_MISSING",
|
|
AUTH_TOKEN_MISMATCH: "AUTH_TOKEN_MISMATCH",
|
|
AUTH_TOKEN_NOT_CONFIGURED: "AUTH_TOKEN_NOT_CONFIGURED",
|
|
AUTH_PASSWORD_MISSING: "AUTH_PASSWORD_MISSING", // pragma: allowlist secret
|
|
AUTH_PASSWORD_MISMATCH: "AUTH_PASSWORD_MISMATCH", // pragma: allowlist secret
|
|
AUTH_PASSWORD_NOT_CONFIGURED: "AUTH_PASSWORD_NOT_CONFIGURED", // pragma: allowlist secret
|
|
AUTH_BOOTSTRAP_TOKEN_INVALID: "AUTH_BOOTSTRAP_TOKEN_INVALID",
|
|
AUTH_DEVICE_TOKEN_MISMATCH: "AUTH_DEVICE_TOKEN_MISMATCH",
|
|
AUTH_RATE_LIMITED: "AUTH_RATE_LIMITED",
|
|
AUTH_TAILSCALE_IDENTITY_MISSING: "AUTH_TAILSCALE_IDENTITY_MISSING",
|
|
AUTH_TAILSCALE_PROXY_MISSING: "AUTH_TAILSCALE_PROXY_MISSING",
|
|
AUTH_TAILSCALE_WHOIS_FAILED: "AUTH_TAILSCALE_WHOIS_FAILED",
|
|
AUTH_TAILSCALE_IDENTITY_MISMATCH: "AUTH_TAILSCALE_IDENTITY_MISMATCH",
|
|
CONTROL_UI_ORIGIN_NOT_ALLOWED: "CONTROL_UI_ORIGIN_NOT_ALLOWED",
|
|
CONTROL_UI_DEVICE_IDENTITY_REQUIRED: "CONTROL_UI_DEVICE_IDENTITY_REQUIRED",
|
|
DEVICE_IDENTITY_REQUIRED: "DEVICE_IDENTITY_REQUIRED",
|
|
DEVICE_AUTH_INVALID: "DEVICE_AUTH_INVALID",
|
|
DEVICE_AUTH_DEVICE_ID_MISMATCH: "DEVICE_AUTH_DEVICE_ID_MISMATCH",
|
|
DEVICE_AUTH_SIGNATURE_EXPIRED: "DEVICE_AUTH_SIGNATURE_EXPIRED",
|
|
DEVICE_AUTH_NONCE_REQUIRED: "DEVICE_AUTH_NONCE_REQUIRED",
|
|
DEVICE_AUTH_NONCE_MISMATCH: "DEVICE_AUTH_NONCE_MISMATCH",
|
|
DEVICE_AUTH_SIGNATURE_INVALID: "DEVICE_AUTH_SIGNATURE_INVALID",
|
|
DEVICE_AUTH_PUBLIC_KEY_INVALID: "DEVICE_AUTH_PUBLIC_KEY_INVALID",
|
|
PAIRING_REQUIRED: "PAIRING_REQUIRED",
|
|
} as const;
|
|
|
|
export type ConnectErrorDetailCode =
|
|
(typeof ConnectErrorDetailCodes)[keyof typeof ConnectErrorDetailCodes];
|
|
|
|
export type ConnectRecoveryNextStep =
|
|
| "retry_with_device_token"
|
|
| "update_auth_configuration"
|
|
| "update_auth_credentials"
|
|
| "wait_then_retry"
|
|
| "review_auth_configuration";
|
|
|
|
export type ConnectErrorRecoveryAdvice = {
|
|
canRetryWithDeviceToken?: boolean;
|
|
recommendedNextStep?: ConnectRecoveryNextStep;
|
|
};
|
|
|
|
const CONNECT_RECOVERY_NEXT_STEP_VALUES: ReadonlySet<ConnectRecoveryNextStep> = new Set([
|
|
"retry_with_device_token",
|
|
"update_auth_configuration",
|
|
"update_auth_credentials",
|
|
"wait_then_retry",
|
|
"review_auth_configuration",
|
|
]);
|
|
|
|
export function resolveAuthConnectErrorDetailCode(
|
|
reason: string | undefined,
|
|
): ConnectErrorDetailCode {
|
|
switch (reason) {
|
|
case "token_missing":
|
|
return ConnectErrorDetailCodes.AUTH_TOKEN_MISSING;
|
|
case "token_mismatch":
|
|
return ConnectErrorDetailCodes.AUTH_TOKEN_MISMATCH;
|
|
case "token_missing_config":
|
|
return ConnectErrorDetailCodes.AUTH_TOKEN_NOT_CONFIGURED;
|
|
case "password_missing":
|
|
return ConnectErrorDetailCodes.AUTH_PASSWORD_MISSING;
|
|
case "password_mismatch":
|
|
return ConnectErrorDetailCodes.AUTH_PASSWORD_MISMATCH;
|
|
case "password_missing_config":
|
|
return ConnectErrorDetailCodes.AUTH_PASSWORD_NOT_CONFIGURED;
|
|
case "bootstrap_token_invalid":
|
|
return ConnectErrorDetailCodes.AUTH_BOOTSTRAP_TOKEN_INVALID;
|
|
case "tailscale_user_missing":
|
|
return ConnectErrorDetailCodes.AUTH_TAILSCALE_IDENTITY_MISSING;
|
|
case "tailscale_proxy_missing":
|
|
return ConnectErrorDetailCodes.AUTH_TAILSCALE_PROXY_MISSING;
|
|
case "tailscale_whois_failed":
|
|
return ConnectErrorDetailCodes.AUTH_TAILSCALE_WHOIS_FAILED;
|
|
case "tailscale_user_mismatch":
|
|
return ConnectErrorDetailCodes.AUTH_TAILSCALE_IDENTITY_MISMATCH;
|
|
case "rate_limited":
|
|
return ConnectErrorDetailCodes.AUTH_RATE_LIMITED;
|
|
case "device_token_mismatch":
|
|
return ConnectErrorDetailCodes.AUTH_DEVICE_TOKEN_MISMATCH;
|
|
case undefined:
|
|
return ConnectErrorDetailCodes.AUTH_REQUIRED;
|
|
default:
|
|
return ConnectErrorDetailCodes.AUTH_UNAUTHORIZED;
|
|
}
|
|
}
|
|
|
|
export function resolveDeviceAuthConnectErrorDetailCode(
|
|
reason: string | undefined,
|
|
): ConnectErrorDetailCode {
|
|
switch (reason) {
|
|
case "device-id-mismatch":
|
|
return ConnectErrorDetailCodes.DEVICE_AUTH_DEVICE_ID_MISMATCH;
|
|
case "device-signature-stale":
|
|
return ConnectErrorDetailCodes.DEVICE_AUTH_SIGNATURE_EXPIRED;
|
|
case "device-nonce-missing":
|
|
return ConnectErrorDetailCodes.DEVICE_AUTH_NONCE_REQUIRED;
|
|
case "device-nonce-mismatch":
|
|
return ConnectErrorDetailCodes.DEVICE_AUTH_NONCE_MISMATCH;
|
|
case "device-signature":
|
|
return ConnectErrorDetailCodes.DEVICE_AUTH_SIGNATURE_INVALID;
|
|
case "device-public-key":
|
|
return ConnectErrorDetailCodes.DEVICE_AUTH_PUBLIC_KEY_INVALID;
|
|
default:
|
|
return ConnectErrorDetailCodes.DEVICE_AUTH_INVALID;
|
|
}
|
|
}
|
|
|
|
export function readConnectErrorDetailCode(details: unknown): string | null {
|
|
if (!details || typeof details !== "object" || Array.isArray(details)) {
|
|
return null;
|
|
}
|
|
const code = (details as { code?: unknown }).code;
|
|
return typeof code === "string" && code.trim().length > 0 ? code : null;
|
|
}
|
|
|
|
export function readConnectErrorRecoveryAdvice(details: unknown): ConnectErrorRecoveryAdvice {
|
|
if (!details || typeof details !== "object" || Array.isArray(details)) {
|
|
return {};
|
|
}
|
|
const raw = details as {
|
|
canRetryWithDeviceToken?: unknown;
|
|
recommendedNextStep?: unknown;
|
|
};
|
|
const canRetryWithDeviceToken =
|
|
typeof raw.canRetryWithDeviceToken === "boolean" ? raw.canRetryWithDeviceToken : undefined;
|
|
const normalizedNextStep =
|
|
typeof raw.recommendedNextStep === "string" ? raw.recommendedNextStep.trim() : "";
|
|
const recommendedNextStep = CONNECT_RECOVERY_NEXT_STEP_VALUES.has(
|
|
normalizedNextStep as ConnectRecoveryNextStep,
|
|
)
|
|
? (normalizedNextStep as ConnectRecoveryNextStep)
|
|
: undefined;
|
|
return {
|
|
canRetryWithDeviceToken,
|
|
recommendedNextStep,
|
|
};
|
|
}
|