jac-scale 0.1.1__tar.gz → 0.1.2__tar.gz

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.
Files changed (67) hide show
  1. {jac_scale-0.1.1 → jac_scale-0.1.2}/PKG-INFO +9 -2
  2. {jac_scale-0.1.1 → jac_scale-0.1.2}/README.md +7 -0
  3. jac_scale-0.1.2/jac_scale/google_sso_provider.jac +85 -0
  4. {jac_scale-0.1.1 → jac_scale-0.1.2}/jac_scale/impl/serve.impl.jac +12 -225
  5. jac_scale-0.1.2/jac_scale/impl/user_manager.impl.jac +349 -0
  6. {jac_scale-0.1.1 → jac_scale-0.1.2}/jac_scale/plugin.jac +8 -0
  7. {jac_scale-0.1.1 → jac_scale-0.1.2}/jac_scale/serve.jac +0 -12
  8. jac_scale-0.1.2/jac_scale/sso_provider.jac +72 -0
  9. {jac_scale-0.1.1 → jac_scale-0.1.2}/jac_scale/targets/kubernetes/kubernetes_target.jac +7 -2
  10. jac_scale-0.1.2/jac_scale/tests/test_hooks.py +39 -0
  11. {jac_scale-0.1.1 → jac_scale-0.1.2}/jac_scale/tests/test_sso.py +273 -284
  12. jac_scale-0.1.2/jac_scale/user_manager.jac +49 -0
  13. {jac_scale-0.1.1 → jac_scale-0.1.2}/jac_scale.egg-info/PKG-INFO +9 -2
  14. {jac_scale-0.1.1 → jac_scale-0.1.2}/jac_scale.egg-info/SOURCES.txt +5 -0
  15. {jac_scale-0.1.1 → jac_scale-0.1.2}/jac_scale.egg-info/requires.txt +1 -1
  16. {jac_scale-0.1.1 → jac_scale-0.1.2}/pyproject.toml +2 -2
  17. {jac_scale-0.1.1 → jac_scale-0.1.2}/jac_scale/__init__.py +0 -0
  18. {jac_scale-0.1.1 → jac_scale-0.1.2}/jac_scale/abstractions/config/app_config.jac +0 -0
  19. {jac_scale-0.1.1 → jac_scale-0.1.2}/jac_scale/abstractions/config/base_config.jac +0 -0
  20. {jac_scale-0.1.1 → jac_scale-0.1.2}/jac_scale/abstractions/database_provider.jac +0 -0
  21. {jac_scale-0.1.1 → jac_scale-0.1.2}/jac_scale/abstractions/deployment_target.jac +0 -0
  22. {jac_scale-0.1.1 → jac_scale-0.1.2}/jac_scale/abstractions/image_registry.jac +0 -0
  23. {jac_scale-0.1.1 → jac_scale-0.1.2}/jac_scale/abstractions/logger.jac +0 -0
  24. {jac_scale-0.1.1 → jac_scale-0.1.2}/jac_scale/abstractions/models/deployment_result.jac +0 -0
  25. {jac_scale-0.1.1 → jac_scale-0.1.2}/jac_scale/abstractions/models/resource_status.jac +0 -0
  26. {jac_scale-0.1.1 → jac_scale-0.1.2}/jac_scale/config_loader.jac +0 -0
  27. {jac_scale-0.1.1 → jac_scale-0.1.2}/jac_scale/context.jac +0 -0
  28. {jac_scale-0.1.1 → jac_scale-0.1.2}/jac_scale/factories/database_factory.jac +0 -0
  29. {jac_scale-0.1.1 → jac_scale-0.1.2}/jac_scale/factories/deployment_factory.jac +0 -0
  30. {jac_scale-0.1.1 → jac_scale-0.1.2}/jac_scale/factories/registry_factory.jac +0 -0
  31. {jac_scale-0.1.1 → jac_scale-0.1.2}/jac_scale/factories/utility_factory.jac +0 -0
  32. {jac_scale-0.1.1 → jac_scale-0.1.2}/jac_scale/impl/config_loader.impl.jac +0 -0
  33. {jac_scale-0.1.1 → jac_scale-0.1.2}/jac_scale/impl/context.impl.jac +0 -0
  34. {jac_scale-0.1.1 → jac_scale-0.1.2}/jac_scale/impl/memory_hierarchy.main.impl.jac +0 -0
  35. {jac_scale-0.1.1 → jac_scale-0.1.2}/jac_scale/impl/memory_hierarchy.mongo.impl.jac +0 -0
  36. {jac_scale-0.1.1 → jac_scale-0.1.2}/jac_scale/impl/memory_hierarchy.redis.impl.jac +0 -0
  37. {jac_scale-0.1.1 → jac_scale-0.1.2}/jac_scale/jserver/__init__.py +0 -0
  38. {jac_scale-0.1.1 → jac_scale-0.1.2}/jac_scale/jserver/impl/jfast_api.impl.jac +0 -0
  39. {jac_scale-0.1.1 → jac_scale-0.1.2}/jac_scale/jserver/impl/jserver.impl.jac +0 -0
  40. {jac_scale-0.1.1 → jac_scale-0.1.2}/jac_scale/jserver/jfast_api.jac +0 -0
  41. {jac_scale-0.1.1 → jac_scale-0.1.2}/jac_scale/jserver/jserver.jac +0 -0
  42. {jac_scale-0.1.1 → jac_scale-0.1.2}/jac_scale/memory_hierarchy.jac +0 -0
  43. {jac_scale-0.1.1 → jac_scale-0.1.2}/jac_scale/plugin_config.jac +0 -0
  44. {jac_scale-0.1.1 → jac_scale-0.1.2}/jac_scale/providers/database/kubernetes_mongo.jac +0 -0
  45. {jac_scale-0.1.1 → jac_scale-0.1.2}/jac_scale/providers/database/kubernetes_redis.jac +0 -0
  46. {jac_scale-0.1.1 → jac_scale-0.1.2}/jac_scale/providers/registry/dockerhub.jac +0 -0
  47. {jac_scale-0.1.1 → jac_scale-0.1.2}/jac_scale/targets/kubernetes/kubernetes_config.jac +0 -0
  48. {jac_scale-0.1.1 → jac_scale-0.1.2}/jac_scale/targets/kubernetes/utils/kubernetes_utils.impl.jac +0 -0
  49. {jac_scale-0.1.1 → jac_scale-0.1.2}/jac_scale/targets/kubernetes/utils/kubernetes_utils.jac +0 -0
  50. {jac_scale-0.1.1 → jac_scale-0.1.2}/jac_scale/tests/__init__.py +0 -0
  51. {jac_scale-0.1.1 → jac_scale-0.1.2}/jac_scale/tests/conftest.py +0 -0
  52. {jac_scale-0.1.1 → jac_scale-0.1.2}/jac_scale/tests/fixtures/test_api.jac +0 -0
  53. {jac_scale-0.1.1 → jac_scale-0.1.2}/jac_scale/tests/fixtures/todo_app.jac +0 -0
  54. {jac_scale-0.1.1 → jac_scale-0.1.2}/jac_scale/tests/test_abstractions.py +0 -0
  55. {jac_scale-0.1.1 → jac_scale-0.1.2}/jac_scale/tests/test_deploy_k8s.py +0 -0
  56. {jac_scale-0.1.1 → jac_scale-0.1.2}/jac_scale/tests/test_examples.py +0 -0
  57. {jac_scale-0.1.1 → jac_scale-0.1.2}/jac_scale/tests/test_factories.py +0 -0
  58. {jac_scale-0.1.1 → jac_scale-0.1.2}/jac_scale/tests/test_file_upload.py +0 -0
  59. {jac_scale-0.1.1 → jac_scale-0.1.2}/jac_scale/tests/test_k8s_utils.py +0 -0
  60. {jac_scale-0.1.1 → jac_scale-0.1.2}/jac_scale/tests/test_memory_hierarchy.py +0 -0
  61. {jac_scale-0.1.1 → jac_scale-0.1.2}/jac_scale/tests/test_serve.py +0 -0
  62. {jac_scale-0.1.1 → jac_scale-0.1.2}/jac_scale/utilities/loggers/standard_logger.jac +0 -0
  63. {jac_scale-0.1.1 → jac_scale-0.1.2}/jac_scale/utils.jac +0 -0
  64. {jac_scale-0.1.1 → jac_scale-0.1.2}/jac_scale.egg-info/dependency_links.txt +0 -0
  65. {jac_scale-0.1.1 → jac_scale-0.1.2}/jac_scale.egg-info/entry_points.txt +0 -0
  66. {jac_scale-0.1.1 → jac_scale-0.1.2}/jac_scale.egg-info/top_level.txt +0 -0
  67. {jac_scale-0.1.1 → jac_scale-0.1.2}/setup.cfg +0 -0
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jac-scale
3
- Version: 0.1.1
3
+ Version: 0.1.2
4
4
  Author-email: Jason Mars <jason@mars.ninja>
5
5
  Requires-Python: >=3.12
6
6
  Description-Content-Type: text/markdown
7
- Requires-Dist: jaclang>=0.9.10
7
+ Requires-Dist: jaclang>=0.9.11
8
8
  Requires-Dist: python-dotenv<2.0.0,>=1.2.1
9
9
  Requires-Dist: docker<8.0.0,>=7.1.0
10
10
  Requires-Dist: kubernetes<35.0.0,>=34.1.0
@@ -43,6 +43,13 @@ Requires-Dist: python-multipart<1.0.0,>=0.0.21
43
43
 
44
44
  Whether you're developing locally with `jac start` or deploying to production with `jac start --scale`, you get the same powerful features with the flexibility to choose your deployment strategy.
45
45
 
46
+ ### 4. Single Sign-On (SSO) Support
47
+
48
+ - **Google SSO**: Built-in support for Google Sign-In out of the box
49
+ - **Extensible Architecture**: Easily add other providers (GitHub, Microsoft, etc.)
50
+ - **Secure Authentication**: Integrated with JWT for secure session management
51
+ - **User Management**: Automatic account creation and linking
52
+
46
53
  ## Prerequisites
47
54
 
48
55
  - kubenetes(K8s) installed
@@ -25,6 +25,13 @@
25
25
 
26
26
  Whether you're developing locally with `jac start` or deploying to production with `jac start --scale`, you get the same powerful features with the flexibility to choose your deployment strategy.
27
27
 
28
+ ### 4. Single Sign-On (SSO) Support
29
+
30
+ - **Google SSO**: Built-in support for Google Sign-In out of the box
31
+ - **Extensible Architecture**: Easily add other providers (GitHub, Microsoft, etc.)
32
+ - **Secure Authentication**: Integrated with JWT for secure session management
33
+ - **User Management**: Automatic account creation and linking
34
+
28
35
  ## Prerequisites
29
36
 
30
37
  - kubenetes(K8s) installed
@@ -0,0 +1,85 @@
1
+ """Google SSO Provider implementation for jac-scale.
2
+
3
+ This module implements the SSOProvider interface for Google OAuth authentication.
4
+ """
5
+
6
+ import from fastapi { Request, Response }
7
+ import from fastapi_sso.sso.google { GoogleSSO }
8
+ import from jac_scale.sso_provider { SSOProvider, SSOUserInfo }
9
+
10
+ """Google OAuth SSO Provider.
11
+
12
+ This class wraps the fastapi_sso GoogleSSO implementation to conform to
13
+ our SSOProvider interface, enabling consistent handling across all SSO vendors.
14
+ """
15
+ obj GoogleSSOProvider(SSOProvider) {
16
+ has client_id: str,
17
+ client_secret: str,
18
+ redirect_uri: str,
19
+ allow_insecure_http: bool = True,
20
+ _google_sso: (GoogleSSO | None) = None;
21
+
22
+ """Initialize the Google SSO provider."""
23
+ def postinit -> None {
24
+ self._google_sso = GoogleSSO(
25
+ client_id=self.client_id,
26
+ client_secret=self.client_secret,
27
+ redirect_uri=self.redirect_uri,
28
+ allow_insecure_http=self.allow_insecure_http
29
+ );
30
+ }
31
+
32
+ """Initiate Google OAuth authentication flow.
33
+
34
+ Args:
35
+ operation: The operation type ('login' or 'register')
36
+
37
+ Returns:
38
+ RedirectResponse to Google's OAuth authorization page
39
+ """
40
+ async def initiate_auth(operation: str) -> Response {
41
+ if not self._google_sso {
42
+ raise RuntimeError("GoogleSSO not initialized") ;
43
+ }
44
+
45
+ with self._google_sso {
46
+ return await self._google_sso.get_login_redirect();
47
+ }
48
+ }
49
+
50
+ """Handle the OAuth callback from Google.
51
+
52
+ Args:
53
+ request: The FastAPI request object containing OAuth callback data
54
+
55
+ Returns:
56
+ SSOUserInfo: Standardized user information from Google
57
+
58
+ Raises:
59
+ Exception: If authentication fails or user info cannot be retrieved
60
+ """
61
+ async def handle_callback(request: Request) -> SSOUserInfo {
62
+ if not self._google_sso {
63
+ raise RuntimeError("GoogleSSO not initialized") ;
64
+ }
65
+
66
+ with self._google_sso {
67
+ user_info = await self._google_sso.verify_and_process(request);
68
+ return SSOUserInfo(
69
+ email=user_info.email,
70
+ external_id=user_info.id,
71
+ platform=self.get_platform_name(),
72
+ display_name=user_info?.display_name
73
+ );
74
+ }
75
+ }
76
+
77
+ """Get the platform identifier.
78
+
79
+ Returns:
80
+ str: 'google'
81
+ """
82
+ def get_platform_name -> str {
83
+ return "google";
84
+ }
85
+ }
@@ -558,7 +558,7 @@ impl JacAPIServer.create_function_callback(
558
558
  ) {
559
559
  token = authorization[7:];
560
560
  }
561
- username = self.validate_jwt_token(token) if token else None;
561
+ username = self.user_manager.validate_jwt_token(token) if token else None;
562
562
  if not username {
563
563
  return TransportResponse.fail(
564
564
  code='UNAUTHORIZED',
@@ -685,7 +685,7 @@ impl JacAPIServer.create_walker_callback(
685
685
  ) {
686
686
  token = authorization[7:];
687
687
  }
688
- username = self.validate_jwt_token(token) if token else None;
688
+ username = self.user_manager.validate_jwt_token(token) if token else None;
689
689
  if not username {
690
690
  return TransportResponse.fail(
691
691
  code='UNAUTHORIZED',
@@ -783,7 +783,7 @@ impl JacAPIServer.refresh_token(token: (str | None) = None) -> TransportResponse
783
783
  if token.startswith('Bearer ') {
784
784
  token = token[7:];
785
785
  }
786
- new_token = self.refresh_jwt_token(token);
786
+ new_token = self.user_manager.refresh_jwt_token(token);
787
787
  if (not new_token) {
788
788
  return TransportResponse.fail(
789
789
  code='UNAUTHORIZED',
@@ -809,7 +809,7 @@ impl JacAPIServer.create_user(username: str, password: str) -> TransportResponse
809
809
  meta=Meta(extra={'http_status': 400})
810
810
  );
811
811
  }
812
- res['token'] = self.create_jwt_token(username);
812
+ res['token'] = self.user_manager.create_jwt_token(username);
813
813
  return TransportResponse.success(
814
814
  data=res, meta=Meta(extra={'http_status': 201})
815
815
  );
@@ -837,7 +837,7 @@ impl JacAPIServer.update_username(
837
837
  ) {
838
838
  token = Authorization[7:];
839
839
  }
840
- token_username = self.validate_jwt_token(token) if token else None;
840
+ token_username = self.user_manager.validate_jwt_token(token) if token else None;
841
841
  if not token_username {
842
842
  return TransportResponse.fail(
843
843
  code='UNAUTHORIZED',
@@ -869,7 +869,7 @@ impl JacAPIServer.update_username(
869
869
  );
870
870
  }
871
871
  # Generate new JWT token with updated username
872
- result['token'] = self.create_jwt_token(new_username);
872
+ result['token'] = self.user_manager.create_jwt_token(new_username);
873
873
  return TransportResponse.success(
874
874
  data=result, meta=Meta(extra={'http_status': 200})
875
875
  );
@@ -891,7 +891,7 @@ impl JacAPIServer.update_password(
891
891
  ) {
892
892
  token = Authorization[7:];
893
893
  }
894
- token_username = self.validate_jwt_token(token) if token else None;
894
+ token_username = self.user_manager.validate_jwt_token(token) if token else None;
895
895
  if not token_username {
896
896
  return TransportResponse.fail(
897
897
  code='UNAUTHORIZED',
@@ -1066,7 +1066,7 @@ impl JacAPIServer.login(username: str, password: str) -> TransportResponse {
1066
1066
  meta=Meta(extra={'http_status': 401})
1067
1067
  );
1068
1068
  }
1069
- result['token'] = self.create_jwt_token(username);
1069
+ result['token'] = self.user_manager.create_jwt_token(username);
1070
1070
  return TransportResponse.success(
1071
1071
  data=dict[(str, JsonValue)](result), meta=Meta(extra={'http_status': 200})
1072
1072
  );
@@ -1109,219 +1109,6 @@ impl JacAPIServer.postinit -> None {
1109
1109
  }
1110
1110
  self.server.app.add_middleware(CustomHeadersMiddleware);
1111
1111
  }
1112
- self.SUPPORTED_PLATFORMS: dict = {};
1113
- # Load SSO config fresh (not from cached global) to support env var overrides at runtime
1114
- sso_config = get_scale_config().get_sso_config();
1115
- for platform in Platforms {
1116
- key = platform.lower();
1117
- platform_config = sso_config.get(key, {});
1118
-
1119
- client_id = platform_config.get('client_id', '');
1120
- client_secret = platform_config.get('client_secret', '');
1121
-
1122
- if not client_id or not client_secret {
1123
- continue;
1124
- }
1125
-
1126
- self.SUPPORTED_PLATFORMS[platform.value] = {
1127
- "client_id": client_id,
1128
- "client_secret": client_secret
1129
- };
1130
- }
1131
- }
1132
-
1133
- impl JacAPIServer.refresh_jwt_token(token: str) -> (str | None) {
1134
- try {
1135
- decoded = jwt.decode(
1136
- token, JWT_SECRET, algorithms=[JWT_ALGORITHM], options={"verify_exp": True}
1137
- );
1138
- username = decoded.get('username');
1139
-
1140
- if not username {
1141
- return None;
1142
- }
1143
-
1144
- return JacAPIServer.create_jwt_token(username);
1145
- } except Exception {
1146
- return None;
1147
- }
1148
- }
1149
-
1150
- impl JacAPIServer.validate_jwt_token(token: str) -> (str | None) {
1151
- try {
1152
- decoded = jwt.decode(token, JWT_SECRET, algorithms=[JWT_ALGORITHM]);
1153
- return decoded['username'];
1154
- } except Exception {
1155
- return None;
1156
- }
1157
- }
1158
-
1159
- impl JacAPIServer.create_jwt_token(username: str) -> str {
1160
- now = datetime.now(UTC);
1161
- payload: dict[(str, Any)] = {
1162
- 'username': username,
1163
- 'exp': (now + timedelta(days=JWT_EXP_DELTA_DAYS)),
1164
- 'iat': now.timestamp()
1165
- };
1166
- return jwt.encode(payload, JWT_SECRET, algorithm=JWT_ALGORITHM);
1167
- }
1168
-
1169
- impl JacAPIServer.get_sso(platform: str, operation: str) -> (GoogleSSO | None) {
1170
- if (platform not in self.SUPPORTED_PLATFORMS) {
1171
- return None;
1172
- }
1173
- credentials = self.SUPPORTED_PLATFORMS[platform];
1174
- redirect_uri = f"{SSO_HOST}/{platform}/{operation}/callback";
1175
- if (platform == Platforms.GOOGLE.value) {
1176
- return GoogleSSO(
1177
- client_id=credentials['client_id'],
1178
- client_secret=credentials['client_secret'],
1179
- redirect_uri=redirect_uri,
1180
- allow_insecure_http=True
1181
- );
1182
- }
1183
- return None;
1184
- }
1185
-
1186
- impl JacAPIServer.sso_initiate(
1187
- platform: str, operation: str
1188
- ) -> (Response | TransportResponse) {
1189
- import from jaclang.runtimelib.transport { TransportResponse, Meta }
1190
- if (platform not in [p.value for p in Platforms]) {
1191
- return TransportResponse.fail(
1192
- code='INVALID_PLATFORM',
1193
- message=f"Invalid platform '{platform}'. Supported platforms: {', '.join(
1194
- [p.value for p in Platforms]
1195
- )}",
1196
- meta=Meta(extra={'http_status': 400})
1197
- );
1198
- }
1199
- if (platform not in self.SUPPORTED_PLATFORMS) {
1200
- return TransportResponse.fail(
1201
- code='SSO_NOT_CONFIGURED',
1202
- message=f"SSO for platform '{platform}' is not configured. Please set SSO_{platform.upper()}_CLIENT_ID and SSO_{platform.upper()}_CLIENT_SECRET environment variables.",
1203
- meta=Meta(extra={'http_status': 501})
1204
- );
1205
- }
1206
- if (operation not in [o.value for o in Operations]) {
1207
- return TransportResponse.fail(
1208
- code='INVALID_OPERATION',
1209
- message=f"Invalid operation '{operation}'. Must be 'login' or 'register'",
1210
- meta=Meta(extra={'http_status': 400})
1211
- );
1212
- }
1213
- sso = self.get_sso(platform, operation);
1214
- if not sso {
1215
- return TransportResponse.fail(
1216
- code='SSO_INIT_FAILED',
1217
- message=f"Failed to initialize SSO for platform '{platform}'",
1218
- meta=Meta(extra={'http_status': 500})
1219
- );
1220
- }
1221
- with sso {
1222
- return await sso.get_login_redirect();
1223
- }
1224
- }
1225
-
1226
- impl JacAPIServer.sso_callback(
1227
- request: Request, platform: str, operation: str
1228
- ) -> TransportResponse {
1229
- import from jaclang.runtimelib.transport { TransportResponse, Meta }
1230
- if (platform not in [p.value for p in Platforms]) {
1231
- return TransportResponse.fail(
1232
- code='INVALID_PLATFORM',
1233
- message=f"Invalid platform '{platform}'. Supported platforms: {', '.join(
1234
- [p.value for p in Platforms]
1235
- )}",
1236
- meta=Meta(extra={'http_status': 400})
1237
- );
1238
- }
1239
- if (platform not in self.SUPPORTED_PLATFORMS) {
1240
- return TransportResponse.fail(
1241
- code='SSO_NOT_CONFIGURED',
1242
- message=f"SSO for platform '{platform}' is not configured. Please set SSO_{platform.upper()}_CLIENT_ID and SSO_{platform.upper()}_CLIENT_SECRET environment variables.",
1243
- meta=Meta(extra={'http_status': 501})
1244
- );
1245
- }
1246
- if (operation not in [o.value for o in Operations]) {
1247
- return TransportResponse.fail(
1248
- code='INVALID_OPERATION',
1249
- message=f"Invalid operation '{operation}'. Must be 'login' or 'register'",
1250
- meta=Meta(extra={'http_status': 400})
1251
- );
1252
- }
1253
- sso = self.get_sso(platform, operation);
1254
- if not sso {
1255
- return TransportResponse.fail(
1256
- code='SSO_INIT_FAILED',
1257
- message=f"Failed to initialize SSO for platform '{platform}'",
1258
- meta=Meta(extra={'http_status': 500})
1259
- );
1260
- }
1261
- try {
1262
- with sso {
1263
- user_info = await sso.verify_and_process(request);
1264
- email = user_info.email;
1265
- if not email {
1266
- return TransportResponse.fail(
1267
- code='EMAIL_MISSING',
1268
- message=f"Email not provided by {platform}",
1269
- meta=Meta(extra={'http_status': 400})
1270
- );
1271
- }
1272
- if (operation == Operations.LOGIN.value) {
1273
- user = self.user_manager.get_user(email);
1274
- if not user {
1275
- return TransportResponse.fail(
1276
- code='USER_NOT_FOUND',
1277
- message='User not found. Please register first.',
1278
- meta=Meta(extra={'http_status': 404})
1279
- );
1280
- }
1281
- token = self.create_jwt_token(email);
1282
- return TransportResponse.success(
1283
- data={
1284
- 'message': 'Login successful',
1285
- 'email': email,
1286
- 'token': token,
1287
- 'platform': platform,
1288
- 'user': dict[(str, JsonValue)](user)
1289
- },
1290
- meta=Meta(extra={'http_status': 200})
1291
- );
1292
- } elif (operation == Operations.REGISTER.value) {
1293
- existing_user = self.user_manager.get_user(email);
1294
- if existing_user {
1295
- return TransportResponse.fail(
1296
- code='USER_EXISTS',
1297
- message='User already exists. Please login instead.',
1298
- meta=Meta(extra={'http_status': 400})
1299
- );
1300
- }
1301
- random_password = generate_random_password();
1302
- result = self.user_manager.create_user(email, random_password);
1303
- if ('error' in result) {
1304
- return TransportResponse.fail(
1305
- code='USER_CREATION_FAILED',
1306
- message=result.get('error', 'User creation failed'),
1307
- meta=Meta(extra={'http_status': 400})
1308
- );
1309
- }
1310
- token = self.create_jwt_token(email);
1311
- result['token'] = token;
1312
- result['platform'] = platform;
1313
- return TransportResponse.success(
1314
- data=result, meta=Meta(extra={'http_status': 201})
1315
- );
1316
- }
1317
- }
1318
- } except Exception as e {
1319
- return TransportResponse.fail(
1320
- code='AUTHENTICATION_FAILED',
1321
- message=f"Authentication failed: {str(e)}",
1322
- meta=Meta(extra={'http_status': 500})
1323
- );
1324
- }
1325
1112
  }
1326
1113
 
1327
1114
  impl JacAPIServer.register_sso_endpoints -> None {
@@ -1329,7 +1116,7 @@ impl JacAPIServer.register_sso_endpoints -> None {
1329
1116
  JEndPoint(
1330
1117
  method=HTTPMethod.GET,
1331
1118
  path='/sso/{platform}/{operation}',
1332
- callback=self.sso_initiate,
1119
+ callback=self.user_manager.sso_initiate,
1333
1120
  parameters=[
1334
1121
  APIParameter(
1335
1122
  name='platform',
@@ -1358,7 +1145,7 @@ impl JacAPIServer.register_sso_endpoints -> None {
1358
1145
  JEndPoint(
1359
1146
  method=HTTPMethod.GET,
1360
1147
  path='/sso/{platform}/{operation}/callback',
1361
- callback=self.sso_callback,
1148
+ callback=self.user_manager.sso_callback,
1362
1149
  parameters=[
1363
1150
  APIParameter(
1364
1151
  name='platform',
@@ -1455,7 +1242,7 @@ impl JacAPIServer.register_dynamic_walker_endpoint -> None {
1455
1242
  ) {
1456
1243
  token = Authorization[7:];
1457
1244
  }
1458
- username = self.validate_jwt_token(token) if token else None;
1245
+ username = self.user_manager.validate_jwt_token(token) if token else None;
1459
1246
  if not username {
1460
1247
  return TransportResponse.fail(
1461
1248
  code='UNAUTHORIZED',
@@ -1604,7 +1391,7 @@ impl JacAPIServer.register_dynamic_function_endpoint -> None {
1604
1391
  ) {
1605
1392
  token = Authorization[7:];
1606
1393
  }
1607
- username = self.validate_jwt_token(token) if token else None;
1394
+ username = self.user_manager.validate_jwt_token(token) if token else None;
1608
1395
  if not username {
1609
1396
  return TransportResponse.fail(
1610
1397
  code='UNAUTHORIZED',