Custom SSO not working after secure_session added as a parameter

Hello! We have written a custom SSO plugin to bundle our company’s login details into a package that Discourse can use for handling logins and creating accounts. With v2.7.0beta4, we started getting errors that we were not providing a new parameter, secure_session. I can’t find any details anywhere about what the format of this parameter should be and what it should contain. Is there any additional documentation anywhere for this update?

Thanks!

1 Like

Do you know the exact error message you are seeing? My understanding is that Discourse now ties the SSO nonce parameter to the user’s session. That means that if an app has been generating the nonce by making a background request to /session/sso the nonce will not be valid. Possibly this is the issue that you are running into.

2 Likes

Certainly! Here’s the error stack we’re seeing in our APM:

ArgumentError: missing keyword: :secure_session
…r/www/discourse/app/models/discourse_single_sign_on.rb:   28:in `initialize'
               /var/www/discourse/lib/single_sign_on.rb:   65:in `new'
               /var/www/discourse/lib/single_sign_on.rb:   65:in `parse'
…ourse_account_auth/lib/service_gateway_current_user.rb:   72:in `upsert_sso_record'
…ourse_account_auth/lib/service_gateway_current_user.rb:   53:in `create_sso_record'
…ourse_account_auth/lib/service_gateway_current_user.rb:   28:in `current_user'
                 /var/www/discourse/lib/current_user.rb:   36:in `current_user'

The service_gateway_current_user.rb is part of our plugin to build the payload to send to DiscourseSingleSignOn based on the “Service Gateway” headers (“Service Gateway” being our company’s internal structure for determining if you’re logged into our system).

To explain that bit of the stack there:

  • Our current_user function uses our headers to lookup a SingleSignOnRecord where the external_id matches the User ID in the headers. If that’s not the case, we go into the create_sso_record section.
  • create_sso_record creates a hash – { external_id: sg_headers[:user_id], email: sg_headers[:email] } – and sends that to our upsert_sso_record function.
  • upsert_sso_record takes that hash (payload in the context below) and tries to create a DiscourseSingleSignOn object:
def upsert_sso_record(payload)
  encoded_query = Base64.encode64(payload.to_query)
  sig = OpenSSL::HMAC.hexdigest('sha256', ENV['SSO_SECRET'], encoded_query)
  sso = DiscourseSingleSignOn.parse({ sso: encoded_query, sig: sig }.to_query)

  if SiteSetting.verbose_sso_logging
    Rails.logger.warn("Verbose SSO log: Started SSO process\n\n#{sso.diagnostics}")
    Rails.logger.warn("SSO Payload:\n\n#{payload}")
  end

  begin
    user = sso.lookup_or_create_user(@request.ip)
  rescue ActiveRecord::RecordInvalid => ex
    Rails.logger.error "Unable to find/create sso user #{ex}"
  end
  user
end

The parse call on the 3rd line of that function is where it seems to be getting into the issue. Once it tries to initialize the object, the secure_session value it’s expecting is not there.

2 Likes

Bump! I am also curious about this!

I’m wondering if setting the new discourse_connect_csrf_protection site setting to false will fix the issue for you. That site setting is hidden, so it needs to be set from the Rails console. You can find details about it in the replies to this topic: DiscourseConnect flow no longer functions.

Unfortunately I don’t think that new setting will help by itself - the code internals will still require passing in a secure_session object, even though it’s not used.

In general we would recommend using the official hooks for authentication plugins, rather than overriding core’s DiscourseConnect implementation.

It’s tricky to know for sure without seeing the whole plugin, but you could try passing in a nil value for secure_session. Doing that, alongside toggling the new site setting, might help get things a little closer to working.

5 Likes

After digging into the v2.7.0.beta4 code some more, I see that ApplicationController has a secure_session definition. Changing our plugin code to the following seems to work in our Staging environment:

sso = DiscourseSingleSignOn.parse({ sso: encoded_query, sig: sig }.to_query, secure_session: secure_session)

…Is it really that simple? :thinking:

That sounds like it should work - the secure session does normally come from that method in the ApplicationController. :slight_smile:

2 Likes

Awesome! Thanks for letting me rubber-duck this issue. :duck: :metal:

2 Likes