⚠️ This issue respects the following points: ⚠️
Bug description
Broken in Chrome. Works correctly in Firefox. (These are the two browsers we tested ourselves. The original #33164 additionally reported Chromium, Edge, and Safari as affected — we haven't independently verified those, flagging that distinction since some use a different engine than Chrome.)
When approving an OAuth2 client via the login flow's grant page, the "Grant access" submit button has no disabled/loading state while the form is submitting. In Chrome this allows the form to submit twice from a single page load — confirmed via server logs: the grant page loads exactly once, then two POST /login/flow requests fire two seconds apart with no page reload in between. The first submission succeeds server-side; the second, reusing the same now-invalidated stateToken, fails with 403 "Access forbidden. State tokens do not match." In Firefox, the same steps complete successfully every time.
This looks like a regression of #33164, fixed in 2022 by disabling the button in core/js/login/grant.js. That file no longer exists — the grant page was rewritten as core/src/views/LoginFlowGrant.vue, and the current version (confirmed by reading the source on master as of this writing) has no equivalent protection: no :disabled binding on the <NcButton type="submit">, and the onSubmit handler only intercepts the event when password re-confirmation is required.
Steps to reproduce
- In Chrome (fails; the same steps in Firefox succeed — other browsers untested by us), register an OAuth 2.0 client (Admin settings → Security → OAuth 2.0 clients).
- Start an authorization_code flow against
/apps/oauth2/authorize from that client.
- Log in, reach the "Grant access" screen.
- Click "Grant access" once.
- Observe two
POST /login/flow requests in the server log (or browser devtools network tab) from that single click/page load — the second fails with 403 "state tokens do not match," even though only one page load and one intentional click occurred.
Expected behavior
The submit button (or the form) should be protected against re-submission immediately on click, equivalent to what #33166 added to the pre-rewrite page — and should work as reliably in Chrome as it currently does in Firefox.
Nextcloud Server version
34
Additional info
Verified via correlated log inspection (OAuth client application, reverse proxy, and Nextcloud's own nextcloud.log) that Nextcloud's server-side OAuth2/state-token handling is correct on every attempt — the first submission always completes successfully. Confirmed the browser does not reload/navigate between the two submissions (single GET /login/flow/grant followed by two POST /login/flow, no intervening page load). We tested Chrome (reproduces) and Firefox (does not reproduce) ourselves; the original #33164 also lists Chromium, Edge, and Safari as affected, which we have not independently confirmed.
Current file: https://github.com/nextcloud/server/blob/master/core/src/views/LoginFlowGrant.vue
This is pure client-side JS behavior, independent of OS/PHP/webserver/database, so those fields are omitted.
Bug description
Broken in Chrome. Works correctly in Firefox. (These are the two browsers we tested ourselves. The original #33164 additionally reported Chromium, Edge, and Safari as affected — we haven't independently verified those, flagging that distinction since some use a different engine than Chrome.)
When approving an OAuth2 client via the login flow's grant page, the "Grant access" submit button has no disabled/loading state while the form is submitting. In Chrome this allows the form to submit twice from a single page load — confirmed via server logs: the grant page loads exactly once, then two
POST /login/flowrequests fire two seconds apart with no page reload in between. The first submission succeeds server-side; the second, reusing the same now-invalidatedstateToken, fails with 403 "Access forbidden. State tokens do not match." In Firefox, the same steps complete successfully every time.This looks like a regression of #33164, fixed in 2022 by disabling the button in
core/js/login/grant.js. That file no longer exists — the grant page was rewritten ascore/src/views/LoginFlowGrant.vue, and the current version (confirmed by reading the source onmasteras of this writing) has no equivalent protection: no:disabledbinding on the<NcButton type="submit">, and theonSubmithandler only intercepts the event when password re-confirmation is required.Steps to reproduce
/apps/oauth2/authorizefrom that client.POST /login/flowrequests in the server log (or browser devtools network tab) from that single click/page load — the second fails with 403 "state tokens do not match," even though only one page load and one intentional click occurred.Expected behavior
The submit button (or the form) should be protected against re-submission immediately on click, equivalent to what #33166 added to the pre-rewrite page — and should work as reliably in Chrome as it currently does in Firefox.
Nextcloud Server version
34
Additional info
Verified via correlated log inspection (OAuth client application, reverse proxy, and Nextcloud's own
nextcloud.log) that Nextcloud's server-side OAuth2/state-token handling is correct on every attempt — the first submission always completes successfully. Confirmed the browser does not reload/navigate between the two submissions (singleGET /login/flow/grantfollowed by twoPOST /login/flow, no intervening page load). We tested Chrome (reproduces) and Firefox (does not reproduce) ourselves; the original #33164 also lists Chromium, Edge, and Safari as affected, which we have not independently confirmed.Current file: https://github.com/nextcloud/server/blob/master/core/src/views/LoginFlowGrant.vue
This is pure client-side JS behavior, independent of OS/PHP/webserver/database, so those fields are omitted.