Resolve merge conflict, update rspec syntax and docs
[arvados.git] / lib / google / api_client / auth / installed_app.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 'webrick'
16 require 'launchy'
17
18 module Google
19   class APIClient
20
21     # Small helper for the sample apps for performing OAuth 2.0 flows from the command
22     # line or in any other installed app environment.
23     #
24     # @example
25     #
26     #    client = Google::APIClient.new
27     #    flow = Google::APIClient::InstalledAppFlow.new(
28     #      :client_id => '691380668085.apps.googleusercontent.com',
29     #      :client_secret => '...',
30     #      :scope => 'https://www.googleapis.com/auth/drive'
31     #    )
32     #    client.authorization = flow.authorize
33     #
34     class InstalledAppFlow
35       
36       RESPONSE_BODY = <<-HTML
37         <html>
38           <head>
39             <script>
40               function closeWindow() { 
41                 window.open('', '_self', '');
42                 window.close();
43               }
44               setTimeout(closeWindow, 10);
45             </script>
46           </head>
47           <body>You may close this window.</body>
48         </html>
49       HTML
50       
51       ##
52       # Configure the flow
53       #
54       # @param [Hash] options The configuration parameters for the client.
55       # @option options [Fixnum] :port
56       #   Port to run the embedded server on. Defaults to 9292
57       # @option options [String] :client_id 
58       #   A unique identifier issued to the client to identify itself to the
59       #   authorization server.
60       # @option options [String] :client_secret
61       #   A shared symmetric secret issued by the authorization server,
62       #   which is used to authenticate the client.
63       # @option options [String] :scope
64       #   The scope of the access request, expressed either as an Array
65       #   or as a space-delimited String.
66       #
67       # @see Signet::OAuth2::Client
68       def initialize(options)
69         @port = options[:port] || 9292
70         @authorization = Signet::OAuth2::Client.new({
71           :authorization_uri => 'https://accounts.google.com/o/oauth2/auth',
72           :token_credential_uri => 'https://accounts.google.com/o/oauth2/token',
73           :redirect_uri => "http://localhost:#{@port}/"}.update(options)
74         )
75       end
76       
77       ##
78       # Request authorization. Opens a browser and waits for response.
79       #
80       # @param [Google::APIClient::FileStorage] storage
81       #  Optional object that responds to :write_credentials, used to serialize
82       #  the OAuth 2 credentials after completing the flow.
83       #
84       # @return [Signet::OAuth2::Client]
85       #  Authorization instance, nil if user cancelled.
86       def authorize(storage=nil)
87         auth = @authorization
88     
89         server = WEBrick::HTTPServer.new(
90           :Port => @port,
91           :BindAddress =>"localhost",
92           :Logger => WEBrick::Log.new(STDOUT, 0),
93           :AccessLog => []
94         )
95         begin
96           trap("INT") { server.shutdown }
97
98           server.mount_proc '/' do |req, res|
99             auth.code = req.query['code']
100             if auth.code
101               auth.fetch_access_token!
102             end
103             res.status = WEBrick::HTTPStatus::RC_ACCEPTED
104             res.body = RESPONSE_BODY
105             server.stop
106           end
107
108           Launchy.open(auth.authorization_uri.to_s)
109           server.start
110         ensure
111           server.shutdown
112         end
113         if @authorization.access_token
114           if storage.respond_to?(:write_credentials)
115             storage.write_credentials(@authorization)
116           end
117           return @authorization
118         else
119           return nil
120         end
121       end
122     end
123
124   end
125 end
126