File size: 3,088 Bytes
c09f67c | 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 | /**
* Shared OAuth utilities for app integrations
*/
import type { OAuthErrorCode } from "@midday/app-store/oauth-errors";
export type { OAuthErrorCode };
/**
* Map OAuth errors from any provider to standardized error codes
*
* Handles errors from:
* - Standard OAuth (access_denied, consent_required, etc.)
* - Fortnox (error_missing_license, error_missing_system_admin_right, etc.)
* - Google/Microsoft (scope errors)
* - Xero/QuickBooks (various provider errors)
*/
export function mapOAuthError(error: string | undefined): OAuthErrorCode {
if (!error) return "unknown_error";
const errorLower = error.toLowerCase();
// Access denied / User cancelled
if (
errorLower.includes("access_denied") ||
errorLower.includes("user_denied") ||
errorLower.includes("consent") ||
errorLower.includes("cancelled") ||
errorLower.includes("canceled")
) {
return "access_denied";
}
// License errors (Fortnox)
if (
errorLower.includes("missing_license") ||
errorLower.includes("missing_app_license") ||
errorLower.includes("license_required") ||
errorLower.includes("no_license")
) {
return "missing_license";
}
// Permission/Admin errors (Fortnox, general)
if (
errorLower.includes("missing_system_admin") ||
errorLower.includes("admin_right") ||
errorLower.includes("insufficient_permission") ||
errorLower.includes("invalid_scope") ||
errorLower.includes("insufficient_scope") ||
errorLower.includes("scope")
) {
return "missing_permissions";
}
// State errors
if (
errorLower.includes("invalid_state") ||
errorLower.includes("state_mismatch") ||
errorLower.includes("state_expired")
) {
return "invalid_state";
}
return "unknown_error";
}
/**
* Build success redirect URL for OAuth callback
*/
export function buildSuccessRedirect(
dashboardUrl: string,
provider: string,
source?: string,
fallbackPath = "/settings/apps",
): string {
// For apps flow (popup), redirect to oauth-callback
if (source === "apps") {
return `${dashboardUrl}/oauth-callback?status=success`;
}
// For direct navigation flow, redirect to fallback path with success params
const params = new URLSearchParams({
connected: "true",
provider,
});
return `${dashboardUrl}${fallbackPath}?${params.toString()}`;
}
/**
* Build error redirect URL for OAuth callback
*/
export function buildErrorRedirect(
dashboardUrl: string,
errorCode: string,
provider: string,
source?: string,
fallbackPath = "/settings/apps",
): string {
// For apps flow (popup), redirect to oauth-callback to show error
if (source === "apps") {
const params = new URLSearchParams({
status: "error",
error: errorCode,
});
return `${dashboardUrl}/oauth-callback?${params.toString()}`;
}
// For direct navigation flow, redirect to fallback path with error params
const params = new URLSearchParams({
connected: "false",
error: errorCode,
provider,
});
return `${dashboardUrl}${fallbackPath}?${params.toString()}`;
}
|