Giter Site home page Giter Site logo

gds-sso's Introduction

GDS-SSO

This gem provides everything needed to integrate an application with Signon. It's a wrapper around OmniAuth that adds a 'strategy' for oAuth2 integration against Signon, and the necessary controller to support that request flow.

Some of the applications that use this gem:

Usage

Integration with a Rails 4+ app

  • Include the gem in your Gemfile:

    gem 'gds-sso'
  • Create a "users" table in the database: (example migration with all the necessary fields)

  • Create a User model with the following:

    serialize :permissions, Array
  • Add to your ApplicationController:

    include GDS::SSO::ControllerMethods
    before_action :authenticate_user!

Securing your application

GDS::SSO::ControllerMethods provides some useful methods for your application controllers.

To make sure that only people with a signon account and permission to use your app are allowed in use authenticate_user!.

class ApplicationController < ActionController::Base
  include GDS::SSO::ControllerMethods
  before_action :authenticate_user!
  # ...
end

You can refine authorisation to specific controller actions based on permissions using authorise_user!. All permissions are assigned via Signon.

class PublicationsController < ActionController::Base
  include GDS::SSO::ControllerMethods
  before_action :authorise_for_editing!, except: [:show, :index]
  # ...
private
  def authorise_for_editing!
    authorise_user!('edit_publications')
  end
end

authorise_user! can be configured to check for multiple permissions:

# fails unless the user has at least one of these permissions
authorise_user!(any_of: %w(edit create))

# fails unless the user has both of these permissions
authorise_user!(all_of: %w(edit create))

The signon application makes sure that only users who have been granted access to the application can access it (e.g. they have the signin permission for your app).

Authorisation for API Users

In addition to the single-sign-on strategy, this gem also allows authorisation via a "bearer token". This is used by publishing applications to be authorised as an API user.

To authorise with a bearer token, a request has to be made with the header:

# See https://github.com/alphagov/gds-api-adapters/blob/41e9cbf12bec738489340bd9dc63d62427ee3fe7/lib/gds_api/json_client.rb#L122
Authorization: Bearer your-token-here

To avoid making these requests for each incoming request, this gem will automatically cache a successful response, using the Rails cache.

If you are using a Rails 5 app in api_only mode this gem will automatically disable the oauth layers which use session persistence. You can configure this gem to be in api_only mode (or not) with:

GDS::SSO.config do |config|
  # ...
  # Only support bearer token authentication and send responses in JSON
  config.api_only = true
end

Use in production mode

To use gds-sso in production you will need to setup the following environment variables, which we look for in the config. You will need to have admin access to Signon to get these.

  • GDS_SSO_OAUTH_ID
  • GDS_SSO_OAUTH_SECRET

Use in development mode

In development, you generally want to be able to run an application without needing to run your own SSO server to be running as well. GDS-SSO facilitates this by using a 'mock' mode in development. Mock mode loads an arbitrary user from the local application's user tables:

GDS::SSO.test_user || GDS::SSO::Config.user_klass.first

To make it use a real strategy (e.g. if you're testing an app against the signon server), you will need to ensure that your Signon database has got OAuth config that matches what the apps use in development mode. To do this, run this in Signon:

bundle exec ./script/make_oauth_work_in_dev

Once that's done, set an environment variable when you run your app. e.g.:

GDS_SSO_STRATEGY=real bundle exec rails s

Extra permissions for api users

By default the mock strategies will create a user with signin permission.

If your application needs different or extra permissions for access, you can specify this by adding the following to your config:

GDS::SSO.config do |config|
  # other config here
  config.additional_mock_permissions_required = ["array", "of", "permissions"]
end

The mock bearer token will then ensure that the dummy api user has the required permission.

Testing in your application

If your app is using test-unit or minitest, there is a linting test that can verify your User model is compatible with GDS:SSO::User:

require 'gds-sso/lint/user_test'

class GDS::SSO::Lint::UserTest
  def user_class
    ::User
  end
end

Or if your app is using rspec, there is a shared examples spec:

require 'gds-sso/lint/user_spec'

describe User do
  it_behaves_like "a gds-sso user class"
end

Running the test suite

Run the tests with:

bundle exec rake

Licence

MIT License

gds-sso's People

Contributors

alext avatar barrucadu avatar benilovj avatar benjamineskola avatar benlovell avatar boffbowsh avatar cbaines avatar chrisbashton avatar chrisroos avatar dependabot-preview[bot] avatar dependabot-support avatar dependabot[bot] avatar dhwthompson avatar edwardkerry avatar fidothe avatar floehopper avatar gpeng avatar h-lame avatar heathd avatar jamiecobbett avatar jordanhatch avatar jystewart avatar kevindew avatar lazyatom avatar matthewford avatar murilodalri avatar tekin avatar thomasleese avatar threedaymonk avatar tijmenb avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

gds-sso's Issues

Impossible to test CSRF protection in automated tests

In spite of a javascript-enabled cucumber scenario in our tests in specialist publisher, we failed to spot that we were failing to pass the CSRF token in an Ajax request for previewing documents in the specialist publisher.

In other words, the cucumber scenario worked fine, but the preview button actually failed due to the lack of CSRF.

We tracked this down to the the interaction between the CSRF forgery protection and the 'mock' authentication strategy in gds-sso.

If CSRF verification fails, the default behaviour is to clear the session.

However, in test-mode gds-sso does not pay attention to the session it always treats the user as authenticated, by just loading the first user from the database.

It would be good to figure out a general-purpose fix for this issue so that everyone can rely on cucumber-style tests to verify CSRF behaviour.

We should review the design of the mock_gds_sso strategy to see if there's a way to achieve this.

Behaviour in test and development environments

Having used gds-sso for the first time I'm not happy with it's behaviour in test and development environments. It currently forces a mock-login in those environments. This causes a few issues:

  • Makes it nearly impossible to test system's behaviour with unauthenticated users
  • Increases potential of security issues as behaviour in dev/test environments is very different from production

The latter point is made even more dangerous by the fact that controllers need to be "blacklisted" by adding before_action: authenticate_user! (v.s. explicitly opting them out from authentication). If a developer forgets to "blacklist" a controller they would not notice the fact authentication is missing, because all requests in dev/test are mock-authenticated automatically.

Currently devs can set GDS_SSO_STRATEGY=real to do manual testing locally, however this does not allow switching it on/off for different tests as it is evaluated only once at the application load time. This mock-auth behaviour could be controlled by a config flag that could be changed on a per-test basis. This would allow us to keep the behaviour as it is for existing apps at the same time recommending better defaults for new application developers in documentation.

Any thoughts?

Stubbed mix-in for applications to use in their own functional tests

Example: we might want to provide "preview" functionality for a publishing feature, but we don't want to write out own stub that mirrors all the behaviour of GDS::SSO users in our functional test setup method.

If you could provide one for gem consumers that'd be awesome.

License missing from gemspec

RubyGems.org doesn't report a license for your gem. This is because it is not specified in the gemspec of your last release.

via e.g.

spec.license = 'MIT'
# or
spec.licenses = ['MIT', 'GPL-2']

Including a license in your gemspec is an easy way for rubygems.org and other tools to check how your gem is licensed. As you can imagine, scanning your repository for a LICENSE file or parsing the README, and then attempting to identify the license or licenses is much more difficult and more error prone. So, even for projects that already specify a license, including a license in your gemspec is a good practice. See, for example, how rubygems.org uses the gemspec to display the rails gem license.

There is even a License Finder gem to help companies/individuals ensure all gems they use meet their licensing needs. This tool depends on license information being available in the gemspec. This is an important enough issue that even Bundler now generates gems with a default 'MIT' license.

I hope you'll consider specifying a license in your gemspec. If not, please just close the issue with a nice message. In either case, I'll follow up. Thanks for your time!

Appendix:

If you need help choosing a license (sorry, I haven't checked your readme or looked for a license file), GitHub has created a license picker tool. Code without a license specified defaults to 'All rights reserved'-- denying others all rights to use of the code.
Here's a list of the license names I've found and their frequencies

p.s. In case you're wondering how I found you and why I made this issue, it's because I'm collecting stats on gems (I was originally looking for download data) and decided to collect license metadata,too, and make issues for gemspecs not specifying a license as a public service :). See the previous link or my blog post about this project for more information.

Rails 4 update_attributes no longer takes an options param

On Rails 4 apps we have to overwrite the find_for_gds_oauth method on User because update_attributes does not take an :as option, see: https://github.com/rails/rails/blob/582a90c09386839d7374fa1933ca4d805e1140a1/activerecord/lib/active_record/persistence.rb#L225

For examples of the override see: https://github.com/alphagov/hmrc-contacts/blob/master/app/models/user.rb#L11

As attr_protected had been removed, can we edit this so that we do not pass in an option?

Supporting Rails 5

As I mentioned privately to @boffbowsh -- I'm a good way along with making this gem Rails 5 compatible and am keen to keep our changes going upstream.

One outstanding issue is Rails 5 dropping support for Rubies earlier than 2.2.2, thanks to Rack 2's requirements.

I couldn't remember if you folks still maintain projects targeting earlier rubies so perhaps this is a non-issue? If you do, we could still maintain backwards compatibility for those older Ruby / Rails versions but would likely suffer a wall of deprecation messages when running in the context of Rails 5.x. There are of course workarounds we could employ -- checking the Rails version at runtime and doing the Right Thing:tm:.

Happy to take a steer on your most favoured approach. Thoughts?

End support for unused Ruby / Rails versions

We currently support usage of this gem in the context of both Ruby 1.9.3 and Rails 3.2 but our applications and services have since moved on from here. We should end support for these versions.

Determining "API" calls

The issue arises when an application calls back to itself via AJAX from the client - say to bind a UI element to a collection of JSON resources.

The user is authenticated, has a current session and the client library will pass the session cookie - so there is no need to authenticate via other means. However, the presence of the HTTP_ACCEPT: application/json header / value dictates the (gds-sso) hosting application will determine the call "API" in nature and will attempt to authenticate via a bearer-token that is not present. See:

# lib/gds-sso/api_access.rb
require 'rack/accept'

module GDS
  module SSO
    class ApiAccess
      def self.api_call?(env)
        request = Rack::Accept::Request.new(env)
        request.best_media_type(%w{text/html application/json}) == 'application/json'
      end
    end
  end
end

I'm proposing an alteration to this behaviour so requests are considered "API" in nature - based only upon the presence of a bearer token. This would permit clients to make "API" calls in the context of the current user without needing further forms of authentication.

It seems from my rudimentary search in existing GDS apps that this would not be a breaking change, but I thought I'd float the idea before going ahead. Thoughts?

cc: @boffbowsh

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.