Skip to content

Commit 5e481e9

Browse files
author
amuraru
committed
OAUTH support for AdobeIMS
1 parent f091a9d commit 5e481e9

File tree

9 files changed

+488
-17
lines changed

9 files changed

+488
-17
lines changed

docs/oauth-configuration.md

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
# OAuth Configuration Guide
2+
3+
kminion supports OAUTHBEARER SASL mechanism with two flavors:
4+
5+
1. **Generic OAuth** - Standard OAuth 2.0 client credentials flow
6+
2. **Adobe IMS OAuth** - Adobe Identity Management System integration
7+
8+
## Generic OAuth Configuration
9+
10+
For standard OAuth 2.0 providers, configure the following:
11+
12+
```yaml
13+
kafka:
14+
sasl:
15+
enabled: true
16+
mechanism: "OAUTHBEARER"
17+
oauth:
18+
# Leave type empty for generic OAuth
19+
type: ""
20+
tokenEndpoint: "https://your-oauth-provider.com/oauth/token"
21+
clientId: "your-client-id"
22+
clientSecret: "your-client-secret"
23+
scope: "kafka"
24+
```
25+
26+
### Environment Variables
27+
28+
```bash
29+
KAFKA_SASL_ENABLED=true
30+
KAFKA_SASL_MECHANISM=OAUTHBEARER
31+
KAFKA_SASL_OAUTH_TYPE=""
32+
KAFKA_SASL_OAUTH_TOKENENDPOINT=https://your-oauth-provider.com/oauth/token
33+
KAFKA_SASL_OAUTH_CLIENTID=your-client-id
34+
KAFKA_SASL_OAUTH_CLIENTSECRET=your-client-secret
35+
KAFKA_SASL_OAUTH_SCOPE=kafka
36+
```
37+
38+
## Adobe IMS OAuth Configuration
39+
40+
For Adobe Identity Management System, configure the following:
41+
42+
```yaml
43+
kafka:
44+
sasl:
45+
enabled: true
46+
mechanism: "OAUTHBEARER"
47+
oauth:
48+
type: "AdobeIMS"
49+
tokenEndpoint: "https://ims-na1.adobelogin.com"
50+
clientId: "your-ims-client-id"
51+
clientSecret: "your-ims-client-secret"
52+
additional:
53+
clientCode: "your-ims-code"
54+
```
55+
56+
### Environment Variables
57+
58+
```bash
59+
KAFKA_SASL_ENABLED=true
60+
KAFKA_SASL_MECHANISM=OAUTHBEARER
61+
KAFKA_SASL_OAUTH_TYPE=AdobeIMS
62+
KAFKA_SASL_OAUTH_TOKENENDPOINT=https://ims-na1.adobelogin.com
63+
KAFKA_SASL_OAUTH_CLIENTID=your-ims-client-id
64+
KAFKA_SASL_OAUTH_CLIENTSECRET=your-ims-client-secret
65+
KAFKA_SASL_OAUTH_ADDITIONAL_CLIENTCODE=your-ims-code
66+
```
67+
68+
## How It Works
69+
70+
### Generic OAuth Flow
71+
72+
1. kminion uses the client credentials grant type
73+
2. Sends a POST request to the token endpoint with client ID and secret
74+
3. Receives an access token
75+
4. Uses the token for Kafka authentication
76+
77+
### Adobe IMS Flow
78+
79+
1. kminion uses the Adobe IMS Go SDK (`github.com/adobe/ims-go`)
80+
2. Creates an IMS client with the configured endpoint
81+
3. Requests a token using the IMS-specific authentication flow
82+
4. Uses the access token for Kafka authentication
83+
84+
## Switching Between Providers
85+
86+
The `type` field determines which OAuth provider to use:
87+
88+
- **Empty or omitted**: Uses generic OAuth with `tokenEndpoint`, `clientId`, `clientSecret`, and `scope`
89+
- **"AdobeIMS"**: Uses Adobe IMS with `tokenEndpoint`, `clientId`, `clientSecret`, and `additional.clientCode`
90+
91+
### Field Reuse
92+
93+
Both generic OAuth and Adobe IMS share common fields:
94+
- `tokenEndpoint`: OAuth token endpoint URL (for Adobe IMS, this is the IMS endpoint)
95+
- `clientId`: OAuth client ID
96+
- `clientSecret`: OAuth client secret
97+
98+
Provider-specific fields go in the `additional` block:
99+
- For Adobe IMS: `clientCode` (authorization code)
100+
101+
## Validation
102+
103+
- **Generic OAuth**: Validates that `tokenEndpoint`, `clientId`, and `clientSecret` are provided
104+
- **Adobe IMS**: Validation happens during client creation when connecting to Kafka
105+
106+
## Dependencies
107+
108+
- Generic OAuth: Built-in HTTP client
109+
- Adobe IMS: `github.com/adobe/ims-go` v0.16.1+
110+

docs/reference-config.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,20 @@ kafka:
6161
enableFast: true
6262
# OAUTHBEARER config properties
6363
oauth:
64+
# Type of OAuth provider. Valid values: AdobeIMS (leave empty for generic OAuth)
65+
type: ""
66+
# OAuth token endpoint (used by both generic OAuth and provider-specific types like AdobeIMS)
6467
tokenEndpoint: ""
68+
# Client ID (used by both generic OAuth and provider-specific types)
6569
clientId: ""
70+
# Client secret (used by both generic OAuth and provider-specific types)
6671
clientSecret: ""
72+
# Scope for generic OAuth (not used by AdobeIMS)
6773
scope: ""
74+
# Additional provider-specific configuration
75+
# For AdobeIMS: clientCode is required
76+
additional:
77+
clientCode: ""
6878

6979
minion:
7080
consumerGroups:
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# Example configurations for OAuth authentication with Kafka
2+
3+
# Example 1: Generic OAuth (e.g., Keycloak, Okta, Auth0)
4+
---
5+
kafka:
6+
brokers:
7+
- kafka-broker-1:9092
8+
- kafka-broker-2:9092
9+
clientId: "kminion"
10+
11+
sasl:
12+
enabled: true
13+
mechanism: "OAUTHBEARER"
14+
oauth:
15+
# Leave type empty for generic OAuth
16+
type: ""
17+
tokenEndpoint: "https://your-oauth-provider.com/oauth/token"
18+
clientId: "your-kafka-client-id"
19+
clientSecret: "your-kafka-client-secret"
20+
scope: "kafka"
21+
22+
# Example 2: Adobe IMS OAuth
23+
---
24+
kafka:
25+
brokers:
26+
- kafka-broker-1:9092
27+
- kafka-broker-2:9092
28+
clientId: "kminion"
29+
30+
sasl:
31+
enabled: true
32+
mechanism: "OAUTHBEARER"
33+
oauth:
34+
type: "AdobeIMS"
35+
tokenEndpoint: "https://ims-na1.adobelogin.com"
36+
clientId: "your-ims-client-id"
37+
clientSecret: "your-ims-client-secret"
38+
additional:
39+
clientCode: "your-ims-authorization-code"
40+
41+
# Example 3: Generic OAuth with TLS
42+
---
43+
kafka:
44+
brokers:
45+
- kafka-broker-1:9093
46+
- kafka-broker-2:9093
47+
clientId: "kminion"
48+
49+
tls:
50+
enabled: true
51+
caFilepath: "/path/to/ca-cert.pem"
52+
certFilepath: "/path/to/client-cert.pem"
53+
keyFilepath: "/path/to/client-key.pem"
54+
55+
sasl:
56+
enabled: true
57+
mechanism: "OAUTHBEARER"
58+
oauth:
59+
type: ""
60+
tokenEndpoint: "https://your-oauth-provider.com/oauth/token"
61+
clientId: "your-kafka-client-id"
62+
clientSecret: "your-kafka-client-secret"
63+
scope: "kafka"
64+
65+
# Example 4: Adobe IMS OAuth with TLS
66+
---
67+
kafka:
68+
brokers:
69+
- kafka-broker-1:9093
70+
- kafka-broker-2:9093
71+
clientId: "kminion"
72+
73+
tls:
74+
enabled: true
75+
caFilepath: "/path/to/ca-cert.pem"
76+
77+
sasl:
78+
enabled: true
79+
mechanism: "OAUTHBEARER"
80+
oauth:
81+
type: "AdobeIMS"
82+
tokenEndpoint: "https://ims-na1.adobelogin.com"
83+
clientId: "your-ims-client-id"
84+
clientSecret: "your-ims-client-secret"
85+
additional:
86+
clientCode: "your-ims-authorization-code"
87+

go.mod

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@ require (
2222
golang.org/x/sync v0.17.0
2323
)
2424

25+
require github.com/golang-jwt/jwt/v5 v5.0.0 // indirect
26+
2527
require (
28+
github.com/adobe/ims-go v0.16.1
2629
github.com/beorn7/perks v1.0.1 // indirect
2730
github.com/cespare/xxhash/v2 v2.3.0 // indirect
2831
github.com/davecgh/go-spew v1.1.1 // indirect

go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
22
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
33
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
4+
github.com/adobe/ims-go v0.16.1 h1:n1gYlfAV9djx9+r9VGU8I49G8fnIrIsZvX1s/XJVD3w=
5+
github.com/adobe/ims-go v0.16.1/go.mod h1:nsvzRhDFespyZnnLxQlsfBmlfGzqcy/zuS2HGebDHMI=
46
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
57
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
68
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
@@ -63,6 +65,8 @@ github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr6
6365
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
6466
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
6567
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
68+
github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE=
69+
github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
6670
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
6771
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
6872
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=

kafka/client_config_helper.go

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -119,15 +119,30 @@ func NewKgoConfig(cfg Config, logger *zap.Logger) ([]kgo.Opt, error) {
119119
}
120120

121121
// OAuthBearer
122-
if cfg.SASL.Mechanism == "OAUTHBEARER" {
123-
mechanism := oauth.Oauth(func(ctx context.Context) (oauth.Auth, error) {
124-
token, err := cfg.SASL.OAuthBearer.getToken(ctx)
125-
return oauth.Auth{
126-
Zid: cfg.SASL.OAuthBearer.ClientID,
127-
Token: token,
128-
}, err
129-
})
130-
opts = append(opts, kgo.SASL(mechanism))
122+
if cfg.SASL.Mechanism == SASLMechanismOAuthBearer {
123+
switch cfg.SASL.OAuthBearer.Type {
124+
case OAuthBearerTypeAdobeIMS:
125+
bearer, err := NewAdobeImsOAuthBearer(
126+
cfg.SASL.OAuthBearer.ClientID,
127+
cfg.SASL.OAuthBearer.ClientSecret,
128+
cfg.SASL.OAuthBearer.Additional.ClientCode,
129+
cfg.SASL.OAuthBearer.TokenEndpoint,
130+
)
131+
if err != nil {
132+
return nil, fmt.Errorf("failed to create Adobe IMS OAuth bearer: %w", err)
133+
}
134+
opts = append(opts, bearer.Opt())
135+
default:
136+
// Generic OAuth
137+
mechanism := oauth.Oauth(func(ctx context.Context) (oauth.Auth, error) {
138+
token, err := cfg.SASL.OAuthBearer.getToken(ctx)
139+
return oauth.Auth{
140+
Zid: cfg.SASL.OAuthBearer.ClientID,
141+
Token: token,
142+
}, err
143+
})
144+
opts = append(opts, kgo.SASL(mechanism))
145+
}
131146
}
132147
}
133148

kafka/config_sasl_oauthbearer.go

Lines changed: 46 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,25 @@ import (
1010
"strings"
1111
)
1212

13+
// OAuthBearerConfig represents the OAUTHBEARER SASL config with support for different providers
1314
type OAuthBearerConfig struct {
15+
// Type of OAuth provider. Valid values: "AdobeIMS" (leave empty for generic OAuth)
16+
Type OAuthBearerType `koanf:"type"`
17+
18+
// Generic OAuth configuration (used when Type is empty)
1419
TokenEndpoint string `koanf:"tokenEndpoint"`
1520
ClientID string `koanf:"clientId"`
1621
ClientSecret string `koanf:"clientSecret"`
1722
Scope string `koanf:"scope"`
23+
24+
// Additional provider-specific configuration
25+
Additional OAuthAdditionalConfig `koanf:"additional"`
1826
}
1927

20-
func (c *OAuthBearerConfig) Validate() error {
21-
if c.TokenEndpoint == "" {
22-
return fmt.Errorf("OAuthBearer token endpoint is not specified")
23-
}
24-
if c.ClientID == "" || c.ClientSecret == "" {
25-
return fmt.Errorf("OAuthBearer client credentials are not specified")
26-
}
27-
return nil
28+
// OAuthAdditionalConfig holds provider-specific OAuth configuration
29+
type OAuthAdditionalConfig struct {
30+
// Adobe IMS specific fields (used when Type is "AdobeIMS")
31+
ClientCode string `koanf:"clientCode"`
2832
}
2933

3034
// same as AcquireToken in Console https://github.com/redpanda-data/console/blob/master/backend/pkg/config/kafka_sasl_oauth.go#L56
@@ -71,3 +75,37 @@ func (c *OAuthBearerConfig) getToken(ctx context.Context) (string, error) {
7175

7276
return accessToken, nil
7377
}
78+
79+
// Validate validates the OAuthBearerConfig
80+
func (c *OAuthBearerConfig) Validate() error {
81+
// Common validation for all OAuth types
82+
if c.TokenEndpoint == "" {
83+
return fmt.Errorf("OAuthBearer token endpoint is not specified")
84+
}
85+
if c.ClientID == "" || c.ClientSecret == "" {
86+
return fmt.Errorf("OAuthBearer client credentials are not specified")
87+
}
88+
89+
// Type-specific validation
90+
switch c.Type {
91+
case OAuthBearerTypeAdobeIMS:
92+
// Adobe IMS specific validation
93+
if c.Additional.ClientCode == "" {
94+
return fmt.Errorf("OAuthBearer Adobe IMS client code is not specified")
95+
}
96+
case "":
97+
// Generic OAuth - no additional validation needed
98+
default:
99+
return fmt.Errorf("unknown OAuthBearer type '%s'", c.Type)
100+
}
101+
102+
return nil
103+
}
104+
105+
// OAuthBearerType represents the type of OAuth provider
106+
type OAuthBearerType string
107+
108+
const (
109+
// OAuthBearerTypeAdobeIMS represents Adobe IMS OAuth provider
110+
OAuthBearerTypeAdobeIMS OAuthBearerType = "AdobeIMS"
111+
)

0 commit comments

Comments
 (0)