@@ -38,6 +38,8 @@ class << self
38
38
# @option options [String] :domain G Suite domain for account-selection hint
39
39
# @option options [String] :online if `true` only a temporary access token will be provided,
40
40
# a long-lived refresh token will not be created and stored on the filesystem.
41
+ # @option options [String] :port port for local server to listen on to capture oauth browser redirect.
42
+ # Defaults to an out-of-band authentication process.
41
43
# @option options [::Google::Auth::ClientId] :google_id
42
44
def initialize ( options = { } )
43
45
@oauth_attempted = false
@@ -54,6 +56,7 @@ def initialize(options = {})
54
56
@client = options [ :client ] || Aws ::STS ::Client . new ( credentials : nil )
55
57
@domain = options [ :domain ]
56
58
@online = options [ :online ]
59
+ @port = options [ :port ]
57
60
58
61
# Use existing AWS credentials stored in the shared config if available.
59
62
# If this is `nil` or expired, #refresh will be called on the first AWS API service call
@@ -96,20 +99,50 @@ def google_oauth
96
99
uri_options [ :hd ] = @domain if @domain
97
100
uri_options [ :access_type ] = 'online' if @online
98
101
99
- require 'google/api_client/auth/installed_app'
100
- if defined? ( Launchy ) && Launchy ::Application ::Browser . new . app_list . any?
101
- ::Google ::APIClient ::InstalledAppFlow . new ( options ) . authorize ( storage , uri_options )
102
- else
103
- credentials = ::Google ::Auth ::UserRefreshCredentials . new (
104
- options . merge ( redirect_uri : 'urn:ietf:wg:oauth:2.0:oob' )
105
- )
106
- url = credentials . authorization_uri ( uri_options )
107
- print 'Open the following URL in the browser and enter the ' \
108
- "resulting code after authorization:\n #{ url } \n > "
109
- credentials . code = gets
110
- credentials . fetch_access_token!
111
- credentials . tap ( &storage . method ( :write_credentials ) )
102
+ credentials = ::Google ::Auth ::UserRefreshCredentials . new ( options )
103
+ credentials . code = get_oauth_code ( credentials , uri_options )
104
+ credentials . fetch_access_token!
105
+ credentials . tap ( &storage . method ( :write_credentials ) )
106
+ end
107
+
108
+ def get_oauth_code ( client , options )
109
+ raise 'fallback' unless @port
110
+ require 'launchy'
111
+ require 'webrick'
112
+ code = nil
113
+ server = WEBrick ::HTTPServer . new (
114
+ Port : @port ,
115
+ Logger : WEBrick ::Log . new ( STDOUT , 0 ) ,
116
+ AccessLog : [ ]
117
+ )
118
+ server . mount_proc '/' do |req , res |
119
+ code = req . query [ 'code' ]
120
+ res . status = 202
121
+ res . body = 'Login successful, you may close this browser window.'
122
+ server . stop
112
123
end
124
+ trap ( 'INT' ) { server . shutdown }
125
+ client . redirect_uri = "http://localhost:#{ @port } "
126
+ launchy = Launchy . open ( client . authorization_uri ( options ) . to_s )
127
+ server_thread = Thread . new do
128
+ begin
129
+ server . start
130
+ ensure server . shutdown
131
+ end
132
+ end
133
+ while server_thread . alive?
134
+ raise 'fallback' if !launchy . alive? && !launchy . value . success?
135
+ sleep 0.1
136
+ end
137
+ code || raise ( 'fallback' )
138
+ rescue StandardError
139
+ trap ( 'INT' , 'DEFAULT' )
140
+ # Fallback to out-of-band authentication if browser launch failed.
141
+ client . redirect_uri = 'oob'
142
+ url = client . authorization_uri ( options )
143
+ print "\n Open the following URL in a browser and enter the " \
144
+ "resulting code after authorization:\n #{ url } \n > "
145
+ gets
113
146
end
114
147
115
148
def refresh
0 commit comments