Skip to content

Commit

Permalink
Merge pull request #25 from patrickeasters/offline_flow
Browse files Browse the repository at this point in the history
Add support for refresh token flow
  • Loading branch information
bserdar authored Oct 23, 2019
2 parents 74945ad + ff4fa87 commit 26c1a42
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 10 deletions.
5 changes: 5 additions & 0 deletions proto/oidc/cfg.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ type ServerProfile struct {
Form *HTMLFormConfig `yaml:"form,omitempty" mapstructure:"form,omitempty"`
Insecure bool
PasswordGrant *bool `yaml:"passwordgrant,omitempty"`
RefreshOnly *bool `yaml:"refreshonly,omitempty"`
AdditionalScopes []string `yaml:"additionalscopes,omitempty"`
}

Expand All @@ -21,6 +22,10 @@ func (s ServerProfile) Merge(in ServerProfile) ServerProfile {
if ret.PasswordGrant == nil {
ret.PasswordGrant = in.PasswordGrant
}
ret.RefreshOnly = s.RefreshOnly
if ret.RefreshOnly == nil {
ret.RefreshOnly = in.RefreshOnly
}
ret.Form = s.Form
if ret.Form == nil {
ret.Form = in.Form
Expand Down
25 changes: 18 additions & 7 deletions proto/oidc/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,10 @@ var oidcConnectWizard = []proto.SetupStep{
oidcCfg.Cfg.CallbackURL = in
return nil
}, GetDefault: func(remoteCfg interface{}) string { return remoteCfg.(*Config).CallbackURL }},
{Prompt: "OIDC flow (auth - authorization code flow, pwd - password grant flow, leave empty to use server profile default):",
{Prompt: "OIDC flow (auth - authorization code flow, pwd - password grant flow, refresh - refresh grant flow, leave empty to use server profile default):",
Parse: func(in string) error {
in = strings.TrimSpace(in)
if in == "pwd" || in == "auth" || in == "" {
if in == "pwd" || in == "auth" || in == "refresh" || in == "" {
oidcCfg.flow = in
} else {
return fmt.Errorf("Invalid entry %s, enter auth, pwd,or leave empty", in)
Expand Down Expand Up @@ -74,7 +74,7 @@ func init() {
cmd.Flags().StringVarP(&oidcCfg.Cfg.TokenAPI, "token-api", "a", "", "Token API (defaults to protocol/openid-connect/token)")
cmd.Flags().StringVarP(&oidcCfg.Cfg.AuthAPI, "auth-api", "t", "", "Auth API (defaults to protocol/openid-connect/auth)")
cmd.Flags().StringVarP(&oidcCfg.scopes, "scopes", "o", "", "Additional scopes to request from server (-o scope1,scope2,scope3)")
cmd.Flags().StringVarP(&oidcCfg.flow, "flow", "f", "", "Use authorization code flow (auth) or password grant flow (pwd)")
cmd.Flags().StringVarP(&oidcCfg.flow, "flow", "f", "", "Use authorization code flow (auth), password grant flow (pwd), or refresh token flow (refresh)")
if cfg.InsecureAllowed() {
cmd.Flags().BoolVarP(&oidcCfg.Cfg.Insecure, "insecure", "k", false, "Do not validate server certificates")
}
Expand Down Expand Up @@ -165,14 +165,25 @@ func parseOidc(c *cobra.Command) {
}
}
}
if oidcCfg.flow == "auth" {
switch oidcCfg.flow {
case "auth":
x := false
oidcCfg.Cfg.PasswordGrant = &x
} else if oidcCfg.flow == "pwd" {
oidcCfg.Cfg.RefreshOnly = &x
case "pwd":
x := true
y := false
oidcCfg.Cfg.PasswordGrant = &x
} else if oidcCfg.flow != "" {
log.Fatalf("Invalid flow: %s Use 'auth' or 'pwd'", oidcCfg.flow)
oidcCfg.Cfg.RefreshOnly = &y
case "refresh":
x := false
y := true
oidcCfg.Cfg.PasswordGrant = &x
oidcCfg.Cfg.RefreshOnly = &y
case "":
break
default:
log.Fatalf("Invalid flow: %s Use 'auth', 'pwd', or 'refresh'", oidcCfg.flow)
}

var formCfg HTMLFormConfig
Expand Down
11 changes: 9 additions & 2 deletions proto/oidc/protocol.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ func (p *Protocol) GetToken(request proto.TokenRequest) (string, interface{}, er
}

if userName == "" {
log.Fatalf("Username is required for oidc quth")
log.Fatalf("Username is required for oidc auth")
return "", nil, nil
}
var tok *TokenData
Expand Down Expand Up @@ -184,7 +184,14 @@ func (p *Protocol) GetToken(request proto.TokenRequest) (string, interface{}, er
ctx = context.WithValue(ctx, oauth2.HTTPClient, proto.GetHTTPClient())
conf.Scopes = append(conf.Scopes, config.AdditionalScopes...)
log.Debugf("Password grant: %v", config.PasswordGrant)
if config.PasswordGrant != nil && *config.PasswordGrant {
if config.RefreshOnly != nil && *config.RefreshOnly {
tok.RefreshToken = cfg.AskPasswordWithPrompt(fmt.Sprintf("Refresh token for %s: ", userName))
err := p.Refresh(tok, serverData)
if err != nil {
return "", nil, err
}
return tok.FormatToken(request.Out), p.Tokens, nil
} else if config.PasswordGrant != nil && *config.PasswordGrant {
var password string
if len(request.Password) > 0 {
password = request.Password
Expand Down
13 changes: 12 additions & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,22 @@ Took supports direct access grants. In this flow, took asks username and passwor
them to the authentication server.

```
took add oidc -n prod-direct -c 12345 -s abcdef -u https://myserver/realms/myrealm -p
took add oidc -n prod-direct -c 12345 -s abcdef -u https://myserver/realms/myrealm -f pwd
```
To use this, the authentication server must be configured to support
direct access grants flow for this client.

## Refresh Token Flow

Took supports using only refresh token grants. In this flow, took asks for a refresh token
to send to the authentication server. This is commonly used in conjunction with
offline refresh tokens.

```
took add oidc -n prod-refresh -c 12345 -u https://myserver/realms/myrealm -f refresh
```
To use this, you must already have obtained a refresh token via some other means
(usually from a web portal).

# Multiple users

Expand Down

0 comments on commit 26c1a42

Please sign in to comment.