Skip to content

Commit

Permalink
Add account-api adapter with auth endpoints
Browse files Browse the repository at this point in the history
  • Loading branch information
barrucadu committed Mar 11, 2021
1 parent b5686a8 commit 5a0e778
Show file tree
Hide file tree
Showing 5 changed files with 134 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

* BREAKING: Minimum ruby version supported is updated to 2.6
* BREAKING: `content_store_endpoint` helper now accepts a keyword argument instead of a boolean
* Add new account-api adapter with auth endpoints

# 69.3.0

Expand Down
14 changes: 14 additions & 0 deletions lib/gds_api.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
require "addressable"
require "plek"
require "time"
require "gds_api/account_api"
require "gds_api/asset_manager"
require "gds_api/calendars"
require "gds_api/content_store"
Expand All @@ -21,6 +22,19 @@

# @api documented
module GdsApi
# Creates a GdsApi::AccountApi adapter
#
# This will set a bearer token if a ACCOUNT_API_BEARER_TOKEN environment
# variable is set
#
# @return [GdsApi::AccountApi]
def self.account_api(options = {})
GdsApi::AccountApi.new(
Plek.find("account-api"),
{ bearer_token: ENV["ACCOUNT_API_BEARER_TOKEN"] }.merge(options),
)
end

# Creates a GdsApi::AssetManager adapter
#
# This will set a bearer token if a ASSET_MANAGER_BEARER_TOKEN environment
Expand Down
44 changes: 44 additions & 0 deletions lib/gds_api/account_api.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
require_relative "base"
require_relative "exceptions"

# Adapter for the Account API
#
# @see https://github.com/alphagov/account-api
# @api documented
class GdsApi::AccountApi < GdsApi::Base
# Get an OAuth sign-in URL to redirect the user to
#
# @param [String, nil] redirect_path path on GOV.UK to send the user to after authentication
# @param [String, nil] state_id identifier originally returned by #create_registration_state
#
# @return [Hash] An authentication URL and the OAuth state parameter (for CSRF protection)
def get_sign_in_url(redirect_path: nil, state_id: nil)
querystring = nested_query_string({ redirect_path: redirect_path, state_id: state_id }.compact)
get_json("#{endpoint}/api/oauth2/sign-in?#{querystring}")
end

# Validate an OAuth authentication response
#
# @param [String] code The OAuth code parameter, from the auth server.
# @param [String] state The OAuth state parameter, from the auth server.
#
# @return [Hash] The value for the govuk_account_session header, the path to redirect the user to, and the GA client ID (if there is one)
def validate_auth_response(code:, state:)
post_json("#{endpoint}/api/oauth2/callback", code: code, state: state)
end

# Register some initial state, to pass to get_sign_in_url, which is used to initialise the account if the user signs up
#
# @param [Hash, nil] attributes Initial attributes to store
#
# @return [Hash] The state ID to pass to get_sign_in_url
def create_registration_state(attributes:)
post_json("#{endpoint}/api/oauth2/state", attributes: attributes)
end

private

def nested_query_string(params)
Rack::Utils.build_nested_query(params)
end
end
42 changes: 42 additions & 0 deletions lib/gds_api/test_helpers/account_api.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
require "json"

module GdsApi
module TestHelpers
module AccountApi
ACCOUNT_API_ENDPOINT = Plek.find("account-api")

def stub_account_api_get_sign_in_url(redirect_path: nil, state_id: nil, auth_uri: "http://auth/provider", state: "state")
querystring = Rack::Utils.build_nested_query({ redirect_path: redirect_path, state_id: state_id }.compact)
stub_request(:get, "#{ACCOUNT_API_ENDPOINT}/api/oauth2/sign-in?#{querystring}")
.to_return(
status: 200,
body: { auth_uri: auth_uri, state: state }.to_json,
)
end

def stub_account_api_validates_auth_response(code: nil, state: nil, govuk_account_session: "govuk-account-session", redirect_path: "/", ga_client_id: "ga-client-id")
stub_request(:post, "#{ACCOUNT_API_ENDPOINT}/api/oauth2/callback")
.with(body: hash_including({ code: code, state: state }.compact))
.to_return(
status: 200,
body: { govuk_account_session: govuk_account_session, redirect_path: redirect_path, ga_client_id: ga_client_id }.to_json,
)
end

def stub_account_api_rejects_auth_response(code: nil, state: nil)
stub_request(:post, "#{ACCOUNT_API_ENDPOINT}/api/oauth2/callback")
.with(body: hash_including({ code: code, state: state }.compact))
.to_return(status: 401)
end

def stub_account_api_create_registration_state(attributes: nil, state_id: "state-id")
stub_request(:post, "#{ACCOUNT_API_ENDPOINT}/api/oauth2/state")
.with(body: hash_including({ attributes: attributes }.compact))
.to_return(
status: 200,
body: { state_id: state_id }.to_json,
)
end
end
end
end
33 changes: 33 additions & 0 deletions test/account_api_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
require "test_helper"
require "gds_api/account_api"
require "gds_api/test_helpers/account_api"

describe GdsApi::AccountApi do
include GdsApi::TestHelpers::AccountApi

let(:base_url) { Plek.find("account-api") }
let(:api_client) { GdsApi::AccountApi.new(base_url) }

it "gets a sign in URL" do
stub_account_api_get_sign_in_url(auth_uri: "https://www.example.com")
assert_equal("https://www.example.com", api_client.get_sign_in_url.to_hash["auth_uri"])
end

it "gives a session ID if the auth response validates" do
stub_account_api_validates_auth_response(code: "foo", state: "bar")
assert(!api_client.validate_auth_response(code: "foo", state: "bar")["govuk_account_session"].nil?)
end

it "throws a 401 if the auth response does not validate" do
stub_account_api_rejects_auth_response(code: "foo", state: "bar")

assert_raises GdsApi::HTTPUnauthorized do
api_client.validate_auth_response(code: "foo", state: "bar")
end
end

it "gets a state ID" do
stub_account_api_create_registration_state(attributes: { foo: "bar" }, state_id: "state-id")
assert_equal("state-id", api_client.create_registration_state(attributes: { foo: "bar" }).to_hash["state_id"])
end
end

0 comments on commit 5a0e778

Please sign in to comment.