Skip to content

Commit 928356c

Browse files
authored
Don't assume csrf token is the first node (#828)
The behaviour seems to be inconsistent in the latest version of kratos, so we adopt the `find()` pattern used elsewhere in the code. Fixes canonical/kratos-operator#579
2 parents 70caffe + 8c8a8ff commit 928356c

File tree

6 files changed

+33
-21
lines changed

6 files changed

+33
-21
lines changed

ui/pages/login.tsx

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import {
2929
isWebauthnAutologin,
3030
toggleWebauthnSkip,
3131
} from "../util/webauthnAutoLogin";
32+
import { getCsrfNode, getCsrfToken } from "../util/getCsrfNode";
3233

3334
type AppConfig = {
3435
oidc_webauthn_sequencing_enabled?: boolean;
@@ -306,17 +307,10 @@ const Login: NextPage = () => {
306307
// autosubmit webauthn in case email is provided
307308
const email = urlParams.get("email");
308309
if (isWebauthn && email) {
309-
const csrfNode = renderFlow?.ui.nodes.find(
310-
(node) =>
311-
node.group === "default" &&
312-
node.attributes.node_type === "input" &&
313-
node.attributes.name === "csrf_token",
314-
)?.attributes as UiNodeInputAttributes;
315-
316310
void handleSubmit({
317311
method: "webauthn",
318312
identifier: email,
319-
csrf_token: (csrfNode.value as string) ?? "",
313+
csrf_token: getCsrfToken(renderFlow?.ui.nodes),
320314
}).catch(() => {
321315
if (flow?.return_to) {
322316
window.location.href = flow.return_to;
@@ -358,14 +352,13 @@ const Login: NextPage = () => {
358352
});
359353

360354
// automatically forward to single oidc provider if it is the only option
355+
const csrfNode = getCsrfNode(renderFlow?.ui.nodes);
361356
const isSingleOidcOption =
362357
isSequencedLogin &&
363358
renderFlow?.ui.nodes.length === 2 &&
364359
renderFlow?.ui.nodes[1].group === "oidc" &&
365-
(renderFlow?.ui.nodes[0].attributes as UiNodeInputAttributes).name ===
366-
"csrf_token";
360+
csrfNode !== undefined;
367361
if (isSingleOidcOption) {
368-
const csrfNode = renderFlow?.ui.nodes[0];
369362
const oidcNode = renderFlow?.ui.nodes[1];
370363
const oidcAttributes = oidcNode.attributes as UiNodeInputAttributes;
371364
const csrfAttributes = csrfNode.attributes as UiNodeInputAttributes;

ui/pages/reset_password.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { handleFlowError } from "../util/handleFlowError";
88
import { kratos } from "../api/kratos";
99
import PageLayout from "../components/PageLayout";
1010
import Password from "../components/Password";
11-
import { UiNodeInputAttributes } from "@ory/client/api";
11+
import { getCsrfToken } from "../util/getCsrfNode";
1212
import { AxiosError } from "axios";
1313
import { FlowResponse } from "./consent";
1414
import {
@@ -102,8 +102,7 @@ const ResetPassword: NextPage<Props> = ({ forceSelfServe }: Props) => {
102102
.updateSettingsFlow({
103103
flow: String(flow?.id),
104104
updateSettingsFlowBody: {
105-
csrf_token: (flow?.ui?.nodes[0].attributes as UiNodeInputAttributes)
106-
.value as string,
105+
csrf_token: getCsrfToken(flow?.ui?.nodes),
107106
method: "password",
108107
password: password,
109108
},

ui/pages/setup_backup_codes.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import PageLayout from "../components/PageLayout";
1414
import { AxiosError } from "axios";
1515
import { Spinner } from "@canonical/react-components";
1616
import { UiNodeInputAttributes } from "@ory/client/api";
17+
import { getCsrfToken } from "../util/getCsrfNode";
1718
import {
1819
isBackupCodeConfirm,
1920
isBackupCodeConfirmText,
@@ -95,8 +96,7 @@ const SetupBackupCodes: NextPage<Props> = ({ forceSelfServe }: Props) => {
9596
.updateSettingsFlow({
9697
flow: String(flow?.id),
9798
updateSettingsFlowBody: {
98-
csrf_token: (flow?.ui?.nodes[0].attributes as UiNodeInputAttributes)
99-
.value as string,
99+
csrf_token: getCsrfToken(flow?.ui?.nodes),
100100
method: "lookup_secret",
101101
lookup_secret_reveal: methodValues.lookup_secret_reveal
102102
? true

ui/pages/setup_passkey.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import PageLayout from "../components/PageLayout";
1515
import { AxiosError } from "axios";
1616
import { Button, Icon, Spinner } from "@canonical/react-components";
1717
import { UpdateSettingsFlowWithWebAuthnMethod } from "@ory/client/api";
18+
import { getCsrfToken } from "../util/getCsrfNode";
1819
import {
1920
isSecurityKeyAddBtn,
2021
isSecurityKeyNameInput,
@@ -113,8 +114,7 @@ const SetupPasskey: NextPage<Props> = ({ forceSelfServe }: Props) => {
113114
.updateSettingsFlow({
114115
flow: String(flow?.id),
115116
updateSettingsFlowBody: {
116-
csrf_token: (flow?.ui?.nodes[0].attributes as UiNodeInputAttributes)
117-
.value as string,
117+
csrf_token: getCsrfToken(flow?.ui?.nodes),
118118
method: "webauthn",
119119
webauthn_remove: authValues.webauthn_remove,
120120
},

ui/pages/setup_secure.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { kratos } from "../api/kratos";
1313
import PageLayout from "../components/PageLayout";
1414
import { AxiosError } from "axios";
1515
import { Notification, Spinner } from "@canonical/react-components";
16-
import { UiNodeInputAttributes } from "@ory/client/api";
16+
import { getCsrfToken } from "../util/getCsrfNode";
1717
import {
1818
getLoggedInName,
1919
hasSelfServeReturn,
@@ -89,8 +89,7 @@ const SetupSecure: NextPage<Props> = ({ forceSelfServe }: Props) => {
8989
.updateSettingsFlow({
9090
flow: String(flow?.id),
9191
updateSettingsFlowBody: {
92-
csrf_token: (flow?.ui?.nodes[0].attributes as UiNodeInputAttributes)
93-
.value as string,
92+
csrf_token: getCsrfToken(flow?.ui?.nodes),
9493
method: "totp",
9594
totp_code: methodValues.totp_code,
9695
totp_unlink: methodValues.totp_unlink ? true : undefined,

ui/util/getCsrfNode.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { UiNode, UiNodeInputAttributes } from "@ory/client";
2+
3+
export const getCsrfNode = (
4+
nodes?: UiNode[],
5+
): UiNode | undefined =>
6+
nodes?.find(
7+
(node) =>
8+
node.group === "default" &&
9+
// ensure it's an input node with the csrf token name
10+
(node.attributes as UiNodeInputAttributes | undefined)?.node_type ===
11+
"input" &&
12+
(node.attributes as UiNodeInputAttributes | undefined)?.name ===
13+
"csrf_token",
14+
);
15+
16+
export const getCsrfToken = (nodes?: UiNode[]): string | undefined => {
17+
const node = getCsrfNode(nodes);
18+
return (node?.attributes as UiNodeInputAttributes | undefined)?.value as
19+
| string
20+
| undefined;
21+
};

0 commit comments

Comments
 (0)