Skip to content

Commit 4809f66

Browse files
huntspatryk
authored andcommitted
Add support for the new API Tokens (beta) auth scheme (#326)
* Add support for the new API Tokens auth scheme
1 parent 711263f commit 4809f66

File tree

3 files changed

+48
-3
lines changed

3 files changed

+48
-3
lines changed

cloudflare.go

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,14 @@ import (
2020
)
2121

2222
const apiURL = "https://api.cloudflare.com/client/v4"
23+
2324
const (
2425
// AuthKeyEmail specifies that we should authenticate with API key and email address
2526
AuthKeyEmail = 1 << iota
2627
// AuthUserService specifies that we should authenticate with a User-Service key
2728
AuthUserService
29+
// AuthToken specifies that we should authenticate with an API Token
30+
AuthToken
2831
)
2932

3033
// API holds the configuration for the current API client. A client should not
@@ -33,6 +36,7 @@ type API struct {
3336
APIKey string
3437
APIEmail string
3538
APIUserServiceKey string
39+
APIToken string
3640
BaseURL string
3741
OrganizationID string
3842
UserAgent string
@@ -92,6 +96,23 @@ func New(key, email string, opts ...Option) (*API, error) {
9296
return api, nil
9397
}
9498

99+
// NewWithAPIToken creates a new Cloudflare v4 API client using API Tokens
100+
func NewWithAPIToken(token string, opts ...Option) (*API, error) {
101+
if token == "" {
102+
return nil, errors.New(errEmptyAPIToken)
103+
}
104+
105+
api, err := newClient(opts...)
106+
if err != nil {
107+
return nil, err
108+
}
109+
110+
api.APIToken = token
111+
api.authType = AuthToken
112+
113+
return api, nil
114+
}
115+
95116
// NewWithUserServiceKey creates a new Cloudflare v4 API client using service key authentication.
96117
func NewWithUserServiceKey(key string, opts ...Option) (*API, error) {
97118
if key == "" {
@@ -109,7 +130,7 @@ func NewWithUserServiceKey(key string, opts ...Option) (*API, error) {
109130
return api, nil
110131
}
111132

112-
// SetAuthType sets the authentication method (AuthyKeyEmail or AuthUserService).
133+
// SetAuthType sets the authentication method (AuthKeyEmail, AuthToken, or AuthUserService).
113134
func (api *API) SetAuthType(authType int) {
114135
api.authType = authType
115136
}
@@ -141,7 +162,7 @@ func (api *API) ZoneIDByName(zoneName string) (string, error) {
141162
}
142163

143164
// makeRequest makes a HTTP request and returns the body as a byte slice,
144-
// closing it before returnng. params will be serialized to JSON.
165+
// closing it before returning. params will be serialized to JSON.
145166
func (api *API) makeRequest(method, uri string, params interface{}) ([]byte, error) {
146167
return api.makeRequestWithAuthType(context.TODO(), method, uri, params, api.authType)
147168
}
@@ -186,7 +207,7 @@ func (api *API) makeRequestWithAuthTypeAndHeaders(ctx context.Context, method, u
186207
}
187208
if i > 0 {
188209
// expect the backoff introduced here on errored requests to dominate the effect of rate limiting
189-
// dont need a random component here as the rate limiter should do something similar
210+
// don't need a random component here as the rate limiter should do something similar
190211
// nb time duration could truncate an arbitrary float. Since our inputs are all ints, we should be ok
191212
sleepDuration := time.Duration(math.Pow(2, float64(i-1)) * float64(api.retryPolicy.MinRetryDelay))
192213

@@ -277,13 +298,18 @@ func (api *API) request(ctx context.Context, method, uri string, reqBody io.Read
277298
copyHeader(combinedHeaders, api.headers)
278299
copyHeader(combinedHeaders, headers)
279300
req.Header = combinedHeaders
301+
280302
if authType&AuthKeyEmail != 0 {
281303
req.Header.Set("X-Auth-Key", api.APIKey)
282304
req.Header.Set("X-Auth-Email", api.APIEmail)
283305
}
284306
if authType&AuthUserService != 0 {
285307
req.Header.Set("X-Auth-User-Service-Key", api.APIUserServiceKey)
286308
}
309+
if authType&AuthToken != 0 {
310+
req.Header.Set("Authorization", "Bearer "+api.APIToken)
311+
}
312+
287313
if api.UserAgent != "" {
288314
req.Header.Set("User-Agent", api.UserAgent)
289315
}

cloudflare_test.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ func TestClient_Headers(t *testing.T) {
7272
assert.Equal(t, "GET", r.Method, "Expected method 'GET', got %s", r.Method)
7373
assert.Empty(t, r.Header.Get("X-Auth-Email"))
7474
assert.Empty(t, r.Header.Get("X-Auth-Key"))
75+
assert.Empty(t, r.Header.Get("Authorization"))
7576
assert.Equal(t, "userservicekey", r.Header.Get("X-Auth-User-Service-Key"))
7677
assert.Equal(t, "application/json", r.Header.Get("Content-Type"))
7778
})
@@ -87,11 +88,28 @@ func TestClient_Headers(t *testing.T) {
8788
assert.Equal(t, "GET", r.Method, "Expected method 'GET', got %s", r.Method)
8889
assert.Empty(t, r.Header.Get("X-Auth-Email"))
8990
assert.Empty(t, r.Header.Get("X-Auth-Key"))
91+
assert.Empty(t, r.Header.Get("Authorization"))
9092
assert.Equal(t, "userservicekey", r.Header.Get("X-Auth-User-Service-Key"))
9193
assert.Equal(t, "application/json", r.Header.Get("Content-Type"))
9294
})
9395
client.UserDetails()
9496
teardown()
97+
98+
// it should set Authorization and omit others credential headers when using NewWithAPIToken
99+
setup()
100+
client, err = NewWithAPIToken("my-api-token")
101+
assert.NoError(t, err)
102+
client.BaseURL = server.URL
103+
mux.HandleFunc("/zones/123456", func(w http.ResponseWriter, r *http.Request) {
104+
assert.Equal(t, "GET", r.Method, "Expected method 'GET', got %s", r.Method)
105+
assert.Empty(t, r.Header.Get("X-Auth-Email"))
106+
assert.Empty(t, r.Header.Get("X-Auth-Key"))
107+
assert.Empty(t, r.Header.Get("X-Auth-User-Service-Key"))
108+
assert.Equal(t, "Bearer my-api-token", r.Header.Get("Authorization"))
109+
assert.Equal(t, "application/json", r.Header.Get("Content-Type"))
110+
})
111+
client.UserDetails()
112+
teardown()
95113
}
96114

97115
func TestClient_RetryCanSucceedAfterErrors(t *testing.T) {

errors.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package cloudflare
33
// Error messages
44
const (
55
errEmptyCredentials = "invalid credentials: key & email must not be empty"
6+
errEmptyAPIToken = "invalid credentials: API Token must not be empty"
67
errMakeRequestError = "error from makeRequest"
78
errUnmarshalError = "error unmarshalling the JSON response"
89
errRequestNotSuccessful = "error reported by API"

0 commit comments

Comments
 (0)