Skip to content

Commit 612349e

Browse files
Add UAMI OAuth changes (#2189)
* Add UAMI OAuth changes * Fix formatting * Fix concurrent and syntax issues * Create sync files for uami oauth * make style fix * Add async base client * Fix examples and expiry * Add api version to example * make stylefix * Fix typos * remove word
1 parent 35d5608 commit 612349e

11 files changed

Lines changed: 935 additions & 351 deletions

examples/oauth_oidc_ccloud_azure_imds_producer.py

Lines changed: 100 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,55 @@
1717

1818

1919
# This example uses Azure IMDS for credential-less authentication
20-
# to Kafka on Confluent Cloud
20+
# to Schema Registry on Confluent Cloud
2121

2222
import argparse
2323
import logging
2424

2525
from confluent_kafka import Producer
26-
from confluent_kafka.serialization import StringSerializer
26+
from confluent_kafka.schema_registry import SchemaRegistryClient
27+
from confluent_kafka.schema_registry.json_schema import JSONSerializer
28+
from confluent_kafka.serialization import MessageField, SerializationContext, StringSerializer
29+
30+
31+
class User(object):
32+
"""
33+
User record
34+
35+
Args:
36+
name (str): User's name
37+
38+
favorite_number (int): User's favorite number
39+
40+
favorite_color (str): User's favorite color
41+
42+
address(str): User's address; confidential
43+
"""
44+
45+
def __init__(self, name, address, favorite_number, favorite_color):
46+
self.name = name
47+
self.favorite_number = favorite_number
48+
self.favorite_color = favorite_color
49+
# address should not be serialized, see user_to_dict()
50+
self._address = address
51+
52+
53+
def user_to_dict(user, ctx):
54+
"""
55+
Returns a dict representation of a User instance for serialization.
56+
57+
Args:
58+
user (User): User instance.
59+
60+
ctx (SerializationContext): Metadata pertaining to the serialization
61+
operation.
62+
63+
Returns:
64+
dict: Dict populated with user attributes to be serialized.
65+
"""
66+
67+
# User._address must not be serialized; omit from dict
68+
return dict(name=user.name, favorite_number=user.favorite_number, favorite_color=user.favorite_color)
2769

2870

2971
def producer_config(args):
@@ -38,7 +80,7 @@ def producer_config(args):
3880
'sasl.oauthbearer.config': f'query={args.query}',
3981
}
4082
# These two parameters are only applicable when producing to
41-
# Confluent Cloud where some sasl extensions are required.
83+
# confluent cloud where some sasl extensions are required.
4284
if args.logical_cluster and args.identity_pool_id:
4385
params['sasl.oauthbearer.extensions'] = (
4486
'logicalCluster=' + args.logical_cluster + ',identityPoolId=' + args.identity_pool_id
@@ -47,12 +89,27 @@ def producer_config(args):
4789
return params
4890

4991

92+
def schema_registry_config(args):
93+
params = {
94+
'url': args.schema_registry,
95+
'bearer.auth.credentials.source': 'OAUTHBEARER_AZURE_IMDS',
96+
'bearer.auth.issuer.endpoint.query': args.query,
97+
}
98+
# These two parameters are only applicable when producing to
99+
# confluent cloud where some sasl extensions are required.
100+
if args.logical_schema_registry_cluster and args.identity_pool_id:
101+
params['bearer.auth.logical.cluster'] = args.logical_schema_registry_cluster
102+
params['bearer.auth.identity.pool.id'] = args.identity_pool_id
103+
104+
return params
105+
106+
50107
def delivery_report(err, msg):
51108
"""
52109
Reports the failure or success of a message delivery.
53110
54111
Args:
55-
err (KafkaError): The error that occurred, or None on success.
112+
err (KafkaError): The error that occurred on None on success.
56113
57114
msg (Message): The message that was produced or failed.
58115
@@ -80,15 +137,45 @@ def main(args):
80137
producer_conf = producer_config(args)
81138
producer = Producer(producer_conf)
82139
string_serializer = StringSerializer('utf_8')
140+
schema_str = """
141+
{
142+
"$schema": "http://json-schema.org/draft-07/schema#",
143+
"title": "User",
144+
"description": "A Confluent Kafka Python User",
145+
"type": "object",
146+
"properties": {
147+
"name": {
148+
"description": "User's name",
149+
"type": "string"
150+
},
151+
"favorite_number": {
152+
"description": "User's favorite number",
153+
"type": "number",
154+
"exclusiveMinimum": 0
155+
},
156+
"favorite_color": {
157+
"description": "User's favorite color",
158+
"type": "string"
159+
}
160+
},
161+
"required": [ "name", "favorite_number", "favorite_color" ]
162+
}
163+
"""
164+
schema_registry_conf = schema_registry_config(args)
165+
schema_registry_client = SchemaRegistryClient(schema_registry_conf)
166+
167+
json_serializer = JSONSerializer(schema_str, schema_registry_client, user_to_dict)
83168

84169
print('Producing records to topic {}. ^C to exit.'.format(topic))
85170
while True:
86171
# Serve on_delivery callbacks from previous calls to produce()
87172
producer.poll(0.0)
88173
try:
89174
name = input(">")
175+
user = User(name=name, address="NA", favorite_color="blue", favorite_number=7)
176+
serialized_user = json_serializer(user, SerializationContext(topic, MessageField.VALUE))
90177
producer.produce(
91-
topic=topic, key=string_serializer(name), value=string_serializer(name), on_delivery=delivery_report
178+
topic=topic, key=string_serializer(name), value=serialized_user, on_delivery=delivery_report
92179
)
93180
except KeyboardInterrupt:
94181
break
@@ -98,11 +185,18 @@ def main(args):
98185

99186

100187
if __name__ == '__main__':
101-
parser = argparse.ArgumentParser(description="OAuth/OIDC example using Azure IMDS metadata-based authentication")
188+
parser = argparse.ArgumentParser(description="OAUTH example with client credentials grant")
102189
parser.add_argument('-b', dest="bootstrap_servers", required=True, help="Bootstrap broker(s) (host[:port])")
103190
parser.add_argument('-t', dest="topic", default="example_producer_oauth", help="Topic name")
191+
parser.add_argument('-s', dest="schema_registry", required=True, help="Schema Registry (http(s)://host[:port]")
104192
parser.add_argument('--query', dest="query", required=True, help="Query parameters for Azure IMDS token endpoint")
105193
parser.add_argument('--logical-cluster', dest="logical_cluster", required=False, help="Logical Cluster.")
194+
parser.add_argument(
195+
'--logical-schema-registry-cluster',
196+
dest="logical_schema_registry_cluster",
197+
required=False,
198+
help="Logical Schema Registry Cluster.",
199+
)
106200
parser.add_argument('--identity-pool-id', dest="identity_pool_id", required=False, help="Identity Pool ID.")
107201

108202
main(parser.parse_args())

examples/oauth_schema_registry.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
# limitations under the License.
1717

1818
# Examples of setting up Schema Registry with OAuth with static token,
19-
# Client Credentials, and custom functions
19+
# Client Credentials, Azure IMDS, and custom functions
2020

2121

2222
# CUSTOM OAuth configuration takes in a custom function, config for that
@@ -52,6 +52,17 @@ def main():
5252
client_credentials_oauth_sr_client = SchemaRegistryClient(client_credentials_oauth_config)
5353
print(client_credentials_oauth_sr_client.get_subjects())
5454

55+
azure_imds_oauth_config = {
56+
'url': 'https://psrc-123456.us-east-1.aws.confluent.cloud',
57+
'bearer.auth.credentials.source': 'OAUTHBEARER_AZURE_IMDS',
58+
'bearer.auth.issuer.endpoint.query': 'resource=&api-version=&client_id=',
59+
'bearer.auth.logical.cluster': 'lsrc-12345',
60+
'bearer.auth.identity.pool.id': 'pool-abcd',
61+
}
62+
63+
azure_imds_oauth_sr_client = SchemaRegistryClient(azure_imds_oauth_config)
64+
print(azure_imds_oauth_sr_client.get_subjects())
65+
5566
def custom_oauth_function(config):
5667
return config
5768

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
#
4+
# Copyright 2026 Confluent Inc.
5+
#
6+
# Licensed under the Apache License, Version 2.0 (the "License");
7+
# you may not use this file except in compliance with the License.
8+
# You may obtain a copy of the License at
9+
#
10+
# http://www.apache.org/licenses/LICENSE-2.0
11+
#
12+
# Unless required by applicable law or agreed to in writing, software
13+
# distributed under the License is distributed on an "AS IS" BASIS,
14+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
# See the License for the specific language governing permissions and
16+
# limitations under the License.
17+
18+
# Examples of setting up Schema Registry with OAuth with static token,
19+
# Client Credentials, Azure IMDS, and custom functions
20+
21+
22+
# CUSTOM OAuth configuration takes in a custom function, config for that
23+
# function, and returns the fields shown below. All fields must be returned
24+
# for OAuth authentication to work
25+
26+
27+
from confluent_kafka.schema_registry.schema_registry_client import SchemaRegistryClient
28+
29+
30+
def main():
31+
BOOTSTRAP = "api://bootstrapid"
32+
UAMI_CLIENT_ID = "uamiid"
33+
34+
endpoint_query = f"api-version=2025-04-07&resource={BOOTSTRAP}&client_id={UAMI_CLIENT_ID}"
35+
36+
azure_imds_oauth_config = {
37+
'url': 'https://psrc-123456.us-east-1.aws.confluent.cloud',
38+
'bearer.auth.credentials.source': 'OAUTHBEARER_AZURE_IMDS',
39+
'bearer.auth.issuer.endpoint.query': endpoint_query,
40+
'bearer.auth.logical.cluster': 'lsrc-12345',
41+
'bearer.auth.identity.pool.id': 'pool-abcd',
42+
}
43+
44+
azure_imds_oauth_sr_client = SchemaRegistryClient(azure_imds_oauth_config)
45+
print(azure_imds_oauth_sr_client.get_subjects())
46+
47+
48+
if __name__ == '__main__':
49+
main()

0 commit comments

Comments
 (0)