Skip to content

Possible race condition with multiple authorization requests causes CSRF error #198

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
difernandez opened this issue Jan 27, 2025 · 0 comments

Comments

@difernandez
Copy link

difernandez commented Jan 27, 2025

Description

When multiple authorize requests are made in quick succession, some times a CSRF error appears:

ERROR -- omniauth: (openid_connect) Authentication failure! csrf_detected: OmniAuth::Strategies::OpenIDConnect::CallbackError, csrf_detected | Invalid 'state' parameter

This can happen if the user clicks many times on a button or link that triggers authorization, if they open said links in many different tabs, or if silent authentication on render is implemented and the user reloads a page many times.

As far as I can tell, the problem comes from how the state is saved to storage:

def new_state
  state = if options.state.respond_to?(:call)
            if options.state.arity == 1
              options.state.call(env)
            else
              options.state.call
            end
          end
  session['omniauth.state'] = state || SecureRandom.hex(16)
end

State is always saved in session as a string, always under a fixed key. That means that if a second auth request is made before the callback for a first request has been received, the state for the first request is overwritten and when it's callback eventually is received, it fails with the csrf error because of the missing first state.

Possible solutions

I can think of two approaches to address this:

  1. Define session['omniauth.state'] as an array. That way, new_state would push to it, and stored_state would find, delete and return the state if it's present in the array.
  2. Use the state as key instead of value of the session stored state. That would also prevent collision/overwrite of stage, and it would also allow saving of other related state if you wanted (like a return_url to be used after a successful callback for example). This seems to be the approach used/recommenbed by Auth0, as seen in their documentation and in their Client Side SDK:
this.storage.setItem(this.namespace + state, transactionPayload, {
  expires: this.stateExpiration
});

I think the first approach is a bit simpler, but the second one comes with the added benefit of being able to associate a payload with the state, and the auth request by proxy, I don't know if that is something that would be of interest to this project.

@difernandez difernandez changed the title Possible race condition with multiple authorization requests Possible race condition with multiple authorization requests causes CSRF error Feb 5, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant