diff --git a/lib/google/api_client/auth/jwt_asserter.rb b/lib/google/api_client/auth/jwt_asserter.rb index 4d912724a..ea7d2738b 100644 --- a/lib/google/api_client/auth/jwt_asserter.rb +++ b/lib/google/api_client/auth/jwt_asserter.rb @@ -22,17 +22,31 @@ module Google # Generates access tokens using the JWT assertion profile. Requires a # service account & access to the private key. # - # @example + # @example Using Signet + # + # key = Google::APIClient::KeyUtils.load_from_pkcs12('client.p12', 'notasecret') + # client.authorization = Signet::OAuth2::Client.new( + # :token_credential_uri => 'https://accounts.google.com/o/oauth2/token', + # :audience => 'https://accounts.google.com/o/oauth2/token', + # :scope => https://www.googleapis.com/auth/prediction',, + # :issuer => 123456-abcdef@developer.gserviceaccount.com', + # :signing_key => key) + # client.authorization.fetch_access_token! + # client.execute(...) + # + # @example Deprecated version # # client = Google::APIClient.new # key = Google::APIClient::PKCS12.load_key('client.p12', 'notasecret') # service_account = Google::APIClient::JWTAsserter.new( - # '123456-abcdef@developer.gserviceaccount.com', - # 'https://www.googleapis.com/auth/prediction', - # key) + # '123456-abcdef@developer.gserviceaccount.com', + # 'https://www.googleapis.com/auth/prediction', + # key) # client.authorization = service_account.authorize # client.execute(...) # + # @deprecated + # Service accounts are now supported directly in Signet # @see https://developers.google.com/accounts/docs/OAuth2ServiceAccount class JWTAsserter # @return [String] ID/email of the issuing party @@ -43,9 +57,11 @@ module Google attr_accessor :skew # @return [String] Scopes to authorize attr_reader :scope - # @return [OpenSSL::PKey] key for signing assertions + # @return [String,OpenSSL::PKey] key for signing assertions attr_writer :key - + # @return [String] Algorithm used for signing + attr_accessor :algorithm + ## # Initializes the asserter for a service account. # @@ -53,14 +69,18 @@ module Google # Name/ID of the client issuing the assertion # @param [String, Array] scope # Scopes to authorize. May be a space delimited string or array of strings - # @param [OpenSSL::PKey] key - # RSA private key for signing assertions - def initialize(issuer, scope, key) + # @param [String,OpenSSL::PKey] key + # Key for signing assertions + # @param [String] algorithm + # Algorithm to use, either 'RS256' for RSA with SHA-256 + # or 'HS256' for HMAC with SHA-256 + def initialize(issuer, scope, key, algorithm = "RS256") self.issuer = issuer self.scope = scope self.expiry = 60 # 1 min default self.skew = 60 self.key = key + self.algorithm = algorithm end ## @@ -81,25 +101,6 @@ module Google end end - ## - # Builds & signs the assertion. - # - # @param [String] person - # Email address of a user, if requesting a token to act on their behalf - # @return [String] Encoded JWT - def to_jwt(person=nil) - now = Time.new - assertion = { - "iss" => @issuer, - "scope" => self.scope, - "aud" => "https://accounts.google.com/o/oauth2/token", - "exp" => (now + expiry).to_i, - "iat" => (now - skew).to_i - } - assertion['prn'] = person unless person.nil? - return JWT.encode(assertion, @key, "RS256") - end - ## # Request a new access token. # @@ -111,43 +112,26 @@ module Google # # @see Signet::OAuth2::Client.fetch_access_token! def authorize(person = nil, options={}) - assertion = self.to_jwt(person) - authorization = Signet::OAuth2::Client.new( - :token_credential_uri => 'https://accounts.google.com/o/oauth2/token' - ) - authorization.grant_type = 'urn:ietf:params:oauth:grant-type:jwt-bearer' - authorization.extension_parameters = { :assertion => assertion } + authorization = self.to_authorization authorization.fetch_access_token!(options) - return JWTAuthorization.new(authorization, self, person) + return authorization end - end - - ## - # Proxy for authorization that allows refreshing the access token - # using assertions instead of refresh tokens. - class JWTAuthorization < DelegateClass(Signet::OAuth2::Client) + ## - # Initialize the proxy + # Builds a Signet OAuth2 client # - # @param [Signet::OAuth2::Client] authorization - # Original authorization instance - # @param [Google::APIClient::JWTAsserter] - # Asserter for generating new access tokens - # @param [String] person - # Optional target user if impersonating. - def initialize(authorization, asserter, person = nil) - @asserter = asserter - @person = person - super(authorization) - end - - ## - # @see Signet::OAuth2::Client#fetch_access_token! - def fetch_access_token!(options={}) - new_authorization = @asserter.authorize(@person, options) - __setobj__(new_authorization) - self - end + # @return [Signet::OAuth2::Client] Access token + def to_authorization(person = nil) + return Signet::OAuth2::Client.new( + :token_credential_uri => 'https://accounts.google.com/o/oauth2/token', + :audience => 'https://accounts.google.com/o/oauth2/token', + :scope => self.scope, + :issuer => @issuer, + :signing_key => @key, + :signing_algorithm => @algorithm, + :person => person + ) + end end end end diff --git a/spec/google/api_client/service_account_spec.rb b/spec/google/api_client/service_account_spec.rb index e338dcf21..431e5647e 100644 --- a/spec/google/api_client/service_account_spec.rb +++ b/spec/google/api_client/service_account_spec.rb @@ -20,12 +20,14 @@ fixtures_path = File.expand_path('../../../fixtures', __FILE__) describe Google::APIClient::KeyUtils do it 'should read PKCS12 files from the filesystem' do + pending "Reading from PKCS12 not supported on jruby" if RUBY_PLATFORM == 'java' path = File.expand_path('files/privatekey.p12', fixtures_path) key = Google::APIClient::KeyUtils.load_from_pkcs12(path, 'notasecret') key.should_not == nil end it 'should read PKCS12 files from loaded files' do + pending "Reading from PKCS12 not supported on jruby" if RUBY_PLATFORM == 'java' path = File.expand_path('files/privatekey.p12', fixtures_path) content = File.read(path) key = Google::APIClient::KeyUtils.load_from_pkcs12(content, 'notasecret') @@ -56,7 +58,7 @@ describe Google::APIClient::JWTAsserter do it 'should generate valid JWTs' do asserter = Google::APIClient::JWTAsserter.new('client1', 'scope1 scope2', @key) - jwt = asserter.to_jwt + jwt = asserter.to_authorization.to_jwt jwt.should_not == nil claim = JWT.decode(jwt, @key.public_key, true)