Skip to content

Commit f0ca12f

Browse files
committed
Adds callback mode that is direct to vault
1 parent 24d3f19 commit f0ca12f

File tree

7 files changed

+308
-83
lines changed

7 files changed

+308
-83
lines changed

backend.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ func backend() *jwtAuthBackend {
5555
"login",
5656
"oidc/auth_url",
5757
"oidc/callback",
58+
"oidc/poll",
5859

5960
// Uncomment to mount simple UI handler for local development
6061
// "ui",

cli.go

Lines changed: 105 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"io/ioutil"
77
"net"
88
"net/http"
9+
"net/url"
910
"os"
1011
"os/exec"
1112
"os/signal"
@@ -26,6 +27,7 @@ const (
2627
defaultPort = "8250"
2728
defaultCallbackHost = "localhost"
2829
defaultCallbackMethod = "http"
30+
defaultCallbackMode = "client"
2931
defaultSkipBrowserLaunch = false
3032
)
3133

@@ -63,19 +65,42 @@ func (h *CLIHandler) Auth(c *api.Client, m map[string]string) (*api.Secret, erro
6365
port = defaultPort
6466
}
6567

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+
6679
callbackHost, ok := m["callbackhost"]
6780
if !ok {
68-
callbackHost = defaultCallbackHost
81+
if vaultURL != nil {
82+
callbackHost = vaultURL.Hostname()
83+
} else {
84+
callbackHost = defaultCallbackHost
85+
}
6986
}
7087

7188
callbackMethod, ok := m["callbackmethod"]
7289
if !ok {
73-
callbackMethod = defaultCallbackMethod
90+
if vaultURL != nil {
91+
callbackMethod = vaultURL.Scheme
92+
} else {
93+
callbackMethod = defaultCallbackMethod
94+
}
7495
}
7596

7697
callbackPort, ok := m["callbackport"]
7798
if !ok {
78-
callbackPort = port
99+
if vaultURL != nil {
100+
callbackPort = vaultURL.Port() + "/v1/auth/" + mount
101+
} else {
102+
callbackPort = port
103+
}
79104
}
80105

81106
skipBrowserLaunch := defaultSkipBrowserLaunch
@@ -89,19 +114,47 @@ func (h *CLIHandler) Auth(c *api.Client, m map[string]string) (*api.Secret, erro
89114

90115
role := m["role"]
91116

92-
authURL, clientNonce, err := fetchAuthURL(c, role, mount, callbackPort, callbackMethod, callbackHost)
117+
authURL, clientNonce, secret, err := fetchAuthURL(c, role, mount, callbackPort, callbackMethod, callbackHost)
93118
if err != nil {
94119
return nil, err
95120
}
96121

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
99126

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()
103157
}
104-
defer listener.Close()
105158

106159
// Open the default browser to the callback URL.
107160
if !skipBrowserLaunch {
@@ -114,6 +167,26 @@ func (h *CLIHandler) Auth(c *api.Client, m map[string]string) (*api.Secret, erro
114167
}
115168
fmt.Fprintf(os.Stderr, "Waiting for OIDC authentication to complete...\n")
116169

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+
117190
// Start local server
118191
go func() {
119192
err := http.Serve(listener, nil)
@@ -180,12 +253,12 @@ func callbackHandler(c *api.Client, mount string, clientNonce string, doneCh cha
180253
}
181254
}
182255

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) {
184257
var authURL string
185258

186259
clientNonce, err := base62.Random(20)
187260
if err != nil {
188-
return "", "", err
261+
return "", "", nil, err
189262
}
190263

191264
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
197270

198271
secret, err := c.Logical().Write(fmt.Sprintf("auth/%s/oidc/auth_url", mount), data)
199272
if err != nil {
200-
return "", "", err
273+
return "", "", nil, err
201274
}
202275

203276
if secret != nil {
204277
authURL = secret.Data["auth_url"].(string)
205278
}
206279

207280
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)
209282
}
210283

211-
return authURL, clientNonce, nil
284+
return authURL, clientNonce, secret, nil
212285
}
213286

214287
// 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...]
295368
296369
https://accounts.google.com/o/oauth2/v2/...
297370
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.
300373
301374
Configuration:
302375
303376
role=<string>
304377
Vault role of type "OIDC" to use for authentication.
305378
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+
306383
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).
308386
309387
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).
311390
312391
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)
314394
315395
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).
317398
318399
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).
320403
321404
skip_browser=<bool>
322405
Toggle the automatic launching of the default browser to the login URL. (default: false).
File renamed without changes.

0 commit comments

Comments
 (0)