Skip to content
This repository has been archived by the owner on Nov 18, 2024. It is now read-only.

Commit

Permalink
Merge pull request #31 from puppetlabs/improvements/optional-auth-cod…
Browse files Browse the repository at this point in the history
…e-url

Make auth code URL optional
  • Loading branch information
impl authored Jan 19, 2021
2 parents 0eef83a + ba33aeb commit 7ec3146
Show file tree
Hide file tree
Showing 7 changed files with 71 additions and 14 deletions.
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,9 @@ tokens.

#### `PUT` (`write`)

Retrieve an authorization code URL for the given state.
Retrieve an authorization code URL for the given state. Some providers may not
provide the plugin with information about this URL, in which case accessing this
endpoint will return an error.

| Name | Description | Type | Default | Required |
|------|-------------|------|---------|----------|
Expand Down Expand Up @@ -284,6 +286,6 @@ arbitrary OAuth 2 authorization code grant flow.

| Name | Description | Default | Required |
|------|-------------|---------|----------|
| `auth_code_url` | The URL to submit the initial authorization code request to. | None | Yes |
| `auth_code_url` | The URL to submit the initial authorization code request to. | None | No |
| `token_url` | The URL to use for exchanging temporary codes and refreshing access tokens. | None | Yes |
| `auth_style` | How to authenticate to the token URL. If specified, must be one of `in_header` or `in_params`. | Automatically detect | No |
5 changes: 4 additions & 1 deletion pkg/backend/path_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,13 +104,16 @@ func (b *backend) configAuthCodeURLUpdateOperation(ctx context.Context, req *log
return logical.ErrorResponse("missing state"), nil
}

url := c.Provider.Public(c.Config.ClientID).AuthCodeURL(
url, ok := c.Provider.Public(c.Config.ClientID).AuthCodeURL(
state.(string),
provider.WithRedirectURL(data.Get("redirect_url").(string)),
provider.WithScopes(data.Get("scopes").([]string)),
provider.WithURLParams(data.Get("auth_url_params").(map[string]string)),
provider.WithURLParams(c.Config.AuthURLParams),
)
if !ok {
return logical.ErrorResponse("authorization code URL not available"), nil
}

resp := &logical.Response{
Data: map[string]interface{}{
Expand Down
49 changes: 49 additions & 0 deletions pkg/backend/path_config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,3 +128,52 @@ func TestConfigAuthCodeURL(t *testing.T) {
assert.Equal(t, "geoff", qs.Get("foo")) // Configuration takes precedence!
assert.Equal(t, "quux", qs.Get("baz"))
}

func TestConfigClientCredentials(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()

storage := &logical.InmemStorage{}

b := New(Options{})
require.NoError(t, b.Setup(ctx, &logical.BackendConfig{}))

// Write configuration.
req := &logical.Request{
Operation: logical.UpdateOperation,
Path: configPath,
Storage: storage,
Data: map[string]interface{}{
"client_id": "abc",
"client_secret": "def",
"provider": "custom",
"provider_options": map[string]interface{}{
"token_url": "http://localhost/auth/token",
},
},
}

resp, err := b.HandleRequest(ctx, req)
require.NoError(t, err)
require.False(t, resp != nil && resp.IsError(), "response has error: %+v", resp.Error())
require.Nil(t, resp)

// Retrieve an auth code URL.
req = &logical.Request{
Operation: logical.UpdateOperation,
Path: configAuthCodeURLPath,
Storage: storage,
Data: map[string]interface{}{
"state": "qwerty",
"scopes": []string{"read", "write"},
"redirect_url": "http://example.com/redirect",
"auth_url_params": map[string]string{"foo": "bar", "baz": "quux"},
},
}

resp, err = b.HandleRequest(ctx, req)
require.NoError(t, err)
require.NotNil(t, resp)
require.True(t, resp.IsError())
require.EqualError(t, resp.Error(), "authorization code URL not available")
}
12 changes: 6 additions & 6 deletions pkg/provider/basic.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,11 @@ type basicOperations struct {
base *oauth2.Config
}

func (bo *basicOperations) AuthCodeURL(state string, opts ...AuthCodeURLOption) string {
func (bo *basicOperations) AuthCodeURL(state string, opts ...AuthCodeURLOption) (string, bool) {
if bo.base.Endpoint.AuthURL == "" {
return "", false
}

o := &AuthCodeURLOptions{}
o.ApplyOptions(opts)

Expand All @@ -38,7 +42,7 @@ func (bo *basicOperations) AuthCodeURL(state string, opts ...AuthCodeURLOption)
cfg.Scopes = o.Scopes
cfg.RedirectURL = o.RedirectURL

return cfg.AuthCodeURL(state, o.AuthCodeOptions...)
return cfg.AuthCodeURL(state, o.AuthCodeOptions...), true
}

func (bo *basicOperations) AuthCodeExchange(ctx context.Context, code string, opts ...AuthCodeExchangeOption) (*Token, error) {
Expand Down Expand Up @@ -175,10 +179,6 @@ func CustomFactory(ctx context.Context, vsn int, opts map[string]string) (Provid
return nil, ErrNoProviderWithVersion
}

if opts["auth_code_url"] == "" {
return nil, &OptionError{Option: "auth_code_url", Message: "authorization code URL is required"}
}

if opts["token_url"] == "" {
return nil, &OptionError{Option: "token_url", Message: "token URL is required"}
}
Expand Down
7 changes: 5 additions & 2 deletions pkg/provider/basic_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,15 @@ func TestBasicPublic(t *testing.T) {

ops := basicTest.Public("foo")

u, err := url.Parse(ops.AuthCodeURL(
authCodeURL, ok := ops.AuthCodeURL(
"state",
provider.WithRedirectURL("http://example.com/redirect"),
provider.WithScopes{"a", "b", "c"},
provider.WithURLParams{"baz": "quux"},
))
)
require.True(t, ok)

u, err := url.Parse(authCodeURL)
require.NoError(t, err)

assert.Equal(t, "http", u.Scheme)
Expand Down
2 changes: 1 addition & 1 deletion pkg/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func (o *AuthCodeURLOptions) ApplyOptions(opts []AuthCodeURLOption) {
// PublicOperations defines the operations for a client that only require
// knowledge of the client ID.
type PublicOperations interface {
AuthCodeURL(state string, opts ...AuthCodeURLOption) string
AuthCodeURL(state string, opts ...AuthCodeURLOption) (string, bool)
}

// AuthCodeExchangeOptions are options for the AuthCodeExchange operation.
Expand Down
4 changes: 2 additions & 2 deletions pkg/testutil/mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ type mockOperations struct {
clientCredentialsFn MockClientCredentialsFunc
}

func (mo *mockOperations) AuthCodeURL(state string, opts ...provider.AuthCodeURLOption) string {
func (mo *mockOperations) AuthCodeURL(state string, opts ...provider.AuthCodeURLOption) (string, bool) {
o := &provider.AuthCodeURLOptions{}
o.ApplyOptions(opts)

Expand All @@ -178,7 +178,7 @@ func (mo *mockOperations) AuthCodeURL(state string, opts ...provider.AuthCodeURL
Endpoint: MockEndpoint,
Scopes: o.Scopes,
RedirectURL: o.RedirectURL,
}).AuthCodeURL(state, o.AuthCodeOptions...)
}).AuthCodeURL(state, o.AuthCodeOptions...), true
}

func (mo *mockOperations) AuthCodeExchange(ctx context.Context, code string, opts ...provider.AuthCodeExchangeOption) (*provider.Token, error) {
Expand Down

0 comments on commit 7ec3146

Please sign in to comment.