Module: OAuthSystemSpecHelpers

Defined in:
decidim-core/lib/decidim/core/test/shared_examples/oauth_application_examples.rb

Instance Method Summary collapse

Instance Method Details

#oauth_api_authorization(scope) ⇒ Object



23
24
25
26
# File 'decidim-core/lib/decidim/core/test/shared_examples/oauth_application_examples.rb', line 23

def oauth_api_authorization(scope)
  token = oauth_api_token(scope)
  %w(token_type access_token).map { |key| token[key] }.join(" ")
end

#oauth_api_token(scope) ⇒ Object

Runs through the whole OAuth authorization code flow to fetch a valid OAuth token with the given scopes.



30
31
32
33
34
35
36
37
38
39
40
41
42
# File 'decidim-core/lib/decidim/core/test/shared_examples/oauth_application_examples.rb', line 30

def oauth_api_token(scope)
  authorization = visit_oauth_authorization_page(scope)

  click_on "Authorize application"

  expect(page).to have_content("Authorization code:")

  page_params = Rack::Utils.parse_query(URI.parse(page.current_url).query)
  raise "Invalid OAuth state returned." if page_params["state"] != authorization[:state]

  code = page_params["code"]
  oauth_fetch_token(code, authorization[:verifier])
end

#oauth_authorization_details(scope) ⇒ Object



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'decidim-core/lib/decidim/core/test/shared_examples/oauth_application_examples.rb', line 44

def oauth_authorization_details(scope)
  # https://datatracker.ietf.org/doc/html/rfc6749#appendix-A.5
  chars = Array(0x20..0x7E).map(&:chr)
  state = Base64.urlsafe_encode64(oauth_random_string(chars, 36), padding: false)

  # https://datatracker.ietf.org/doc/html/rfc7636#section-4.1
  chars = Array(0x30..0x39).map(&:chr) + Array(0x41..0x5A).map(&:chr) +
          Array(0x61..0x7A).map(&:chr) + "-._~".chars
  verifier = oauth_random_string(chars, 96)
  challenge = Base64.urlsafe_encode64(Digest::SHA256.digest(verifier), padding: false)

  # https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.1
  # https://datatracker.ietf.org/doc/html/rfc7636
  params = {
    response_type: "code",
    client_id: oauth_application.uid,
    redirect_uri:,
    scope:,
    state:,
    code_challenge: challenge,
    code_challenge_method: "S256"
  }

  { params:, state:, verifier: }
end

#oauth_fetch_token(code, verifier) ⇒ Object



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'decidim-core/lib/decidim/core/test/shared_examples/oauth_application_examples.rb', line 70

def oauth_fetch_token(code, verifier)
  # https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.3
  # https://datatracker.ietf.org/doc/html/rfc7636
  uri = URI.parse("#{organization_host}/oauth/token")
  request = Net::HTTP::Post.new(uri)
  data = {
    grant_type: "authorization_code",
    code:,
    redirect_uri:,
    client_id: oauth_application.uid,
    code_verifier: verifier
  }
  data[:client_secret] = oauth_application.secret if confidential
  request.set_form_data(data)

  http = Net::HTTP.new(uri.host, uri.port)
  response = http.request(request)
  raise "Invalid response from token request: #{response.code}" unless response.is_a?(Net::HTTPOK)
  raise "Unexpected content type from token request: #{response.content_type}" unless response.content_type == "application/json"

  JSON.parse(response.body)
end

#oauth_random_string(chars, length) ⇒ Object



93
94
95
# File 'decidim-core/lib/decidim/core/test/shared_examples/oauth_application_examples.rb', line 93

def oauth_random_string(chars, length)
  Array.new(length) { chars[SecureRandom.random_number(chars.length)] }.join
end

#visit_oauth_authorization_page(scope = nil) ⇒ Object



6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# File 'decidim-core/lib/decidim/core/test/shared_examples/oauth_application_examples.rb', line 6

def visit_oauth_authorization_page(scope = nil)
  scope ||= self.scope
  authorization = oauth_authorization_details(scope)
  visit "/oauth/authorize?#{URI.encode_www_form(authorization[:params])}"
  expect(page).to have_content("Log in")

  within "#session_new_user" do
    fill_in :session_user_email, with: user.email
    fill_in :session_user_password, with: "decidim123456789"
    find("*[type=submit]").click
  end

  expect(page).to have_content("Logged in successfully.")

  authorization
end