Skip to content

Commit

Permalink
feat: support for fetching ID tokens from google oauth2 endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
dazuma authored Mar 31, 2020
1 parent af9cfc2 commit 8915e56
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 1 deletion.
28 changes: 27 additions & 1 deletion lib/signet/oauth_2/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ class Client
# - <code>:scope</code> -
# The scope of the access request, expressed either as an Array
# or as a space-delimited String.
# - <code>:target_audience</code> -
# The final target audience for ID tokens fetched by this client,
# as a String.
# - <code>:state</code> -
# An arbitrary string designed to allow the client to maintain state.
# - <code>:code</code> -
Expand Down Expand Up @@ -101,6 +104,7 @@ def initialize options = {}
@principal = nil
@redirect_uri = nil
@scope = nil
@target_audience = nil
@state = nil
@username = nil
@access_type = nil
Expand Down Expand Up @@ -130,6 +134,9 @@ def initialize options = {}
# - <code>:scope</code> -
# The scope of the access request, expressed either as an Array
# or as a space-delimited String.
# - <code>:target_audience</code> -
# The final target audience for ID tokens fetched by this client,
# as a String.
# - <code>:state</code> -
# An arbitrary string designed to allow the client to maintain state.
# - <code>:code</code> -
Expand Down Expand Up @@ -181,6 +188,7 @@ def update! options = {}
self.client_id = options[:client_id] if options.key? :client_id
self.client_secret = options[:client_secret] if options.key? :client_secret
self.scope = options[:scope] if options.key? :scope
self.target_audience = options[:target_audience] if options.key? :target_audience
self.state = options[:state] if options.key? :state
self.code = options[:code] if options.key? :code
self.redirect_uri = options[:redirect_uri] if options.key? :redirect_uri
Expand Down Expand Up @@ -423,6 +431,22 @@ def scope= new_scope
end
end

##
# Returns the final target audience for ID tokens fetched by this client.
#
# @return [String] The target audience.
def target_audience
@target_audience
end

##
# Sets the final target audience for ID tokens fetched by this client.
#
# @param [String] new_target_audience The new target audience.
def target_audience= new_target_audience
@target_audience = new_target_audience
end

##
# Returns the client's current state value.
#
Expand Down Expand Up @@ -893,11 +917,13 @@ def to_jwt options = {}
"iat" => (now - skew).to_i
}
assertion["scope"] = scope.join " " unless scope.nil?
assertion["target_audience"] = target_audience unless target_audience.nil?
assertion["prn"] = person unless person.nil?
assertion["sub"] = sub unless sub.nil?
JWT.encode assertion, signing_key, signing_algorithm
end
# rubocop:disable Style/MethodDefParentheses
# rubocop:disable Metrics/AbcSize

##
# Serialize the client object to JSON.
Expand All @@ -912,6 +938,7 @@ def to_json(*)
"client_id" => client_id,
"client_secret" => client_secret,
"scope" => scope,
"target_audience" => target_audience,
"state" => state,
"code" => code,
"redirect_uri" => redirect_uri ? redirect_uri.to_s : nil,
Expand All @@ -930,7 +957,6 @@ def to_json(*)
)
end
# rubocop:enable Style/MethodDefParentheses
# rubocop:disable Metrics/AbcSize
# rubocop:disable Metrics/CyclomaticComplexity
# rubocop:disable Metrics/MethodLength
# rubocop:disable Metrics/PerceivedComplexity
Expand Down
40 changes: 40 additions & 0 deletions spec/signet/oauth_2/client_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1212,3 +1212,43 @@ def build_form_encoded_response payload
expect(params).to include("new_param" => "new_val")
end
end

describe Signet::OAuth2::Client, "configured for id tokens" do
before do
@key = OpenSSL::PKey::RSA.new 2048
@client = Signet::OAuth2::Client.new(
token_credential_uri: "https://oauth2.googleapis.com/token",
target_audience: "https://api.example.com",
issuer: "[email protected]",
audience: "https://hello.googleapis.com",
signing_key: @key
)
end

it "should set target_audience" do
expect(@client.target_audience).to eq "https://api.example.com"
end

it "should send a valid id token request" do
stubs = Faraday::Adapter::Test::Stubs.new do |stub|
stub.post "/token" do |env|
params = Addressable::URI.form_unencode env[:body]
claim, header = JWT.decode params.assoc("assertion").last, @key.public_key, true, algorithm: "RS256"
expect(params.assoc("grant_type")).to eq ["grant_type", "urn:ietf:params:oauth:grant-type:jwt-bearer"]
expect(claim["target_audience"]).to eq "https://api.example.com"
expect(claim["iss"]).to eq "[email protected]"
expect(claim["aud"]).to eq "https://hello.googleapis.com"
build_json_response(
"id_token" => "12345id",
"refresh_token" => "54321refresh",
"expires_in" => "3600"
)
end
end
connection = Faraday.new url: "https://www.google.com" do |builder|
builder.adapter :test, stubs
end
@client.fetch_access_token! connection: connection
expect(@client.id_token).to eq "12345id"
end
end

0 comments on commit 8915e56

Please sign in to comment.