Skip to content

Commit

Permalink
feat: add --no-shutdown flag to "hydra token user" to prevent auto-te…
Browse files Browse the repository at this point in the history
…rmination (#2382) (#2386)

Closes #2382

Co-authored-by: hackerman <3372410+aeneasr@users.noreply.github.com>
  • Loading branch information
verzac and aeneasr committed Mar 7, 2021
1 parent 48b75ab commit a17d10e
Showing 1 changed file with 48 additions and 28 deletions.
76 changes: 48 additions & 28 deletions cmd/token_user.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ var tokenUserResult = template.Must(template.New("").Parse(`<html>
<li>Expires in: <code>{{ .Expiry }}</code></li>
<li>ID Token: <code>{{ .IDToken }}</code></li>
</ul>
{{ if .DisplayBackButton }}
<a href="{{ .BackURL }}">Back to Welcome Page</a>
{{ end }}
</body>
</html>`))

Expand All @@ -92,7 +95,7 @@ var tokenUserCmd = &cobra.Command{
This command will help you to see if ORY Hydra has been configured properly.
This command must not be used for anything else than manual testing or demo purposes. The server will terminate on error
and success.`,
and success, unless if the --no-shutdown flag is provided.`,
Run: func(cmd *cobra.Command, args []string) {
/* #nosec G402 - we want to support dev environments, hence tls trickery */
ctx := context.WithValue(context.Background(), oauth2.HTTPClient, &http.Client{Transport: &http.Transport{
Expand All @@ -108,6 +111,7 @@ and success.`,
backend := flagx.MustGetString(cmd, "token-url")
frontend := flagx.MustGetString(cmd, "auth-url")
audience := flagx.MustGetStringSlice(cmd, "audience")
noShutdown := flagx.MustGetBool(cmd, "no-shutdown")

clientID := flagx.MustGetString(cmd, "client-id")
clientSecret := flagx.MustGetString(cmd, "client-secret")
Expand Down Expand Up @@ -145,19 +149,23 @@ and success.`,
Scopes: scopes,
}

state, err := randx.RuneSequence(24, randx.AlphaLower)
cmdx.Must(err, "Could not generate random state: %s", err)

nonce, err := randx.RuneSequence(24, randx.AlphaLower)
cmdx.Must(err, "Could not generate random state: %s", err)

authCodeURL := conf.AuthCodeURL(
string(state),
oauth2.SetAuthURLParam("audience", strings.Join(audience, "+")),
oauth2.SetAuthURLParam("nonce", string(nonce)),
oauth2.SetAuthURLParam("prompt", strings.Join(prompt, "+")),
oauth2.SetAuthURLParam("max_age", strconv.Itoa(maxAge)),
)
var generateAuthCodeURL = func() (string, []rune) {
state, err := randx.RuneSequence(24, randx.AlphaLower)
cmdx.Must(err, "Could not generate random state: %s", err)

nonce, err := randx.RuneSequence(24, randx.AlphaLower)
cmdx.Must(err, "Could not generate random state: %s", err)

authCodeURL := conf.AuthCodeURL(
string(state),
oauth2.SetAuthURLParam("audience", strings.Join(audience, "+")),
oauth2.SetAuthURLParam("nonce", string(nonce)),
oauth2.SetAuthURLParam("prompt", strings.Join(prompt, "+")),
oauth2.SetAuthURLParam("max_age", strconv.Itoa(maxAge)),
)
return authCodeURL, state
}
authCodeURL, state := generateAuthCodeURL()

if !flagx.MustGetBool(cmd, "no-open") {
_ = webbrowser.Open(serverLocation) // ignore errors
Expand Down Expand Up @@ -186,6 +194,14 @@ and success.`,
defer cancel()
_ = server.Shutdown(ctx)
}
var onDone = func() {
if !noShutdown {
go shutdown()
} else {
// regenerate because we don't want to shutdown and we don't want to reuse nonce & state
authCodeURL, state = generateAuthCodeURL()
}
}

r.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
_ = tokenUserWelcome.Execute(w, &struct{ URL string }{URL: authCodeURL})
Expand All @@ -210,7 +226,7 @@ and success.`,
Debug: r.URL.Query().Get("error_debug"),
})

go shutdown()
onDone()
return
}

Expand All @@ -222,7 +238,7 @@ and success.`,
Name: "States do not match",
Description: "Expected state " + string(state) + " but got " + r.URL.Query().Get("state"),
})
go shutdown()
onDone()
return
}

Expand All @@ -235,7 +251,7 @@ and success.`,
_ = tokenUserError.Execute(w, &ed{
Name: err.Error(),
})
go shutdown()
onDone()
return
}

Expand All @@ -246,20 +262,23 @@ and success.`,
fmt.Printf("ID Token:\n\t%v\n\n", idt)

_ = tokenUserResult.Execute(w, struct {
AccessToken string
RefreshToken string
Expiry string
IDToken string
AccessToken string
RefreshToken string
Expiry string
IDToken string
BackURL string
DisplayBackButton bool
}{
AccessToken: token.AccessToken,
RefreshToken: token.RefreshToken,
Expiry: token.Expiry.Format(time.RFC1123),
IDToken: fmt.Sprintf("%v", idt),
AccessToken: token.AccessToken,
RefreshToken: token.RefreshToken,
Expiry: token.Expiry.Format(time.RFC1123),
IDToken: fmt.Sprintf("%v", idt),
BackURL: serverLocation,
DisplayBackButton: noShutdown,
})

go shutdown()
onDone()
})

var err error
if isSSL {
err = server.ListenAndServeTLS("", "")
} else {
Expand All @@ -276,6 +295,7 @@ func init() {
tokenUserCmd.Flags().StringSlice("scope", []string{"offline", "openid"}, "Request OAuth2 scope")
tokenUserCmd.Flags().StringSlice("prompt", []string{}, "Set the OpenID Connect prompt parameter")
tokenUserCmd.Flags().Int("max-age", 0, "Set the OpenID Connect max_age parameter")
tokenUserCmd.Flags().Bool("no-shutdown", false, "Do not terminate on success/error. State and nonce will be regenerated when auth flow has completed (either due to an error or success).")

tokenUserCmd.Flags().String("client-id", os.Getenv("OAUTH2_CLIENT_ID"), "Use the provided OAuth 2.0 Client ID, defaults to environment variable OAUTH2_CLIENT_ID")
tokenUserCmd.Flags().String("client-secret", os.Getenv("OAUTH2_CLIENT_SECRET"), "Use the provided OAuth 2.0 Client Secret, defaults to environment variable OAUTH2_CLIENT_SECRET")
Expand Down

0 comments on commit a17d10e

Please sign in to comment.