Skip to content

Commit e875ba8

Browse files
fix: openid4vci token endpoint
- fix validation scope with parsing with par request - fix normalize token.authorization_details
1 parent 4a36853 commit e875ba8

5 files changed

Lines changed: 29 additions & 10 deletions

File tree

pyeudiw/openid4vci/endpoints/token_endpoint.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@
1818
TokenRequest,
1919
REDIRECT_URI_CTX,
2020
CODE_CHALLENGE_CTX,
21-
CODE_CHALLENGE_METHOD_CTX
21+
CODE_CHALLENGE_METHOD_CTX,
22+
SCOPE_CTX
2223
)
2324
from pyeudiw.openid4vci.models.token_response import TokenResponse
2425
from pyeudiw.openid4vci.storage.engine import OpenId4VciEngine
@@ -79,14 +80,21 @@ def endpoint(self, context: Context):
7980
CONFIG_CTX: self.config,
8081
REDIRECT_URI_CTX: entity.redirect_uri,
8182
CODE_CHALLENGE_METHOD_CTX: entity.code_challenge_method,
82-
CODE_CHALLENGE_CTX: entity.code_challenge
83+
CODE_CHALLENGE_CTX: entity.code_challenge,
84+
SCOPE_CTX: entity.scope
8385
})
8486
iat = iat_now()
87+
authorization_details = entity.authorization_details
88+
if authorization_details or len(authorization_details) > 0:
89+
for ad in authorization_details:
90+
ad.credential_identifiers = self.config_utils.get_credential_configurations_supported(
91+
ad.credential_configuration_id).scope
92+
8593
return TokenResponse.to_created_response(
8694
self._to_token(iat, entity, TokenTypsEnum.ACCESS_TOKEN_TYP),
8795
self._to_token(iat, entity, TokenTypsEnum.REFRESH_TOKEN_TYP),
8896
iat + self.config_utils.get_jwt().access_token_exp,
89-
entity.authorization_details
97+
authorization_details
9098
)
9199
except (InvalidRequestException, InvalidScopeException, JWSVerificationError, ValidationError, TypeError) as e:
92100
return self._handle_400(context, self._handle_validate_request_error(e, "token"), e)

pyeudiw/openid4vci/models/token_request.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
REDIRECT_URI_CTX = "redirect_uri"
1616
CODE_CHALLENGE_METHOD_CTX = "code_challenge_method"
1717
CODE_CHALLENGE_CTX = "code_challenge"
18+
SCOPE_CTX = "scope"
1819

1920
TOKEN_ENDPOINT = "token" # nosec B105
2021

@@ -61,10 +62,15 @@ def validate_scope(self, is_authorization_code_grant):
6162
self.check_unexpected_parameter(self.scope, "scope", TOKEN_ENDPOINT)
6263
elif self.scope:
6364
scopes = self.scope.split(" ")
65+
par_scope_ctx = self.get_ctx(SCOPE_CTX)
66+
par_scopes = par_scope_ctx.split(" ") if par_scope_ctx is not None else None
6467
for s in scopes:
6568
if s not in self.get_config().metadata.oauth_authorization_server.scopes_supported:
6669
logger.error(f"invalid scope value '{s}' in `token` endpoint")
6770
raise InvalidRequestException(f"invalid scope value '{s}'")
71+
elif par_scopes and (s not in par_scopes):
72+
logger.error(f"invalid scope in `token` endpoint: value '{s}' not present in previous `par` endpoint")
73+
raise InvalidRequestException(f"invalid scope value '{s}'")
6874

6975
def validate_refresh_token(self, is_authorization_code_grant):
7076
self.refresh_token = self.strip(self.refresh_token)

pyeudiw/openid4vci/models/token_response.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import List
1+
from typing import List, Optional
22

33
from pydantic import BaseModel
44
from satosa.response import Created
@@ -22,7 +22,7 @@ class TokenResponse(BaseModel):
2222
refresh_token: str
2323
token_type: str
2424
expires_in: int
25-
authorization_details: List[AuthorizationDetail] = None
25+
authorization_details: Optional[List[AuthorizationDetail]] = None
2626

2727
@staticmethod
2828
def to_created_response(access_token: str, refresh_token: str, expires_in: int, authorization_details: List[AuthorizationDetail]) -> Created:
@@ -36,7 +36,9 @@ def to_created_response(access_token: str, refresh_token: str, expires_in: int,
3636
refresh_token=refresh_token,
3737
token_type="DPOP", # nosec B106
3838
expires_in=expires_in,
39-
authorization_details=authorization_details
39+
authorization_details= None
40+
if not authorization_details and len(authorization_details) == 0
41+
else authorization_details
4042
)
4143
return Created(
4244
message=data.model_dump_json(),

pyeudiw/tests/openid4vci/endpoints/test_token_endpoint.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -364,7 +364,9 @@ def _assert_valid_request(result: Response, entity: OpenId4VCIEntity, exp_access
364364
assert response["refresh_token"] == exp_refresh_token
365365
assert response["token_type"] == "DPOP"
366366
assert isinstance(response["expires_in"], int)
367-
assert response["authorization_details"] == entity.authorization_details
367+
assert response["authorization_details"] == None \
368+
if not entity.authorization_details and len(entity.authorization_details) == 0 \
369+
else entity.authorization_details
368370

369371
def _assert_invalid_request(result: Response, error_desc: str):
370372
assert result.status == '400'

pyeudiw/tests/openid4vci/models/test_token_request.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
TokenRequest,
99
CODE_CHALLENGE_CTX,
1010
CODE_CHALLENGE_METHOD_CTX,
11-
REDIRECT_URI_CTX
11+
REDIRECT_URI_CTX, SCOPE_CTX
1212
)
1313
from pyeudiw.openid4vci.tools.exceptions import InvalidRequestException
1414
from pyeudiw.tests.openid4vci.mock_openid4vci import MOCK_PYEUDIW_FRONTEND_CONFIG
@@ -29,7 +29,8 @@ def get_valid_context(code_verifier="testverifier", redirect_uri="https://client
2929
CODE_CHALLENGE_CTX: code_challenge,
3030
CODE_CHALLENGE_METHOD_CTX: challenge_method,
3131
REDIRECT_URI_CTX: redirect_uri,
32-
CONFIG_CTX: PyeudiwFrontendConfig(**MOCK_PYEUDIW_FRONTEND_CONFIG)
32+
CONFIG_CTX: PyeudiwFrontendConfig(**MOCK_PYEUDIW_FRONTEND_CONFIG),
33+
SCOPE_CTX: scopes_supported[0]
3334
}
3435

3536
def test_token_request_valid_authorization_code_grant():
@@ -75,7 +76,7 @@ def test_token_request_valid_refresh_token_grant():
7576
payload = {
7677
"grant_type": "refresh_token",
7778
"refresh_token": "some-refresh-token",
78-
"scope": "scope1 openid"
79+
"scope": "scope1"
7980
}
8081
req = TokenRequest.model_validate(payload, context= get_valid_context(
8182
scopes_supported = ["scope1", "scope2", "openid"]

0 commit comments

Comments
 (0)