|
4 | 4 | from oauthlib.oauth2 import WebApplicationClient, InsecureTransportError
|
5 | 5 | from oauthlib.oauth2 import LegacyApplicationClient
|
6 | 6 | from oauthlib.oauth2 import TokenExpiredError, is_secure_transport
|
| 7 | +from oauthlib.oauth2 import UnsupportedTokenTypeError |
| 8 | +from oauthlib.oauth2 import TemporarilyUnavailableError, ServerError |
7 | 9 | import requests
|
8 | 10 |
|
9 | 11 | log = logging.getLogger(__name__)
|
@@ -98,8 +100,10 @@ def __init__(
|
98 | 100 | self.compliance_hook = {
|
99 | 101 | "access_token_response": set(),
|
100 | 102 | "refresh_token_response": set(),
|
| 103 | + "revoke_token_response": set(), |
101 | 104 | "protected_request": set(),
|
102 | 105 | "refresh_token_request": set(),
|
| 106 | + "revoke_token_request": set(), |
103 | 107 | "access_token_request": set(),
|
104 | 108 | }
|
105 | 109 |
|
@@ -499,6 +503,85 @@ def refresh_token(
|
499 | 503 | self.token["refresh_token"] = refresh_token
|
500 | 504 | return self.token
|
501 | 505 |
|
| 506 | + def revoke_token( |
| 507 | + self, |
| 508 | + token_url, |
| 509 | + token=None, |
| 510 | + token_type=None, |
| 511 | + body="", |
| 512 | + auth=None, |
| 513 | + timeout=None, |
| 514 | + headers=None, |
| 515 | + verify=None, |
| 516 | + proxies=None, |
| 517 | + **kwargs |
| 518 | + ): |
| 519 | + """Revoke a token pair using a token. |
| 520 | +
|
| 521 | + :param token_url: The token endpoint, must be HTTPS. |
| 522 | + :param token: The token to revoke. |
| 523 | + :param token_type: The type of token to revoke. |
| 524 | + :param body: Optional application/x-www-form-urlencoded body to add the |
| 525 | + include in the token request. Prefer kwargs over body. |
| 526 | + :param auth: An auth tuple or method as accepted by `requests`. |
| 527 | + :param timeout: Timeout of the request in seconds. |
| 528 | + :param headers: A dict of headers to be used by `requests`. |
| 529 | + :param verify: Verify SSL certificate. |
| 530 | + :param proxies: The `proxies` argument will be passed to `requests`. |
| 531 | + :param kwargs: Extra parameters to include in the token request. |
| 532 | + :return: A token dict |
| 533 | + """ |
| 534 | + if not token_url: |
| 535 | + raise ValueError("No token endpoint set for revoke.") |
| 536 | + |
| 537 | + if not is_secure_transport(token_url): |
| 538 | + raise InsecureTransportError() |
| 539 | + |
| 540 | + token = token or self.token.get("access_token") |
| 541 | + token_type = token_type or self.token.get("token_type") |
| 542 | + |
| 543 | + _request_headers = headers or {} |
| 544 | + |
| 545 | + if token_type: |
| 546 | + (url, _headers, body) = self._client.prepare_token_revocation_request( |
| 547 | + token_url, token, token_type, body=body, scope=self.scope, **kwargs) |
| 548 | + else: |
| 549 | + (url, _headers, body) = self._client.prepare_revocation_request( |
| 550 | + token_url, token, body=body, scope=self.scope, **kwargs) |
| 551 | + _request_headers.update(_headers) |
| 552 | + log.debug("Prepared revocation request %s", body) |
| 553 | + |
| 554 | + for hook in self.compliance_hook["revoke_token_request"]: |
| 555 | + log.debug("Invoking revoke_token_request hook %s.", hook) |
| 556 | + url, _request_headers, body = hook(url, _headers, body) |
| 557 | + |
| 558 | + r = self.post( |
| 559 | + url, |
| 560 | + data=dict(urldecode(body)), |
| 561 | + auth=auth, |
| 562 | + timeout=timeout, |
| 563 | + headers=_request_headers, |
| 564 | + verify=verify, |
| 565 | + withhold_token=True, |
| 566 | + proxies=proxies, |
| 567 | + ) |
| 568 | + log.debug("Request to revoke token completed with status %s.", r.status_code) |
| 569 | + log.debug("Response headers were %s and content %s.", r.headers, r.text) |
| 570 | + log.debug( |
| 571 | + "Invoking %d token response hooks.", |
| 572 | + len(self.compliance_hook["revoke_token_response"]), |
| 573 | + ) |
| 574 | + for hook in self.compliance_hook["revoke_token_response"]: |
| 575 | + log.debug("Invoking hook %s.", hook) |
| 576 | + r = hook(r) |
| 577 | + |
| 578 | + if not r.ok and r.status_code == 400: |
| 579 | + if 'unsupported_token_type' in r.text: |
| 580 | + raise UnsupportedTokenTypeError("Revocation not supported by server") |
| 581 | + raise ServerError('Server error') |
| 582 | + elif not r.ok and r.code == 503: |
| 583 | + raise TemporarilyUnavailableError("Service unavailable") |
| 584 | + |
502 | 585 | def request(
|
503 | 586 | self,
|
504 | 587 | method,
|
@@ -573,9 +656,11 @@ def register_compliance_hook(self, hook_type, hook):
|
573 | 656 | Available hooks are:
|
574 | 657 | access_token_response invoked before token parsing.
|
575 | 658 | refresh_token_response invoked before refresh token parsing.
|
| 659 | + revoke_token_response invoked after token revocation. |
576 | 660 | protected_request invoked before making a request.
|
577 | 661 | access_token_request invoked before making a token fetch request.
|
578 | 662 | refresh_token_request invoked before making a refresh request.
|
| 663 | + revoke_token_request invoked before making a revoke request. |
579 | 664 |
|
580 | 665 | If you find a new hook is needed please send a GitHub PR request
|
581 | 666 | or open an issue.
|
|
0 commit comments