kailash 0.4.1__py3-none-any.whl → 0.5.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
kailash/__init__.py CHANGED
@@ -3,9 +3,8 @@
3
3
  The Kailash SDK provides a comprehensive framework for creating nodes and workflows
4
4
  that align with container-node architecture while allowing rapid prototyping.
5
5
 
6
- New in v0.4.1: Production-ready Alert Nodes with Discord integration and
7
- AI Provider Vision Support. Rich Discord alerts with embeds, rate limiting,
8
- and universal vision capabilities across OpenAI, Anthropic, and Ollama providers.
6
+ New in v0.4.2: Bug fixes including circular import resolution and JWT implementation
7
+ consolidation. Improved changelog organization with individual release files.
9
8
  """
10
9
 
11
10
  from kailash.nodes.base import Node, NodeMetadata, NodeParameter
@@ -34,7 +33,7 @@ except ImportError:
34
33
  # For backward compatibility
35
34
  WorkflowGraph = Workflow
36
35
 
37
- __version__ = "0.4.1"
36
+ __version__ = "0.4.2"
38
37
 
39
38
  __all__ = [
40
39
  # Core workflow components
@@ -36,7 +36,7 @@ The middleware consists of these interconnected layers:
36
36
 
37
37
  **API Gateway Layer**:
38
38
  - RESTful API endpoints with OpenAPI documentation
39
- - JWT-based authentication using JWTConfigNode
39
+ - JWT-based authentication using JWTAuthManager
40
40
  - Request/response middleware with comprehensive logging
41
41
  - Dynamic schema generation for node discovery
42
42
 
@@ -181,9 +181,11 @@ Usage Examples
181
181
  >>> workflow_id = await agent_ui.create_dynamic_workflow(
182
182
  ... session_id, workflow_config
183
183
  ... )
184
- >>> execution_id = await agent_ui.execute_workflow(
184
+ >>> # Use execute() for consistency with runtime API (preferred)
185
+ >>> execution_id = await agent_ui.execute(
185
186
  ... session_id, workflow_id, inputs={}
186
187
  ... )
188
+ >>> # Note: execute_workflow() is deprecated and will be removed in v1.0.0
187
189
 
188
190
  Production Deployment
189
191
  --------------------
@@ -15,19 +15,62 @@ Features:
15
15
  - Audit logging
16
16
  """
17
17
 
18
- from .access_control import (
19
- MiddlewareAccessControlManager,
20
- MiddlewareAuthenticationMiddleware,
18
+ from .exceptions import (
19
+ AuthenticationError,
20
+ InvalidTokenError,
21
+ PermissionDeniedError,
22
+ TokenBlacklistedError,
23
+ TokenExpiredError,
21
24
  )
22
- from .auth_manager import AuthLevel, MiddlewareAuthManager
23
- from .kailash_jwt_auth import KailashJWTAuthManager
25
+
26
+ # Import without circular dependencies
27
+ from .jwt_auth import JWTAuthManager
28
+ from .models import AuthenticationResult, JWTConfig, TokenPair, TokenPayload, UserClaims
29
+ from .utils import generate_key_pair, generate_secret_key, parse_bearer_token
30
+
31
+ # Import other components (check for circular deps)
32
+ try:
33
+ from .access_control import (
34
+ MiddlewareAccessControlManager,
35
+ MiddlewareAuthenticationMiddleware,
36
+ )
37
+ from .auth_manager import AuthLevel, MiddlewareAuthManager
38
+
39
+ _has_access_control = True
40
+ except ImportError:
41
+ # These imports might fail due to circular dependencies with communication
42
+ _has_access_control = False
43
+
44
+ # KailashJWTAuthManager has been consolidated into JWTAuthManager
45
+ # For backward compatibility, use JWTAuthManager directly
24
46
 
25
47
  __all__ = [
26
- # Core authentication
27
- "KailashJWTAuthManager",
28
- "MiddlewareAuthManager", # Main auth manager using SDK nodes
29
- "AuthLevel",
30
- # Authorization & Access Control
31
- "MiddlewareAccessControlManager",
32
- "MiddlewareAuthenticationMiddleware",
48
+ # Core authentication (always available)
49
+ "JWTAuthManager",
50
+ "JWTConfig",
51
+ "TokenPayload",
52
+ "TokenPair",
53
+ "UserClaims",
54
+ "AuthenticationResult",
55
+ # Exceptions
56
+ "AuthenticationError",
57
+ "TokenExpiredError",
58
+ "InvalidTokenError",
59
+ "TokenBlacklistedError",
60
+ "PermissionDeniedError",
61
+ # Utilities
62
+ "generate_secret_key",
63
+ "generate_key_pair",
64
+ "parse_bearer_token",
33
65
  ]
66
+
67
+ # Add access control components if available
68
+ if _has_access_control:
69
+ __all__.extend(
70
+ [
71
+ "MiddlewareAuthManager",
72
+ "AuthLevel",
73
+ "MiddlewareAccessControlManager",
74
+ "MiddlewareAuthenticationMiddleware",
75
+ ]
76
+ )
@@ -0,0 +1,80 @@
1
+ """
2
+ Authentication Exceptions for Kailash Middleware
3
+
4
+ Provides exception classes for authentication errors without circular dependencies.
5
+ """
6
+
7
+
8
+ class AuthenticationError(Exception):
9
+ """Base exception for authentication errors."""
10
+
11
+ def __init__(self, message: str, error_code: str = None):
12
+ super().__init__(message)
13
+ self.error_code = error_code
14
+
15
+
16
+ class TokenExpiredError(AuthenticationError):
17
+ """Raised when a JWT token has expired."""
18
+
19
+ def __init__(self, message: str = "Token has expired"):
20
+ super().__init__(message, "TOKEN_EXPIRED")
21
+
22
+
23
+ class InvalidTokenError(AuthenticationError):
24
+ """Raised when a JWT token is invalid."""
25
+
26
+ def __init__(self, message: str = "Invalid token"):
27
+ super().__init__(message, "INVALID_TOKEN")
28
+
29
+
30
+ class TokenBlacklistedError(AuthenticationError):
31
+ """Raised when a JWT token has been blacklisted/revoked."""
32
+
33
+ def __init__(self, message: str = "Token has been revoked"):
34
+ super().__init__(message, "TOKEN_BLACKLISTED")
35
+
36
+
37
+ class KeyRotationError(AuthenticationError):
38
+ """Raised when key rotation fails."""
39
+
40
+ def __init__(self, message: str = "Key rotation failed"):
41
+ super().__init__(message, "KEY_ROTATION_ERROR")
42
+
43
+
44
+ class RefreshTokenError(AuthenticationError):
45
+ """Raised when refresh token operations fail."""
46
+
47
+ def __init__(self, message: str = "Refresh token error"):
48
+ super().__init__(message, "REFRESH_TOKEN_ERROR")
49
+
50
+
51
+ class PermissionDeniedError(AuthenticationError):
52
+ """Raised when user lacks required permissions."""
53
+
54
+ def __init__(
55
+ self, message: str = "Permission denied", required_permission: str = None
56
+ ):
57
+ super().__init__(message, "PERMISSION_DENIED")
58
+ self.required_permission = required_permission
59
+
60
+
61
+ class RateLimitError(AuthenticationError):
62
+ """Raised when authentication rate limit is exceeded."""
63
+
64
+ def __init__(self, message: str = "Rate limit exceeded", retry_after: int = None):
65
+ super().__init__(message, "RATE_LIMIT_EXCEEDED")
66
+ self.retry_after = retry_after
67
+
68
+
69
+ class InvalidCredentialsError(AuthenticationError):
70
+ """Raised when login credentials are invalid."""
71
+
72
+ def __init__(self, message: str = "Invalid credentials"):
73
+ super().__init__(message, "INVALID_CREDENTIALS")
74
+
75
+
76
+ class SessionExpiredError(AuthenticationError):
77
+ """Raised when a session has expired."""
78
+
79
+ def __init__(self, message: str = "Session has expired"):
80
+ super().__init__(message, "SESSION_EXPIRED")
@@ -1,16 +1,14 @@
1
1
  """
2
2
  JWT Authentication Manager for Kailash Middleware
3
3
 
4
- Provides enterprise-grade JWT authentication built entirely with Kailash SDK components.
5
- Uses Kailash nodes, workflows, and patterns for all authentication operations.
4
+ Provides enterprise-grade JWT authentication with support for both HS256 and RSA algorithms.
5
+ This consolidates the functionality of both JWTAuthManager and KailashJWTAuthManager.
6
6
  """
7
7
 
8
- import json
9
8
  import logging
10
9
  import secrets
11
10
  import time
12
11
  import uuid
13
- from dataclasses import dataclass
14
12
  from datetime import datetime, timedelta, timezone
15
13
  from typing import Any, Dict, List, Optional, Union
16
14
 
@@ -22,82 +20,18 @@ except ImportError:
22
20
  jwt = None
23
21
  rsa = None
24
22
 
25
- # Import Kailash SDK components
26
- from kailash.nodes.base import Node, NodeParameter
27
- from kailash.nodes.code import PythonCodeNode
28
- from kailash.nodes.data import JSONReaderNode
29
- from kailash.nodes.logic import SwitchNode
30
- from kailash.runtime.local import LocalRuntime
31
- from kailash.workflow.builder import WorkflowBuilder
23
+ from .exceptions import (
24
+ InvalidTokenError,
25
+ RefreshTokenError,
26
+ TokenBlacklistedError,
27
+ TokenExpiredError,
28
+ )
32
29
 
33
- logger = logging.getLogger(__name__)
34
-
35
-
36
- @dataclass
37
- class JWTConfig:
38
- """Configuration for JWT authentication using only Python standard library."""
39
-
40
- # Signing configuration
41
- algorithm: str = (
42
- "HS256" # Use HMAC instead of RSA to avoid external crypto dependencies
43
- )
44
- access_token_expire_minutes: int = 15
45
- refresh_token_expire_days: int = 7
46
-
47
- # Security settings
48
- issuer: str = "kailash-middleware"
49
- audience: str = "kailash-api"
50
-
51
- # Key management
52
- auto_generate_keys: bool = True
53
- key_rotation_days: int = 30
54
-
55
- # Token settings
56
- include_user_claims: bool = True
57
- include_permissions: bool = True
58
- max_refresh_count: int = 10
59
-
60
-
61
- @dataclass
62
- class TokenPayload:
63
- """JWT token payload structure using standard Python."""
30
+ # Import models and utilities (no circular dependencies)
31
+ from .models import JWTConfig, RefreshTokenData, TokenPair, TokenPayload
32
+ from .utils import generate_secret_key, is_token_expired
64
33
 
65
- # Standard claims
66
- sub: str # Subject (user ID)
67
- iss: str # Issuer
68
- aud: str # Audience
69
- exp: int # Expiration time
70
- iat: int # Issued at
71
- jti: str # JWT ID
72
-
73
- # Custom claims
74
- tenant_id: Optional[str] = None
75
- session_id: Optional[str] = None
76
- user_type: str = "user"
77
- permissions: List[str] = None
78
- roles: List[str] = None
79
-
80
- # Token metadata
81
- token_type: str = "access" # access, refresh
82
- refresh_count: int = 0
83
-
84
- def __post_init__(self):
85
- if self.permissions is None:
86
- self.permissions = []
87
- if self.roles is None:
88
- self.roles = []
89
-
90
-
91
- @dataclass
92
- class TokenPair:
93
- """Access and refresh token pair using standard Python."""
94
-
95
- access_token: str
96
- refresh_token: str
97
- token_type: str = "Bearer"
98
- expires_in: int = 0
99
- expires_at: Optional[datetime] = None
100
- scope: Optional[str] = None
34
+ logger = logging.getLogger(__name__)
101
35
 
102
36
 
103
37
  class JWTAuthManager:
@@ -105,30 +39,111 @@ class JWTAuthManager:
105
39
  Enterprise JWT Authentication Manager.
106
40
 
107
41
  Provides comprehensive JWT token management with security best practices:
108
- - RSA key pair generation and rotation
42
+ - Support for both HS256 (default) and RSA algorithms
43
+ - RSA key pair generation and rotation (when using RSA)
109
44
  - Refresh token management
110
45
  - Token blacklisting
111
46
  - Comprehensive audit logging
112
47
  - Rate limiting protection
48
+
49
+ This consolidates both JWTAuthManager and KailashJWTAuthManager functionality.
113
50
  """
114
51
 
115
- def __init__(self, config: JWTConfig = None):
52
+ def __init__(
53
+ self,
54
+ config: JWTConfig = None,
55
+ secret_key: str = None,
56
+ algorithm: str = None,
57
+ use_rsa: bool = None,
58
+ **kwargs,
59
+ ):
60
+ """
61
+ Initialize JWT Auth Manager.
62
+
63
+ Args:
64
+ config: JWTConfig object with full configuration
65
+ secret_key: Secret key for HS256 (overrides config)
66
+ algorithm: Algorithm to use (overrides config)
67
+ use_rsa: Whether to use RSA (overrides config)
68
+ **kwargs: Additional config parameters
69
+ """
116
70
  self.config = config or JWTConfig()
117
71
 
72
+ # Override config with direct parameters for backward compatibility
73
+ if secret_key is not None:
74
+ self.config.secret_key = secret_key
75
+ if algorithm is not None:
76
+ self.config.algorithm = algorithm
77
+ if use_rsa is not None:
78
+ self.config.use_rsa = use_rsa
79
+ if use_rsa:
80
+ self.config.algorithm = "RS256"
81
+
82
+ # Apply any additional kwargs to config
83
+ for key, value in kwargs.items():
84
+ if hasattr(self.config, key):
85
+ setattr(self.config, key, value)
86
+
118
87
  # Key management
119
88
  self._private_key: Optional[rsa.RSAPrivateKey] = None
120
89
  self._public_key: Optional[rsa.RSAPublicKey] = None
90
+ self._secret_key: Optional[str] = self.config.secret_key
121
91
  self._key_id = str(uuid.uuid4())
122
92
  self._key_generated_at = datetime.now(timezone.utc)
123
93
 
124
94
  # Token tracking
125
- self._blacklisted_tokens: set = set()
95
+ self._blacklisted_tokens: set = set() if self.config.enable_blacklist else None
126
96
  self._refresh_tokens: Dict[str, Dict[str, Any]] = {}
127
97
  self._failed_attempts: Dict[str, List[datetime]] = {}
128
98
 
129
- # Initialize keys
130
- if self.config.auto_generate_keys:
131
- self._generate_key_pair()
99
+ # Initialize keys based on algorithm
100
+ self._initialize_keys()
101
+
102
+ def _initialize_keys(self):
103
+ """Initialize keys based on configured algorithm."""
104
+ if self.config.use_rsa or self.config.algorithm.startswith("RS"):
105
+ # RSA mode
106
+ if self.config.private_key and self.config.public_key:
107
+ # Load provided keys
108
+ self._load_rsa_keys()
109
+ elif self.config.auto_generate_keys:
110
+ # Generate new RSA keys
111
+ self._generate_key_pair()
112
+ else:
113
+ raise ValueError(
114
+ "RSA mode requires either provided keys or auto_generate_keys=True"
115
+ )
116
+ else:
117
+ # HS256 mode
118
+ if not self._secret_key:
119
+ if self.config.auto_generate_keys:
120
+ # Generate random secret
121
+ self._secret_key = secrets.token_urlsafe(32)
122
+ self.config.secret_key = self._secret_key
123
+ logger.info("Generated new HS256 secret key")
124
+ else:
125
+ raise ValueError(
126
+ "HS256 mode requires secret_key or auto_generate_keys=True"
127
+ )
128
+
129
+ def _load_rsa_keys(self):
130
+ """Load RSA keys from PEM strings."""
131
+ try:
132
+ from cryptography.hazmat.backends import default_backend
133
+ from cryptography.hazmat.primitives import serialization
134
+
135
+ self._private_key = serialization.load_pem_private_key(
136
+ self.config.private_key.encode(),
137
+ password=None,
138
+ backend=default_backend(),
139
+ )
140
+ self._public_key = serialization.load_pem_public_key(
141
+ self.config.public_key.encode(), backend=default_backend()
142
+ )
143
+ logger.info("Loaded RSA keys from configuration")
144
+ except Exception as e:
145
+ logger.error(f"Failed to load RSA keys: {e}")
146
+ raise
132
147
 
133
148
  def _generate_key_pair(self):
134
149
  """Generate new RSA key pair for token signing."""
@@ -193,7 +208,8 @@ class JWTAuthManager:
193
208
  **kwargs,
194
209
  ) -> str:
195
210
  """Create JWT access token."""
196
- if self._should_rotate_keys():
211
+ # Only rotate keys in RSA mode
212
+ if self.config.use_rsa and self._should_rotate_keys():
197
213
  self._generate_key_pair()
198
214
 
199
215
  payload = self._create_token_payload(
@@ -206,16 +222,39 @@ class JWTAuthManager:
206
222
  **kwargs,
207
223
  )
208
224
 
209
- # Add key ID to header
210
- headers = {"kid": self._key_id}
211
-
212
- # Sign token
213
- token = jwt.encode(
214
- payload.dict(),
215
- self._private_key,
216
- algorithm=self.config.algorithm,
217
- headers=headers,
218
- )
225
+ # Convert payload to dict for encoding
226
+ payload_dict = {
227
+ "sub": payload.sub,
228
+ "iss": payload.iss,
229
+ "aud": payload.aud,
230
+ "exp": payload.exp,
231
+ "iat": payload.iat,
232
+ "jti": payload.jti,
233
+ "tenant_id": payload.tenant_id,
234
+ "session_id": payload.session_id,
235
+ "token_type": payload.token_type,
236
+ "permissions": payload.permissions,
237
+ "roles": payload.roles,
238
+ }
239
+ payload_dict.update(kwargs)
240
+
241
+ # Sign token based on algorithm
242
+ if self.config.use_rsa or self.config.algorithm.startswith("RS"):
243
+ # RSA signing
244
+ headers = {"kid": self._key_id}
245
+ token = jwt.encode(
246
+ payload_dict,
247
+ self._private_key,
248
+ algorithm=self.config.algorithm,
249
+ headers=headers,
250
+ )
251
+ else:
252
+ # HS256 signing
253
+ token = jwt.encode(
254
+ payload_dict,
255
+ self._secret_key,
256
+ algorithm=self.config.algorithm,
257
+ )
219
258
 
220
259
  logger.debug(f"Created access token for user {user_id}")
221
260
  return token
@@ -232,14 +271,36 @@ class JWTAuthManager:
232
271
  **kwargs,
233
272
  )
234
273
 
235
- headers = {"kid": self._key_id}
236
-
237
- token = jwt.encode(
238
- payload.dict(),
239
- self._private_key,
240
- algorithm=self.config.algorithm,
241
- headers=headers,
242
- )
274
+ # Convert payload to dict
275
+ payload_dict = {
276
+ "sub": payload.sub,
277
+ "iss": payload.iss,
278
+ "aud": payload.aud,
279
+ "exp": payload.exp,
280
+ "iat": payload.iat,
281
+ "jti": payload.jti,
282
+ "tenant_id": payload.tenant_id,
283
+ "session_id": payload.session_id,
284
+ "token_type": payload.token_type,
285
+ "refresh_count": payload.refresh_count,
286
+ }
287
+ payload_dict.update(kwargs)
288
+
289
+ # Sign token based on algorithm
290
+ if self.config.use_rsa or self.config.algorithm.startswith("RS"):
291
+ headers = {"kid": self._key_id}
292
+ token = jwt.encode(
293
+ payload_dict,
294
+ self._private_key,
295
+ algorithm=self.config.algorithm,
296
+ headers=headers,
297
+ )
298
+ else:
299
+ token = jwt.encode(
300
+ payload_dict,
301
+ self._secret_key,
302
+ algorithm=self.config.algorithm,
303
+ )
243
304
 
244
305
  # Store refresh token metadata
245
306
  self._refresh_tokens[payload.jti] = {
@@ -291,27 +352,39 @@ class JWTAuthManager:
291
352
  """
292
353
  try:
293
354
  # Check if token is blacklisted
294
- if token in self._blacklisted_tokens:
355
+ if self._blacklisted_tokens and token in self._blacklisted_tokens:
295
356
  raise jwt.InvalidTokenError("Token has been revoked")
296
357
 
297
- # Decode without verification first to get header
298
- unverified_header = jwt.get_unverified_header(token)
299
- key_id = unverified_header.get("kid")
300
-
301
- # Verify key ID matches current key
302
- if key_id != self._key_id:
303
- logger.warning(f"Token signed with unknown key ID: {key_id}")
304
- # In production, you might want to support multiple keys
305
- # for graceful key rotation
306
-
307
- # Verify and decode token
308
- payload = jwt.decode(
309
- token,
310
- self._public_key,
311
- algorithms=[self.config.algorithm],
312
- issuer=self.config.issuer,
313
- audience=self.config.audience,
314
- )
358
+ # Get verification key based on algorithm
359
+ if self.config.use_rsa or self.config.algorithm.startswith("RS"):
360
+ # RSA verification
361
+ # Decode without verification first to get header
362
+ unverified_header = jwt.get_unverified_header(token)
363
+ key_id = unverified_header.get("kid")
364
+
365
+ # Verify key ID matches current key (optional check)
366
+ if key_id and key_id != self._key_id:
367
+ logger.warning(f"Token signed with unknown key ID: {key_id}")
368
+ # In production, you might want to support multiple keys
369
+ # for graceful key rotation
370
+
371
+ # Verify and decode token
372
+ payload = jwt.decode(
373
+ token,
374
+ self._public_key,
375
+ algorithms=[self.config.algorithm],
376
+ issuer=self.config.issuer,
377
+ audience=self.config.audience,
378
+ )
379
+ else:
380
+ # HS256 verification
381
+ payload = jwt.decode(
382
+ token,
383
+ self._secret_key,
384
+ algorithms=[self.config.algorithm],
385
+ issuer=self.config.issuer,
386
+ audience=self.config.audience,
387
+ )
315
388
 
316
389
  return payload
317
390
 
@@ -461,7 +534,9 @@ class JWTAuthManager:
461
534
  """Get authentication manager statistics."""
462
535
  return {
463
536
  "active_refresh_tokens": len(self._refresh_tokens),
464
- "blacklisted_tokens": len(self._blacklisted_tokens),
537
+ "blacklisted_tokens": (
538
+ len(self._blacklisted_tokens) if self._blacklisted_tokens else 0
539
+ ),
465
540
  "key_id": self._key_id,
466
541
  "key_age_days": (
467
542
  (datetime.now(timezone.utc) - self._key_generated_at).days
@@ -475,3 +550,70 @@ class JWTAuthManager:
475
550
  "refresh_token_expire_days": self.config.refresh_token_expire_days,
476
551
  },
477
552
  }
553
+
554
+ # Backward compatibility methods for KailashJWTAuthManager
555
+ def generate_token(self, user_id: str, **claims) -> str:
556
+ """
557
+ Generate access token (backward compatibility).
558
+
559
+ .. deprecated:: 0.5.0
560
+ Use :meth:`create_access_token` instead. This method will be removed in version 1.0.0.
561
+
562
+ This method exists for compatibility with KailashJWTAuthManager.
563
+ """
564
+ import warnings
565
+
566
+ warnings.warn(
567
+ "generate_token() is deprecated and will be removed in version 1.0.0. "
568
+ "Use create_access_token() instead.",
569
+ DeprecationWarning,
570
+ stacklevel=2,
571
+ )
572
+ return self.create_access_token(user_id, **claims)
573
+
574
+ def verify_and_decode_token(self, token: str) -> Dict[str, Any]:
575
+ """
576
+ Verify and decode token (backward compatibility).
577
+
578
+ .. deprecated:: 0.5.0
579
+ Use :meth:`verify_token` instead. This method will be removed in version 1.0.0.
580
+
581
+ This method exists for compatibility with KailashJWTAuthManager.
582
+ """
583
+ import warnings
584
+
585
+ warnings.warn(
586
+ "verify_and_decode_token() is deprecated and will be removed in version 1.0.0. "
587
+ "Use verify_token() instead.",
588
+ DeprecationWarning,
589
+ stacklevel=2,
590
+ )
591
+ return self.verify_token(token)
592
+
593
+ def blacklist_token(self, token: str):
594
+ """
595
+ Blacklist token (backward compatibility).
596
+
597
+ This method exists for compatibility with KailashJWTAuthManager.
598
+ """
599
+ # Just call the main revoke_token method
600
+ self.revoke_token(token)
601
+
602
+ def generate_refresh_token(self, user_id: str, **claims) -> str:
603
+ """
604
+ Generate refresh token (backward compatibility).
605
+
606
+ .. deprecated:: 0.5.0
607
+ Use :meth:`create_refresh_token` instead. This method will be removed in version 1.0.0.
608
+
609
+ This method exists for compatibility with KailashJWTAuthManager.
610
+ """
611
+ import warnings
612
+
613
+ warnings.warn(
614
+ "generate_refresh_token() is deprecated and will be removed in version 1.0.0. "
615
+ "Use create_refresh_token() instead.",
616
+ DeprecationWarning,
617
+ stacklevel=2,
618
+ )
619
+ return self.create_refresh_token(user_id, **claims)