Authorize consents for the account-holder type the bank supports
Kontist authorized but shared no accounts: psu_type was hardcoded to personal, and Enable Banking documents that a psu_type mismatch can yield a consent without the expected accounts. The bank listing now reports each institution's supported psu_types, the connect form offers only those, the chosen type reaches POST /auth, and an unsupported combination is refused before the user is sent to a bank. The choice is stored per consent so reconnecting reuses it; consents predating the choice stay personal. Also repairs the frontend derivation, which the Montserrat dependency broke: npmDepsHash was stale and web/public was missing from the fileset, so the traced duck icon never reached the built assets.
This commit is contained in:
+45
-6
@@ -188,11 +188,13 @@ function AccountsContent({
|
||||
async function authorize(
|
||||
institution: string,
|
||||
country: string,
|
||||
psuType: string,
|
||||
historyMonths: number,
|
||||
) {
|
||||
const response = await request<{ url: string }>("/api/banking/authorize", {
|
||||
institution,
|
||||
country,
|
||||
psu_type: psuType,
|
||||
history_months: historyMonths,
|
||||
});
|
||||
const url = new URL(response.url);
|
||||
@@ -302,6 +304,7 @@ function AccountCard({
|
||||
await authorize(
|
||||
institution,
|
||||
connection.country || "DE",
|
||||
connection.psu_type || "personal",
|
||||
connection.history_months,
|
||||
);
|
||||
} catch (err) {
|
||||
@@ -634,8 +637,19 @@ function ConnectForm({
|
||||
onError: (error: string) => void;
|
||||
}) {
|
||||
const [institution, setInstitution] = useState("");
|
||||
const [psuTypes, setPSUTypes] = useState<string[] | null>(null);
|
||||
const [psuType, setPSUType] = useState("personal");
|
||||
const [country, setCountry] = useState("DE");
|
||||
const [historyMonths, setHistoryMonths] = useState("12");
|
||||
// Supported account types come from the bank listing. Authorizing a business
|
||||
// account with the personal flow yields a consent that shares no accounts,
|
||||
// so keep the choice inside what the selected bank actually offers.
|
||||
const chooseInstitution = (name: string, supported?: string[]) => {
|
||||
setInstitution(name);
|
||||
setPSUTypes(supported ?? null);
|
||||
if (supported?.length && !supported.includes(psuType))
|
||||
setPSUType(supported[0]);
|
||||
};
|
||||
const [busy, setBusy] = useState(false);
|
||||
const [copied, setCopied] = useState(false);
|
||||
const callback =
|
||||
@@ -696,7 +710,12 @@ function ConnectForm({
|
||||
setBusy(true);
|
||||
onError("");
|
||||
try {
|
||||
await authorize(institution.trim(), country, Number(historyMonths));
|
||||
await authorize(
|
||||
institution.trim(),
|
||||
country,
|
||||
psuType,
|
||||
Number(historyMonths),
|
||||
);
|
||||
} catch (err) {
|
||||
onError(err instanceof Error ? err.message : String(err));
|
||||
setBusy(false);
|
||||
@@ -707,8 +726,28 @@ function ConnectForm({
|
||||
country={country}
|
||||
configured={state.status.banking_configured}
|
||||
value={institution}
|
||||
onChange={setInstitution}
|
||||
onChange={chooseInstitution}
|
||||
/>
|
||||
<Field
|
||||
label="Account type"
|
||||
hint={
|
||||
psuTypes?.length === 1
|
||||
? `${institution} offers account information for ${psuTypes[0]} accounts only.`
|
||||
: "Business accounts must be authorized as business: the personal flow returns a consent without accounts."
|
||||
}
|
||||
>
|
||||
<select
|
||||
required
|
||||
value={psuType}
|
||||
onChange={(e) => setPSUType(e.target.value)}
|
||||
>
|
||||
{(psuTypes ?? ["personal", "business"]).map((kind) => (
|
||||
<option key={kind} value={kind}>
|
||||
{kind === "business" ? "Business" : "Personal"}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</Field>
|
||||
<Field label="Country" hint="Two-letter country code">
|
||||
<input
|
||||
required
|
||||
@@ -717,7 +756,7 @@ function ConnectForm({
|
||||
value={country}
|
||||
onChange={(e) => {
|
||||
setCountry(e.target.value.toUpperCase());
|
||||
setInstitution("");
|
||||
chooseInstitution("");
|
||||
}}
|
||||
/>
|
||||
</Field>
|
||||
@@ -765,7 +804,7 @@ function InstitutionSelect({
|
||||
country: string;
|
||||
configured: boolean;
|
||||
value: string;
|
||||
onChange: (name: string) => void;
|
||||
onChange: (name: string, psuTypes?: string[]) => void;
|
||||
}) {
|
||||
const [institutions, setInstitutions] = useState<Institution[] | null>(null);
|
||||
const [loadError, setLoadError] = useState("");
|
||||
@@ -840,7 +879,7 @@ function InstitutionSelect({
|
||||
if (e.key === "Enter" && open) {
|
||||
e.preventDefault();
|
||||
if (shown.length === 1) {
|
||||
onChange(shown[0].name);
|
||||
onChange(shown[0].name, shown[0].psu_types);
|
||||
setOpen(false);
|
||||
}
|
||||
}
|
||||
@@ -860,7 +899,7 @@ function InstitutionSelect({
|
||||
aria-selected={i.name === value}
|
||||
onMouseDown={(e) => e.preventDefault()}
|
||||
onClick={() => {
|
||||
onChange(i.name);
|
||||
onChange(i.name, i.psu_types);
|
||||
setOpen(false);
|
||||
}}
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user