-
Notifications
You must be signed in to change notification settings - Fork 100
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
Can't verify CSRF token authenticity when returning from auth #54
Comments
@nhosoya i noticed |
Does the client use the full server-side flow by first making a call to If using the hybrid and skipping the request phase, then you'll need to set If the state value is stored by the client in a cookie, you'll need to ensure that the cookie's Same-Site is set to https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite |
Hi @btalbot, it is a full server-side flow. I tried changing the cookie to |
Hello. |
Hello @hikaru-w, what do you mean? Are you saying that when you @btalbot I can confirm that changing to |
No, I have no idea why disabling csrf protections would depend on cookie same-site policy as the cookie same site is only enforced by the browser. Disabling csrf protections would make cookie state irrelevant. For whatever reason, Sign In With Apple requires the callback to be made using a POST (via response_mode=form_post) and since that is done by javascript loaded from appleid.apple.com, the Origin and Referer headers are both from apple.com domain. This prevents any but Same-Site=None cookies from being sent by a conforming browser. About the change to the callback suddenly making the provider work, this indicates to me that there is some other error in the callback which is causing the some confusing error message. Please post the simplest possible callback method that you can get to cause the issue. It sounds like others are reporting that a simple |
Hi @btalbot and thanks for the thorough reply. I am able to reproduce the 422 even this with minimal callback controller. This, along with the fact that other providers (e.g. Facebook and LinkedIn) work fine and have essentially identical callback flows to Apple (find or create a user, sign them in) leads me to believe the issue lies somewhere else and is specific to either the Apple flow or somehow caused by this gem. Any other insight is appreciated.
|
I realized that callbacks are defined as
However, while LinkedIn and Facebook send a |
Finally found a good reference for what is going on. Closing this issue since the problem is more general to omniauth. However, given that Apple |
Yeah, apple doesn't make it easy for some reason. If you have additional CSRF protections enabled, that may be an issue as that middleware will see a POST with no anti-csrf token and not XHR headers. Probably need to disable whatever CSRF middleware you have for the apple callback route in particular. |
@waissbluth Thanks for sharing your research on this issue. We ran into the same problem, but we already have the |
Ah, looks like that gem doesn't solve the issue of Apple POSTing to the |
The callback POST request is made by apple client using Origin and Referer using appleid.apple.com domains. This means that any cookies being used (for sessions, state, etc) will only be passed to the callback method if the cookie Same-Site=None. The default Same-Site is typically Lax now with modern browsers. If you don't get the session you expect in the callback phase, that is likely the issue. |
Thanks @btalbot for your answer. So just to clarify, the issue is that we are setting our CSRF token in our session cookie, and that cookie won't be sent to our server when Apple makes a POST request to our site? And the reason is that our session cookie is likely set to use a |
That may be _a_ reason, but the more likely reason is that Apple will not send any headers they didn't originate. So your endpoint needs to be okay with that eventuality.
Walter
… On Jul 23, 2020, at 3:46 PM, Yousuf J ***@***.***> wrote:
Thanks @btalbot for your answer. So just to clarify, the issue is that we are setting our CSRF token in our session cookie, and that cookie won't be sent to our server when Apple makes a POST request to our site? And the reason is that our session cookie is likely set to use a Same-Site value of Lax, which prevents browsers from sending cookies for cross site requests?
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub, or unsubscribe.
|
@btalbot Hi there. FTR, I work with @yjukaku who already posted some comments in this thread last year. I've been taking a 2nd look into this problem because Rails 6.1 has a new default config for As mentioned before, Chrome has been using
This seems to indicate that the only way to get the Rails' session cookie when Apple makes their POST request is to configure the But wouldn't that also make the session cookie unusable in http://localhost? This would force all developers to setup an SSL certificate to develop locally and be able to use HTTPS if they want to use a session at all, even regardless of omniauth. There should be some other way... Doing a bit more digging, I noticed the I can think of 2 possible workarounds and I would like your feedback:
Thoughts? Given this could potentially require a change in omniauth-oauth2, I think it'd be good if we could get an opinion from @BobbyMcWho. |
Hi, @oboxodo It seems like the cleanest work-around would be to include a before_request_phase hook which inspects the request and if using apple, then ensure current session cookie Same-Site is set appropriately for the browser (either 'None' or unset). Then, in the callback phase, reset the Same-Site to your desired value, like 'Lax', as needed. Might be also smart to set the cookie TTL to be shorter than normal as well if you're concerned about Same-Site abuse over a longer time. This pretty much gives the same effect as the chrome work-around you point out above. |
It's been omniauth's goal to not add any Rails specific code to the core libraries, so if there's a solution that omniauth-apple or individual app developers can take when specifically using Rails, I'd tend to lean that direction. |
@btalbot 🤔 that sounds like it might work. but...
Do you know from the top of your head how I would do that? It seems to me in the context of the @BobbyMcWho thanks for chiming in. I actually think this is not specific to Rails. The fact that Rails 6.1 starts defaulting all cookies to use |
That makes sense, if it's something that folks believe needs fixed on the omniauth-oauth2 side, I'd be happy to review a PR in the short term or implement something in the long term, I'd likely need to brush up on the oauth spec again to see if there's something called out explicitly |
The hook gets the request.env so from that you can check or update the session options as needed:
|
Ok so... this seems to work: # This is a default value in Rails 6.1 which can be loaded implicitly as part of
# `config.load_defaults 6.1` in application.rb or explicitly from new_framework_defaults_6_1.rb.
config.action_dispatch.cookies_same_site_protection = :lax # config/initializers/omniauth.rb
previous_before_request_phase = OmniAuth.config.before_request_phase
OmniAuth.config.before_request_phase = -> (env) do
# This is just in case there was something else configured before
previous_before_request_phase.call(env) if previous_before_request_phase
if ENV["OMNIAUTH_CHANGES_SAME_SITE_TO_NONE"] == "true"
# Make sure the session cookie's SameSite option is set to `None` so that when
# Apple does the callback phase with a POST request, we'll get the session cookie.
#
# Ideally, I'd check here if this request is going to Apple and not some other
# Identity Provider where doing this is not necessary. But I'm not sure how to
# check for that in here.
env['rack.session.options']['same_site'] = :none
env['rack.session.options']['secure'] = true
end
end What are the downsides?
I decided to take a look at what are other Rails-based websites doing with their session cookies and these are my findings:
I'm still unsure what we'll do. Most probably we'll avoid setting SameSite explicitly for now... but at some point explicit Lax seems to be the best choice. In any case, I do consider omniauth should potentially try to handle this stuff off-the-shelf. This is certainly NOT Rails-specific. I think the best option could be for omniauth to make itself independent from the session and use a specific cookie of its own to store its intermediate state. This cookie would need to have Thank you for your pointers @btalbot! |
One thing to note is that if you're setting |
@BobbyMcWho do you mean that code will get executed only once at app initialization? I think the proc itself will get executed each time someone clicks on a "sign up with X " link/button in the app to initiate a request phase, right? |
I want to embrace Rails 6.1's new `config.action_dispatch.cookies_same_site_protection = :lax` default value for _most_ of our cookies, but I'd like our session cookie (using the `CookieStore`) to still avoid setting any `SameSite` option for now. The main reason having to do with Apple Sign In and Omniauth not playing nice with `Lax` (because Apple uses POST instead of GET) but you can read more about it [here](nhosoya/omniauth-apple#54 (comment)) if you're interested interested. The way I was trying to do this was with the following settings: ```ruby # config/application.rb # This is not needed explicitly in Rails 6.1+ because it's the default but it's here to help with the explanation. config.action_dispatch.cookies_same_site_protection = :lax # Specifically for the session cookie, avoid setting any explicit `SameSite` value letting the browser use its default. config.session_store :cookie_store, key: "_myapp_session", same_site: nil ``` **But that didn't work** and `lax` was applied even for the session cookie. With this patch, I could use `same_site: :nil` and it'd work. Regardless of why I need it, I do think it's a useful option and the current code doesn't seem to allow avoiding to set ANY `SameSite` value on a cookie by cookie bases, once a default has been set. It's either all or nothing. I think with this little change I would be able to still configure a default to be used whenever I don't set `:same_site` explicitly, but I can use the "special" `:nil` value if I want to make sure that cookie doesn't set any explicit value regardless of the configured default. I'm not a fan of the `:nil` special value but couldn't think of anything better than that. I guess another possibility would be to allow the `cookies_same_site_protection` proc to read the cookie name (we'd need to pass `options` on top of the `request`, I guess) so that the dev can decide to use a different default value depending on the cookie name. but this would be a bigger change, potentially not backward-compatible. I assume you'd need an accompanying automated test in order to merge this but I wanted to get some opinions about this before dedicating more time on it.
@oboxodo Thanks so much for your very helpful notes! Also, thanks so much @dlin-me for doing the legwork to create this utility that's based on the Chromium incompatible browsers list. For anyone who finds this, we decided to make the session cookie |
Hey, thanks for this. I made a small adjustment to verify that the Apple callback path is being used under these conditions. My app seems to get a lot of 'volunteer' penetration testers so if someone could exploit my Discord callback being open (my other strategy), I'm sure they would. Heh # config/initializers/omniauth.rb
previous_before_request_phase = OmniAuth.config.before_request_phase
OmniAuth.config.before_request_phase = -> (env) do
# This is just in case there was something else configured before
previous_before_request_phase.call(env) if previous_before_request_phase
if ENV["OMNIAUTH_CHANGES_SAME_SITE_TO_NONE"] == "true" && env['REQUEST_PATH'] == '/users/auth/apple'
# Make sure the session cookie's SameSite option is set to `None` so that when
# Apple does the callback phase with a POST request, we'll get the session cookie.
#
# Ideally, I'd check here if this request is going to Apple and not some other
# Identity Provider where doing this is not necessary. But I'm not sure how to
# check for that in here.
env['rack.session.options']['same_site'] = 'None'
env['rack.session.options']['secure'] = true
end
end |
An alternative approach, at least possible on Rails 7, is adding the following to the config.action_dispatch.cookies_same_site_protection = lambda { |request|
if request.path.starts_with?("/auth/apple")
:none
else
:lax
end
} |
I fixed this with the following codes: In the Omniauth callbacks controller: private
def verified_request?
action_name == 'apple' || super
end and in application.rb: config.action_dispatch.cookies_same_site_protection = lambda { |request|
request.path == '/users/auth/apple' ? :none : :lax
} |
As mentioned above if there is another request it will set session cookie back to # config/application.rb
config.action_dispatch.cookies_same_site_protection = lambda { |request|
cookies = request.env["action_dispatch.cookies"]
if request.path.starts_with?("/auth/apple", "/users/auth/apple")
cookies[:apple_signin] = { same_site: :none, expires: 2.minutes.from_now, secure: true,
value: "true", http_only: true }
end
cookies.key?(:apple_signin) ? :none : :lax
} |
Could it be that this leads to not being able to set a cookie in I just implemented the workaround of @antulik (and @oboxodo) and I don't get an error but when after creating the user and setting the user cookie with cookies.encrypted[:session_id]. Then I redirect to the root page and there is no cookie. The other auth method works fine. That leads me to believe that it must the Apple cookie issue. Is there a way to set the cookie in the callbacks controller, like setting cookie policy back to :lax? Is there any workaround for this? Like downgrading the gem? |
I am still facing issues with this as well. I get the error
Anyone knows how to resolve this issue? |
|
Tried all solutions here and none have worked so far. Seems like gem's currently unmaintained and broken. |
This worked on Rails 7.1 for me #54 (comment) |
PRs are welcome |
Works as designed on this end. Thanks 🙌 |
I am getting
422 Unprocessable Entity
errors caused byActionController::InvalidAuthenticityToken
after successful auth redirecting back to the app. I have seen some posts that include theprovider_ignores_state: true
setting. However, this does not make a difference either (and would potentially cause security issues?).I am using this as a strategy for Devise, and not directly as Rack middleware, and this is the only auth provider that causes trouble (the other being LinkedIn and Facebook, which work fine).
Is there any reason why this gem would not work as a provider for devise? It seems to be working OK until the redirect step when the CSRF protection causes the problem.
The text was updated successfully, but these errors were encountered: