Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

oauth_service.py 2.2KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061
  1. import json
  2. import uuid
  3. from core.plugin.impl.base import BasePluginClient
  4. from extensions.ext_redis import redis_client
  5. class OAuthProxyService(BasePluginClient):
  6. # Default max age for proxy context parameter in seconds
  7. __MAX_AGE__ = 5 * 60 # 5 minutes
  8. @staticmethod
  9. def create_proxy_context(user_id, tenant_id, plugin_id, provider):
  10. """
  11. Create a proxy context for an OAuth 2.0 authorization request.
  12. This parameter is a crucial security measure to prevent Cross-Site Request
  13. Forgery (CSRF) attacks. It works by generating a unique nonce and storing it
  14. in a distributed cache (Redis) along with the user's session context.
  15. The returned nonce should be included as the 'proxy_context' parameter in the
  16. authorization URL. Upon callback, the `use_proxy_context` method
  17. is used to verify the state, ensuring the request's integrity and authenticity,
  18. and mitigating replay attacks.
  19. """
  20. seconds, _ = redis_client.time()
  21. context_id = str(uuid.uuid4())
  22. data = {
  23. "user_id": user_id,
  24. "plugin_id": plugin_id,
  25. "tenant_id": tenant_id,
  26. "provider": provider,
  27. # encode redis time to avoid distribution time skew
  28. "timestamp": seconds,
  29. }
  30. # ignore nonce collision
  31. redis_client.setex(
  32. f"oauth_proxy_context:{context_id}",
  33. OAuthProxyService.__MAX_AGE__,
  34. json.dumps(data),
  35. )
  36. return context_id
  37. @staticmethod
  38. def use_proxy_context(context_id, max_age=__MAX_AGE__):
  39. """
  40. Validate the proxy context parameter.
  41. This checks if the context_id is valid and not expired.
  42. """
  43. if not context_id:
  44. raise ValueError("context_id is required")
  45. # get data from redis
  46. data = redis_client.getdel(f"oauth_proxy_context:{context_id}")
  47. if not data:
  48. raise ValueError("context_id is invalid")
  49. # check if data is expired
  50. seconds, _ = redis_client.time()
  51. state = json.loads(data)
  52. if state.get("timestamp") < seconds - max_age:
  53. raise ValueError("context_id is expired")
  54. return state