6
6
"io/ioutil"
7
7
"net"
8
8
"net/http"
9
+ "net/url"
9
10
"os"
10
11
"os/exec"
11
12
"os/signal"
@@ -26,6 +27,7 @@ const (
26
27
defaultPort = "8250"
27
28
defaultCallbackHost = "localhost"
28
29
defaultCallbackMethod = "http"
30
+ defaultCallbackMode = "client"
29
31
defaultSkipBrowserLaunch = false
30
32
)
31
33
@@ -63,19 +65,42 @@ func (h *CLIHandler) Auth(c *api.Client, m map[string]string) (*api.Secret, erro
63
65
port = defaultPort
64
66
}
65
67
68
+ var vaultURL * url.URL
69
+ callbackMode , ok := m ["callbackmode" ]
70
+ if ! ok {
71
+ callbackMode = defaultCallbackMode
72
+ } else if callbackMode == "direct" {
73
+ vaultAddr := os .Getenv ("VAULT_ADDR" )
74
+ if vaultAddr != "" {
75
+ vaultURL , _ = url .Parse (vaultAddr )
76
+ }
77
+ }
78
+
66
79
callbackHost , ok := m ["callbackhost" ]
67
80
if ! ok {
68
- callbackHost = defaultCallbackHost
81
+ if vaultURL != nil {
82
+ callbackHost = vaultURL .Hostname ()
83
+ } else {
84
+ callbackHost = defaultCallbackHost
85
+ }
69
86
}
70
87
71
88
callbackMethod , ok := m ["callbackmethod" ]
72
89
if ! ok {
73
- callbackMethod = defaultCallbackMethod
90
+ if vaultURL != nil {
91
+ callbackMethod = vaultURL .Scheme
92
+ } else {
93
+ callbackMethod = defaultCallbackMethod
94
+ }
74
95
}
75
96
76
97
callbackPort , ok := m ["callbackport" ]
77
98
if ! ok {
78
- callbackPort = port
99
+ if vaultURL != nil {
100
+ callbackPort = vaultURL .Port () + "/v1/auth/" + mount
101
+ } else {
102
+ callbackPort = port
103
+ }
79
104
}
80
105
81
106
skipBrowserLaunch := defaultSkipBrowserLaunch
@@ -89,19 +114,47 @@ func (h *CLIHandler) Auth(c *api.Client, m map[string]string) (*api.Secret, erro
89
114
90
115
role := m ["role" ]
91
116
92
- authURL , clientNonce , err := fetchAuthURL (c , role , mount , callbackPort , callbackMethod , callbackHost )
117
+ authURL , clientNonce , secret , err := fetchAuthURL (c , role , mount , callbackPort , callbackMethod , callbackHost )
93
118
if err != nil {
94
119
return nil , err
95
120
}
96
121
97
- // Set up callback handler
98
- http .HandleFunc ("/oidc/callback" , callbackHandler (c , mount , clientNonce , doneCh ))
122
+ var pollInterval string
123
+ var interval int
124
+ var state string
125
+ var listener net.Listener
99
126
100
- listener , err := net .Listen ("tcp" , listenAddress + ":" + port )
101
- if err != nil {
102
- return nil , err
127
+ if secret != nil {
128
+ pollInterval , _ = secret .Data ["poll_interval" ].(string )
129
+ state , _ = secret .Data ["state" ].(string )
130
+ }
131
+ if callbackMode == "direct" {
132
+ if state == "" {
133
+ return nil , errors .New ("no state returned in direct callback mode" )
134
+ }
135
+ if pollInterval == "" {
136
+ return nil , errors .New ("no poll_interval returned in direct callback mode" )
137
+ }
138
+ interval , err = strconv .Atoi (pollInterval )
139
+ if err != nil {
140
+ return nil , errors .New ("cannot convert poll_interval " + pollInterval + " to integer" )
141
+ }
142
+ } else {
143
+ if state != "" {
144
+ return nil , errors .New ("state returned in client callback mode, try direct" )
145
+ }
146
+ if pollInterval != "" {
147
+ return nil , errors .New ("poll_interval returned in client callback mode" )
148
+ }
149
+ // Set up callback handler
150
+ http .HandleFunc ("/oidc/callback" , callbackHandler (c , mount , clientNonce , doneCh ))
151
+
152
+ listener , err := net .Listen ("tcp" , listenAddress + ":" + port )
153
+ if err != nil {
154
+ return nil , err
155
+ }
156
+ defer listener .Close ()
103
157
}
104
- defer listener .Close ()
105
158
106
159
// Open the default browser to the callback URL.
107
160
if ! skipBrowserLaunch {
@@ -114,6 +167,26 @@ func (h *CLIHandler) Auth(c *api.Client, m map[string]string) (*api.Secret, erro
114
167
}
115
168
fmt .Fprintf (os .Stderr , "Waiting for OIDC authentication to complete...\n " )
116
169
170
+ if callbackMode == "direct" {
171
+ data := map [string ]interface {}{
172
+ "state" : state ,
173
+ "client_nonce" : clientNonce ,
174
+ }
175
+ pollUrl := fmt .Sprintf ("auth/%s/oidc/poll" , mount )
176
+ for {
177
+ time .Sleep (time .Duration (interval ) * time .Second )
178
+
179
+ secret , err := c .Logical ().Write (pollUrl , data )
180
+ if err == nil {
181
+ return secret , nil
182
+ }
183
+ if ! strings .HasSuffix (err .Error (), "authorization_pending" ) {
184
+ return nil , err
185
+ }
186
+ // authorization is pending, try again
187
+ }
188
+ }
189
+
117
190
// Start local server
118
191
go func () {
119
192
err := http .Serve (listener , nil )
@@ -180,12 +253,12 @@ func callbackHandler(c *api.Client, mount string, clientNonce string, doneCh cha
180
253
}
181
254
}
182
255
183
- func fetchAuthURL (c * api.Client , role , mount , callbackport string , callbackMethod string , callbackHost string ) (string , string , error ) {
256
+ func fetchAuthURL (c * api.Client , role , mount , callbackport string , callbackMethod string , callbackHost string ) (string , string , * api. Secret , error ) {
184
257
var authURL string
185
258
186
259
clientNonce , err := base62 .Random (20 )
187
260
if err != nil {
188
- return "" , "" , err
261
+ return "" , "" , nil , err
189
262
}
190
263
191
264
redirectURI := fmt .Sprintf ("%s://%s:%s/oidc/callback" , callbackMethod , callbackHost , callbackport )
@@ -197,18 +270,18 @@ func fetchAuthURL(c *api.Client, role, mount, callbackport string, callbackMetho
197
270
198
271
secret , err := c .Logical ().Write (fmt .Sprintf ("auth/%s/oidc/auth_url" , mount ), data )
199
272
if err != nil {
200
- return "" , "" , err
273
+ return "" , "" , nil , err
201
274
}
202
275
203
276
if secret != nil {
204
277
authURL = secret .Data ["auth_url" ].(string )
205
278
}
206
279
207
280
if authURL == "" {
208
- return "" , "" , fmt .Errorf ("Unable to authorize role %q with redirect_uri %q. Check Vault logs for more information." , role , redirectURI )
281
+ return "" , "" , nil , fmt .Errorf ("Unable to authorize role %q with redirect_uri %q. Check Vault logs for more information." , role , redirectURI )
209
282
}
210
283
211
- return authURL , clientNonce , nil
284
+ return authURL , clientNonce , secret , nil
212
285
}
213
286
214
287
// isWSL tests if the binary is being run in Windows Subsystem for Linux
@@ -295,28 +368,38 @@ Usage: vault login -method=oidc [CONFIG K=V...]
295
368
296
369
https://accounts.google.com/o/oauth2/v2/...
297
370
298
- The default browser will be opened for the user to complete the login. Alternatively,
299
- the user may visit the provided URL directly.
371
+ The default browser will be opened for the user to complete the login.
372
+ Alternatively, the user may visit the provided URL directly.
300
373
301
374
Configuration:
302
375
303
376
role=<string>
304
377
Vault role of type "OIDC" to use for authentication.
305
378
379
+ callbackmode=<string>
380
+ Mode of callback: "direct" for direct connection to Vault or "client"
381
+ for connection to command line client (default: client).
382
+
306
383
listenaddress=<string>
307
- Optional address to bind the OIDC callback listener to (default: localhost).
384
+ Optional address to bind the OIDC callback listener to in client callback
385
+ mode (default: localhost).
308
386
309
387
port=<string>
310
- Optional localhost port to use for OIDC callback (default: 8250).
388
+ Optional localhost port to use for OIDC callback in client callback mode
389
+ (default: 8250).
311
390
312
391
callbackmethod=<string>
313
- Optional method to to use in OIDC redirect_uri (default: http).
392
+ Optional method to use in OIDC redirect_uri (default: the method from
393
+ $VAULT_ADDR in direct callback mode, else http)
314
394
315
395
callbackhost=<string>
316
- Optional callback host address to use in OIDC redirect_uri (default: localhost).
396
+ Optional callback host address to use in OIDC redirect_uri (default:
397
+ the host from $VAULT_ADDR in direct callback mode, else localhost).
317
398
318
399
callbackport=<string>
319
- Optional port to to use in OIDC redirect_uri (default: the value set for port).
400
+ Optional port to use in OIDC redirect_uri (default: the value set for
401
+ port in client callback mode, else the port from $VAULT_ADDR with an
402
+ added /v1/auth/<path> where <path> is from the login -path option).
320
403
321
404
skip_browser=<bool>
322
405
Toggle the automatic launching of the default browser to the login URL. (default: false).
0 commit comments