Skip to content

Commit 2cbee93

Browse files
committed
OAuth 2.0 token exchange. Allow multiple resource parameters
1 parent 2c3e0ca commit 2cbee93

File tree

3 files changed

+49
-8
lines changed

3 files changed

+49
-8
lines changed

CHANGELOG.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
* OAuth 2.0 token exchange. Allow multiple resource parameters in according to https://www.rfc-editor.org/rfc/rfc8693
2+
13
## 3.14.0 ##
24
* Added load OAuth 2.0 token exchange credentials provider from config file
35

@@ -83,7 +85,7 @@ yanked bad api release
8385

8486
## 3.3.5 ##
8587
* Fixed use positional argument instead of named in WriterAsyncIO.__del__
86-
* Fixed release buffer while read topic by one messages
88+
* Fixed release buffer while read topic by one messages
8789
* Fixed race condition between commit_with_ack and reconnect in topic writer
8890

8991
## 3.3.4 ##

tests/oauth2_token_exchange/test_token_exchange.py

+7-4
Original file line numberDiff line numberDiff line change
@@ -181,11 +181,12 @@ def check(self, handler, parsed_request) -> None:
181181
assert len(parsed_request.get("scope", [])) == 1
182182
assert parsed_request["scope"][0] == " ".join(self.scope)
183183

184-
if self.resource is None or self.resource == "":
184+
if self.resource is None or len(self.resource) == 0:
185185
assert len(parsed_request.get("resource", [])) == 0
186186
else:
187-
assert len(parsed_request.get("resource", [])) == 1
188-
assert parsed_request["resource"][0] == self.resource
187+
assert len(parsed_request["resource"]) == len(self.resource)
188+
for i in range(len(self.resource)):
189+
assert parsed_request["resource"][i] == self.resource[i]
189190

190191
if self.subject_token_source is None:
191192
assert len(parsed_request.get("subject_token", [])) == 0
@@ -478,7 +479,7 @@ def test_oauth2_token_exchange_credentials_file():
478479
),
479480
grant_type="grant",
480481
requested_token_type="access_token",
481-
resource="tEst",
482+
resource=["tEst"],
482483
),
483484
response=Oauth2TokenExchangeResponse(
484485
200,
@@ -498,6 +499,7 @@ def test_oauth2_token_exchange_credentials_file():
498499
"s1",
499500
"s2",
500501
],
502+
"res": ["r1", "r2"],
501503
"unknown-field": [123],
502504
"actor-credentials": {
503505
"type": "fixed",
@@ -512,6 +514,7 @@ def test_oauth2_token_exchange_credentials_file():
512514
),
513515
audience=["test-aud"],
514516
scope=["s1", "s2"],
517+
resource=["r1", "r2"],
515518
),
516519
response=Oauth2TokenExchangeResponse(
517520
200,

ydb/oauth2_token_exchange/token_exchange.py

+39-3
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ def __init__(
3939
actor_token_source: typing.Optional[TokenSource] = None,
4040
audience: typing.Union[typing.List[str], str, None] = None,
4141
scope: typing.Union[typing.List[str], str, None] = None,
42-
resource: typing.Optional[str] = None,
42+
resource: typing.Union[typing.List[str], str, None] = None,
4343
grant_type: str = "urn:ietf:params:oauth:grant-type:token-exchange",
4444
requested_token_type: str = "urn:ietf:params:oauth:token-type:access_token",
4545
):
@@ -224,6 +224,42 @@ def _duration_seconds_from_config(cls, cfg_json, key_name, default_value):
224224

225225
@classmethod
226226
def from_file(cls, cfg_file, iam_endpoint=None):
227+
"""
228+
Create OAuth 2.0 token exchange protocol credentials from config file.
229+
230+
https://www.rfc-editor.org/rfc/rfc8693
231+
Config file must be a valid json file
232+
233+
Fields of json file
234+
grant-type: [string] Grant type option (default: "urn:ietf:params:oauth:grant-type:token-exchange")
235+
res: [string | list of strings] Resource option (optional)
236+
aud: [string | list of strings] Audience option for token exchange request (optional)
237+
scope: [string | list of strings] Scope option (optional)
238+
requested-token-type: [string] Requested token type option (default: "urn:ietf:params:oauth:token-type:access_token")
239+
subject-credentials: [creds_json] Subject credentials options (optional)
240+
actor-credentials: [creds_json] Actor credentials options (optional)
241+
token-endpoint: [string] Token endpoint
242+
243+
Fields of creds_json (JWT):
244+
type: [string] Token source type. Set JWT
245+
alg: [string] Algorithm for JWT signature.
246+
Supported algorithms can be listed
247+
with GetSupportedOauth2TokenExchangeJwtAlgorithms()
248+
private-key: [string] (Private) key in PEM format (RSA, EC) or Base64 format (HMAC) for JWT signature
249+
kid: [string] Key id JWT standard claim (optional)
250+
iss: [string] Issuer JWT standard claim (optional)
251+
sub: [string] Subject JWT standard claim (optional)
252+
aud: [string | list of strings] Audience JWT standard claim (optional)
253+
jti: [string] JWT ID JWT standard claim (optional)
254+
ttl: [string] Token TTL (default: 1h)
255+
256+
Fields of creds_json (FIXED):
257+
type: [string] Token source type. Set FIXED
258+
token: [string] Token value
259+
token-type: [string] Token type value. It will become
260+
subject_token_type/actor_token_type parameter
261+
in token exchange request (https://www.rfc-editor.org/rfc/rfc8693)
262+
"""
227263
with open(os.path.expanduser(cfg_file), "r") as r:
228264
cfg = r.read()
229265

@@ -245,7 +281,7 @@ def from_content(cls, cfg, iam_endpoint=None):
245281
actor_token_source = cls._token_source_from_config(cfg_json, "actor-credentials")
246282
audience = cls._list_of_strings_or_single_from_config(cfg_json, "aud")
247283
scope = cls._list_of_strings_or_single_from_config(cfg_json, "scope")
248-
resource = cls._string_with_default_from_config(cfg_json, "res", None)
284+
resource = cls._list_of_strings_or_single_from_config(cfg_json, "res")
249285
grant_type = cls._string_with_default_from_config(
250286
cfg_json, "grant-type", "urn:ietf:params:oauth:grant-type:token-exchange"
251287
)
@@ -273,7 +309,7 @@ def __init__(
273309
actor_token_source: typing.Optional[TokenSource] = None,
274310
audience: typing.Union[typing.List[str], str, None] = None,
275311
scope: typing.Union[typing.List[str], str, None] = None,
276-
resource: typing.Optional[str] = None,
312+
resource: typing.Union[typing.List[str], str, None] = None,
277313
grant_type: str = "urn:ietf:params:oauth:grant-type:token-exchange",
278314
requested_token_type: str = "urn:ietf:params:oauth:token-type:access_token",
279315
tracer=None,

0 commit comments

Comments
 (0)