Updated OAuth1 configuration code. Added docs.
[arvados.git] / lib / google / api_client / auth / oauth_1.rb
1 # Copyright 2010 Google Inc.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #      http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 require "addressable/uri"
16 require "oauth"
17
18 module Google #:nodoc:
19   class APIClient #:nodoc:
20     class OAuth1
21       ##
22       # The default OAuth 1.0a configuration values.  These may be overrided
23       # simply by passing in the same key to the constructor.
24       DEFAULTS = {
25         :request_token_uri =>
26           'https://www.google.com/accounts/OAuthGetRequestToken',
27         :authorization_uri =>
28           'https://www.google.com/accounts/OAuthAuthorizeToken',
29         :access_token_uri =>
30           'https://www.google.com/accounts/OAuthGetAccessToken',
31         :scopes => [],
32         :callback => OAuth::OUT_OF_BAND,
33         :display_name => nil,
34         :consumer_key => "anonymous",
35         :consumer_secret => "anonymous"
36       }
37       
38       ##
39       # A set of default configuration values specific to each service.  These
40       # may be overrided simply by passing in the same key to the constructor.
41       SERVICE_DEFAULTS = {
42         :buzz => {
43           :authorization_uri =>
44             'https://www.google.com/buzz/api/auth/OAuthAuthorizeToken',
45           :scopes => ["https://www.googleapis.com/auth/buzz"]
46         }
47       }
48
49       ##
50       # Creates a new OAuth 1.0a handler.  This object obtains the tokens from
51       # the provider and handles signing any requests to the API.
52       #
53       # @param [Hash] options
54       #   The configuration options.
55       #   <code>:request_token_uri</code>::
56       #     The OAuth endpoint for obtaining a request token.
57       #   <code>:authorization_uri</code>::
58       #     The OAuth endpoint for obtaining user permission.
59       #   <code>:access_token_uri</code>::
60       #     The OAuth endpoint for obtaining an access token.
61       #   <code>:scopes</code>::
62       #     An <code>Array</code> of scopes that define the access being
63       #     requested to the API.
64       #   <code>:callback</code>::
65       #     The URI the user will be redirected to if access is granted to the
66       #     API.  For development purposes, the special value
67       #     <code>OAuth::OUT_OF_BAND</code> may also be used.
68       #   <code>:display_name</code>::
69       #     A human-readable service name to present to the user when they
70       #     visit the <code>:authorization_uri</code>.
71       #   <code>:consumer_key</code>::
72       #     The consumer key you registered with the Google Accounts API.
73       #   <code>:consumer_secret</code>::
74       #     The consumer secret issued to you when you registered with the
75       #     Google Accounts API.
76       #
77       # @return [Google::APIClient::OAuth1] The OAuth 1.0a handler.
78       def initialize(options={})
79         if options[:service]
80           @options = DEFAULTS.merge(SERVICE_DEFAULTS[options[:service]])
81         else
82           @options = DEFAULTS.clone
83         end
84         @options.merge!(options)
85         @options[:request_token_uri] =
86           Addressable::URI.parse(@options[:request_token_uri])
87         @options[:authorization_uri] =
88           Addressable::URI.parse(@options[:authorization_uri])
89         @options[:access_token_uri] =
90           Addressable::URI.parse(@options[:access_token_uri])
91         if (@options[:request_token_uri].site !=
92             @options[:authorization_uri].site) ||
93             (@options[:request_token_uri].site !=
94             @options[:authorization_uri].site)
95           raise ArgumentError, "All OAuth endpoints must be on the same site."
96         end
97         @oauth_consumer = ::OAuth::Consumer.new(
98           @options[:consumer_key], @options[:consumer_secret], {
99             # This is an extremely unfortunate way to configure the consumer,
100             # but not worth forking or patching to resolve.  Yet.
101             :site               => @options[:request_token_uri].site,
102             :scheme             => :header,
103             :http_method        => :post,
104             :request_token_path => @options[:request_token_uri].request_uri,
105             :access_token_path  => @options[:access_token_uri].request_uri,
106             :authorize_path     => @options[:authorization_uri].request_uri
107           }
108         )
109       end
110
111       ##
112       # Returns the current request token.  Obtains a new request token if
113       # one hasn't already been obtained.
114       #
115       # @return [OAuth::RequestToken] The request token.
116       def request_token
117         oauth_parameters = {
118           :oauth_callback => @options[:callback]
119         }
120         app_parameters = {
121           :scope => @options[:scopes].join(" ")
122         }
123         if @options[:display_name]
124           app_parameters[:xoauth_displayname] = @options[:display_name]
125         end
126         return @request_token ||= @oauth_consumer.get_request_token(
127           oauth_parameters,
128           app_parameters
129         )
130       end
131
132       ##
133       # Sets the request token for the handler.
134       #
135       # @param [OAuth::RequestToken] new_request_token The request token.
136       def request_token=(new_request_token)
137         if new_request_token.kind_of?(OAuth::RequestToken)
138           @request_token = new_request_token
139         else
140           raise TypeError,
141             "Expected OAuth::RequestToken, got #{new_request_token.class}."
142         end
143       end
144
145       ##
146       # Returns the current access token.  Obtains a new access token if
147       # one hasn't already been obtained.  An request token must have already
148       # been obtained and authorized or this method will fail.
149       #
150       # @return [OAuth::AccessToken] The access token.
151       def access_token
152         return @access_token ||=
153           @oauth_consumer.get_access_token(self.request_token)
154       end
155
156       ##
157       # Sets the access token for the handler.
158       #
159       # @param [OAuth::AccessToken] new_access_token The access token.
160       def access_token=(new_access_token)
161         if new_access_token.kind_of?(OAuth::AccessToken)
162           @access_token = new_access_token
163         else
164           raise TypeError,
165             "Expected OAuth::AccessToken, got #{new_access_token.class}."
166         end
167       end
168
169       ##
170       # Returns the list of scopes for the handler.
171       #
172       # @return [Array] An <code>Array</code> of access scopes.
173       def scopes
174         return @options[:scopes]
175       end
176
177       ##
178       # Returns the consumer key.
179       #
180       # @return [String]
181       #   The consumer key you registered with the Google Accounts API.
182       def consumer_key
183         return @oauth_consumer.key
184       end
185
186       ##
187       # Returns the consumer key.
188       #
189       # @return [String]
190       #   The consumer secret issued to you when you registered with the
191       #   Google Accounts API.
192       def consumer_secret
193         return @oauth_consumer.secret
194       end
195
196       ##
197       # Returns the request token URI.
198       #
199       # @return [String]
200       #   The OAuth endpoint for obtaining a request token.
201       def request_token_uri
202         return @oauth_consumer.request_token_url
203       end
204
205       ##
206       # Returns the authorization endpoint URI.  This URI is used to construct
207       # the {#authorization_uri}.
208       #
209       # @return [String]
210       #   The OAuth endpoint for obtaining user permission.
211       def authorization_endpoint_uri
212         return @oauth_consumer.authorize_url
213       end
214
215       ##
216       # Builds the authorization URI that the user will be redirected to. 
217       # Note that this value is derived from the
218       # {#authorization_endpoint_uri}.
219       #
220       # @param [Hash] parameters
221       #   The extra URI query parameters appended to the
222       #   {#authorization_endpoint_uri}.
223       #
224       # @return [String]
225       #   The URI to redirect the user to to obtain permission.
226       def authorization_uri(parameters={})
227         return self.request_token.authorize_url(parameters)
228       end
229
230       ##
231       # Returns the access token URI.
232       #
233       # @return [String]
234       #   The OAuth endpoint for obtaining an access token.
235       def access_token_uri
236         return @oauth_consumer.access_token_url
237       end
238     end
239   end
240 end