legate 0.1.0

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 (317) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +345 -0
  4. data/bin/legate +13 -0
  5. data/examples/00_quickstart.rb +51 -0
  6. data/examples/01_simple_agent.rb +105 -0
  7. data/examples/02_multi_tool_agent.rb +140 -0
  8. data/examples/03_custom_tool.rb +93 -0
  9. data/examples/04_agent_instructions.rb +84 -0
  10. data/examples/05_state_and_sessions.rb +91 -0
  11. data/examples/06_callbacks.rb +186 -0
  12. data/examples/07_async_jobs.rb +112 -0
  13. data/examples/08_loop_agent.rb +197 -0
  14. data/examples/09_sequential_workflow.rb +40 -0
  15. data/examples/10_parallel_workflow.rb +34 -0
  16. data/examples/11_agent_delegation.rb +24 -0
  17. data/examples/12_http_client_tool.rb +156 -0
  18. data/examples/13_authentication.rb +220 -0
  19. data/examples/14_mcp_client.rb +154 -0
  20. data/examples/15_mcp_server.rb +79 -0
  21. data/examples/16_webhooks.rb +91 -0
  22. data/examples/README_sequential_agents.md +164 -0
  23. data/examples/advanced/auth/cookie_auth_tool.rb +146 -0
  24. data/examples/advanced/auth/custom_auth_flows_example.rb +626 -0
  25. data/examples/advanced/auth/excon_middleware.rb +317 -0
  26. data/examples/advanced/auth/excon_middleware_auth.rb +399 -0
  27. data/examples/advanced/auth/fiber_auth_example.rb +281 -0
  28. data/examples/advanced/auth/fiber_oidc_example.rb +403 -0
  29. data/examples/advanced/auth/httpbin_bearer_tool.rb +159 -0
  30. data/examples/advanced/auth/oauth2_auth.rb +419 -0
  31. data/examples/advanced/auth/oidc_auth.rb +514 -0
  32. data/examples/advanced/auth/openweather_api.rb +251 -0
  33. data/examples/advanced/auth/openweather_tool.rb +153 -0
  34. data/examples/advanced/auth/query_param_middleware_test.rb +138 -0
  35. data/examples/advanced/auth/service_account.rb +135 -0
  36. data/examples/advanced/auth/test_with_httpbin.rb +202 -0
  37. data/examples/advanced/auth/token_lifecycle_example.rb +428 -0
  38. data/examples/advanced/callback_monitoring.rb +679 -0
  39. data/examples/advanced/mas/fixed_delegation_example.rb +191 -0
  40. data/examples/advanced/mas/loop_workflow.rb +28 -0
  41. data/examples/advanced/mas/mock_planner.rb +77 -0
  42. data/examples/advanced/mas/proper_delegation_example.rb +276 -0
  43. data/examples/advanced/mcp/legate_mcp_server_resource_example.rb +182 -0
  44. data/examples/advanced/mcp/mcp_resource_server_example.rb +309 -0
  45. data/examples/advanced/mcp/mcp_server_async.rb +76 -0
  46. data/examples/advanced/mcp/mcp_server_async_tools.rb +122 -0
  47. data/examples/advanced/mcp/mcp_server_legate_agent.rb +95 -0
  48. data/examples/advanced/mcp/mcp_server_rack.rb +89 -0
  49. data/examples/advanced/random_calculator.rb +104 -0
  50. data/examples/advanced/sleep_agent.rb +153 -0
  51. data/examples/advanced/webhooks/webhook_e2e_runner.rb +110 -0
  52. data/examples/advanced/webhooks/webhook_receiver_agent.rb +58 -0
  53. data/examples/advanced/workflows/task_refinement_loop_agent.rb +278 -0
  54. data/examples/advanced/workflows/travel_planner_auto_sequential.rb +444 -0
  55. data/examples/advanced/workflows/travel_planner_parallel.rb +656 -0
  56. data/examples/advanced/workflows/travel_planner_sequential.rb +512 -0
  57. data/examples/tools/oauth2_example.rb +136 -0
  58. data/examples/tools/sleepy_tool.rb +42 -0
  59. data/lib/legate/activity_log.rb +71 -0
  60. data/lib/legate/agent.rb +959 -0
  61. data/lib/legate/agent_code_generator.rb +185 -0
  62. data/lib/legate/agent_definition.rb +812 -0
  63. data/lib/legate/agentic/decision.rb +49 -0
  64. data/lib/legate/agentic/loop.rb +134 -0
  65. data/lib/legate/agentic.rb +5 -0
  66. data/lib/legate/agents/loop_agent.rb +248 -0
  67. data/lib/legate/agents/parallel_agent.rb +163 -0
  68. data/lib/legate/agents/sequential_agent.rb +190 -0
  69. data/lib/legate/agents.rb +14 -0
  70. data/lib/legate/auth/config.rb +148 -0
  71. data/lib/legate/auth/coordinator.rb +218 -0
  72. data/lib/legate/auth/coordinators/oauth2_coordinator.rb +99 -0
  73. data/lib/legate/auth/coordinators/oidc_coordinator.rb +68 -0
  74. data/lib/legate/auth/coordinators/service_account_coordinator.rb +122 -0
  75. data/lib/legate/auth/credential.rb +157 -0
  76. data/lib/legate/auth/encryption.rb +108 -0
  77. data/lib/legate/auth/error.rb +94 -0
  78. data/lib/legate/auth/exchanged_credential.rb +180 -0
  79. data/lib/legate/auth/excon_middleware.rb +285 -0
  80. data/lib/legate/auth/http_client_utils.rb +364 -0
  81. data/lib/legate/auth/manager.rb +531 -0
  82. data/lib/legate/auth/manager_store.rb +394 -0
  83. data/lib/legate/auth/middleware_factory.rb +290 -0
  84. data/lib/legate/auth/runner.rb +279 -0
  85. data/lib/legate/auth/scheme.rb +125 -0
  86. data/lib/legate/auth/schemes/api_key.rb +212 -0
  87. data/lib/legate/auth/schemes/google_service_account.rb +108 -0
  88. data/lib/legate/auth/schemes/http_bearer.rb +98 -0
  89. data/lib/legate/auth/schemes/oauth2.rb +396 -0
  90. data/lib/legate/auth/schemes/openid_connect.rb +346 -0
  91. data/lib/legate/auth/schemes/service_account.rb +388 -0
  92. data/lib/legate/auth/schemes.rb +40 -0
  93. data/lib/legate/auth/token_manager.rb +362 -0
  94. data/lib/legate/auth/token_store.rb +86 -0
  95. data/lib/legate/auth/tool_context_extension.rb +97 -0
  96. data/lib/legate/auth/tool_integration.rb +188 -0
  97. data/lib/legate/auth/url_guard.rb +81 -0
  98. data/lib/legate/auth.rb +453 -0
  99. data/lib/legate/callbacks/callback_context.rb +71 -0
  100. data/lib/legate/cli/agent_commands.rb +950 -0
  101. data/lib/legate/cli/auth_commands.rb +520 -0
  102. data/lib/legate/cli/base_command.rb +24 -0
  103. data/lib/legate/cli/deployment_commands.rb +934 -0
  104. data/lib/legate/cli/output_helper.rb +108 -0
  105. data/lib/legate/cli/session_commands.rb +138 -0
  106. data/lib/legate/cli/skaffold_commands.rb +223 -0
  107. data/lib/legate/cli/tool_commands.rb +261 -0
  108. data/lib/legate/cli/web_commands.rb +182 -0
  109. data/lib/legate/cli.rb +40 -0
  110. data/lib/legate/configuration/webhooks.rb +113 -0
  111. data/lib/legate/configuration.rb +39 -0
  112. data/lib/legate/definition_store.rb +23 -0
  113. data/lib/legate/errors.rb +118 -0
  114. data/lib/legate/event.rb +161 -0
  115. data/lib/legate/gemini_ai_beta_patch.rb +39 -0
  116. data/lib/legate/generators/agent_generator.rb +412 -0
  117. data/lib/legate/generators/code_validator.rb +48 -0
  118. data/lib/legate/generators/legate/install_generator.rb +35 -0
  119. data/lib/legate/generators/legate/templates/create_legate_tables.rb.tt +36 -0
  120. data/lib/legate/generators/legate/templates/initializer.rb +18 -0
  121. data/lib/legate/generators/runtime_tool_loader.rb +76 -0
  122. data/lib/legate/generators/tool_generator.rb +408 -0
  123. data/lib/legate/generators.rb +11 -0
  124. data/lib/legate/global_definition_registry.rb +506 -0
  125. data/lib/legate/global_tool_manager.rb +135 -0
  126. data/lib/legate/llm/adapter.rb +69 -0
  127. data/lib/legate/llm/gemini.rb +172 -0
  128. data/lib/legate/llm/ollama.rb +80 -0
  129. data/lib/legate/llm.rb +34 -0
  130. data/lib/legate/mcp/client.rb +320 -0
  131. data/lib/legate/mcp/connection/sse.rb +292 -0
  132. data/lib/legate/mcp/connection/stdio.rb +273 -0
  133. data/lib/legate/mcp/connection_manager.rb +103 -0
  134. data/lib/legate/mcp/server/legate_agent_adapter.rb +170 -0
  135. data/lib/legate/mcp/server/legate_direct_agent_adapter.rb +140 -0
  136. data/lib/legate/mcp/server/legate_tool_adapter.rb +119 -0
  137. data/lib/legate/mcp/tool_wrapper.rb +138 -0
  138. data/lib/legate/mcp/util/schema_converter.rb +134 -0
  139. data/lib/legate/mcp.rb +23 -0
  140. data/lib/legate/plan_executor.rb +375 -0
  141. data/lib/legate/planner.rb +839 -0
  142. data/lib/legate/rails/railtie.rb +43 -0
  143. data/lib/legate/rails.rb +9 -0
  144. data/lib/legate/redaction.rb +32 -0
  145. data/lib/legate/session.rb +299 -0
  146. data/lib/legate/session_service/active_record.rb +300 -0
  147. data/lib/legate/session_service/base.rb +68 -0
  148. data/lib/legate/session_service/event_broadcast.rb +74 -0
  149. data/lib/legate/session_service/in_memory.rb +188 -0
  150. data/lib/legate/tool/metadata_dsl.rb +122 -0
  151. data/lib/legate/tool.rb +276 -0
  152. data/lib/legate/tool_code_generator.rb +103 -0
  153. data/lib/legate/tool_context.rb +350 -0
  154. data/lib/legate/tool_loader.rb +39 -0
  155. data/lib/legate/tool_registry.rb +73 -0
  156. data/lib/legate/tool_result.rb +61 -0
  157. data/lib/legate/tools/agent_tool.rb +187 -0
  158. data/lib/legate/tools/base/http_client.rb +319 -0
  159. data/lib/legate/tools/base/safe_url.rb +56 -0
  160. data/lib/legate/tools/base_async_job_tool.rb +91 -0
  161. data/lib/legate/tools/calculator.rb +89 -0
  162. data/lib/legate/tools/cat_facts.rb +81 -0
  163. data/lib/legate/tools/check_job_status_tool.rb +48 -0
  164. data/lib/legate/tools/current_time_tool.rb +64 -0
  165. data/lib/legate/tools/echo.rb +43 -0
  166. data/lib/legate/tools/http_request_tool.rb +105 -0
  167. data/lib/legate/tools/random_number_tool.rb +64 -0
  168. data/lib/legate/tools/read_webpage_tool.rb +92 -0
  169. data/lib/legate/tools/sleepy_tool.rb +74 -0
  170. data/lib/legate/tools/webhook_tool.rb +146 -0
  171. data/lib/legate/version.rb +5 -0
  172. data/lib/legate/web/app.rb +984 -0
  173. data/lib/legate/web/public/css/main.css +4980 -0
  174. data/lib/legate/web/public/images/favicon-256.png +0 -0
  175. data/lib/legate/web/public/images/favicon-32.png +0 -0
  176. data/lib/legate/web/public/images/legate-logo-dark.png +0 -0
  177. data/lib/legate/web/public/images/legate-logo-light.png +0 -0
  178. data/lib/legate/web/public/js/legate.js +616 -0
  179. data/lib/legate/web/public/styles/main.scss +4402 -0
  180. data/lib/legate/web/routes/agent_authentication_routes.rb +530 -0
  181. data/lib/legate/web/routes/agent_definition_routes.rb +803 -0
  182. data/lib/legate/web/routes/agent_generator_routes.rb +80 -0
  183. data/lib/legate/web/routes/agent_interaction_routes.rb +734 -0
  184. data/lib/legate/web/routes/agent_runtime_routes.rb +323 -0
  185. data/lib/legate/web/routes/api_routes.rb +56 -0
  186. data/lib/legate/web/routes/authentication_routes.rb +1541 -0
  187. data/lib/legate/web/routes/core_routes.rb +111 -0
  188. data/lib/legate/web/routes/documentation_routes.rb +220 -0
  189. data/lib/legate/web/routes/tool_generator_routes.rb +81 -0
  190. data/lib/legate/web/routes/tools_ui_routes.rb +207 -0
  191. data/lib/legate/web/sass_compiler.rb +73 -0
  192. data/lib/legate/web/views/_active_session_info.slim +25 -0
  193. data/lib/legate/web/views/_activity_list.slim +55 -0
  194. data/lib/legate/web/views/_agent_card.slim +56 -0
  195. data/lib/legate/web/views/_agent_generator_modal.slim +382 -0
  196. data/lib/legate/web/views/_agent_status_controls.slim +71 -0
  197. data/lib/legate/web/views/_agent_tool_table.slim +74 -0
  198. data/lib/legate/web/views/_chat_message.slim +95 -0
  199. data/lib/legate/web/views/_display_agent_configuration.slim +26 -0
  200. data/lib/legate/web/views/_display_agent_description.slim +11 -0
  201. data/lib/legate/web/views/_display_agent_fallback.slim +15 -0
  202. data/lib/legate/web/views/_display_agent_hierarchy.slim +93 -0
  203. data/lib/legate/web/views/_display_agent_instruction.slim +17 -0
  204. data/lib/legate/web/views/_display_agent_mcp.slim +13 -0
  205. data/lib/legate/web/views/_display_agent_model.slim +17 -0
  206. data/lib/legate/web/views/_display_agent_name.slim +42 -0
  207. data/lib/legate/web/views/_display_agent_output_key.slim +26 -0
  208. data/lib/legate/web/views/_display_agent_type.slim +65 -0
  209. data/lib/legate/web/views/_edit_agent_configuration.slim +74 -0
  210. data/lib/legate/web/views/_edit_agent_description.slim +16 -0
  211. data/lib/legate/web/views/_edit_agent_fallback.slim +25 -0
  212. data/lib/legate/web/views/_edit_agent_hierarchy.slim +98 -0
  213. data/lib/legate/web/views/_edit_agent_instruction.slim +49 -0
  214. data/lib/legate/web/views/_edit_agent_mcp.slim +33 -0
  215. data/lib/legate/web/views/_edit_agent_model.slim +23 -0
  216. data/lib/legate/web/views/_edit_agent_output_key.slim +36 -0
  217. data/lib/legate/web/views/_edit_agent_tools.slim +40 -0
  218. data/lib/legate/web/views/_edit_agent_type.slim +67 -0
  219. data/lib/legate/web/views/_session_error.slim +4 -0
  220. data/lib/legate/web/views/_skeleton.slim +69 -0
  221. data/lib/legate/web/views/_tool_card.slim +9 -0
  222. data/lib/legate/web/views/_tool_generator_modal.slim +311 -0
  223. data/lib/legate/web/views/agent.slim +436 -0
  224. data/lib/legate/web/views/agent_auth.slim +562 -0
  225. data/lib/legate/web/views/agents.slim +369 -0
  226. data/lib/legate/web/views/auth.slim +112 -0
  227. data/lib/legate/web/views/auth_credential_detail.slim +327 -0
  228. data/lib/legate/web/views/auth_credentials.slim +261 -0
  229. data/lib/legate/web/views/auth_debug.slim +94 -0
  230. data/lib/legate/web/views/auth_mapping_detail.slim +151 -0
  231. data/lib/legate/web/views/auth_mapping_new.slim +123 -0
  232. data/lib/legate/web/views/auth_mappings.slim +120 -0
  233. data/lib/legate/web/views/auth_scheme_detail.slim +274 -0
  234. data/lib/legate/web/views/auth_schemes.slim +259 -0
  235. data/lib/legate/web/views/auth_test.slim +418 -0
  236. data/lib/legate/web/views/chat.slim +192 -0
  237. data/lib/legate/web/views/docs_index.slim +105 -0
  238. data/lib/legate/web/views/docs_show.slim +105 -0
  239. data/lib/legate/web/views/error_404.slim +5 -0
  240. data/lib/legate/web/views/index.slim +148 -0
  241. data/lib/legate/web/views/layout.slim +144 -0
  242. data/lib/legate/web/views/tool_detail.slim +87 -0
  243. data/lib/legate/web/views/tools.slim +50 -0
  244. data/lib/legate/web/webhook_listener.rb +367 -0
  245. data/lib/legate/web.rb +9 -0
  246. data/lib/legate.rb +220 -0
  247. data/public/docs/advanced/callbacks.md +828 -0
  248. data/public/docs/advanced/mcp_schema_conversion.md +59 -0
  249. data/public/docs/authentication/api_reference/config.md +210 -0
  250. data/public/docs/authentication/api_reference/credential.md +246 -0
  251. data/public/docs/authentication/api_reference/encryption.md +218 -0
  252. data/public/docs/authentication/api_reference/exchanged_credential.md +271 -0
  253. data/public/docs/authentication/api_reference/excon_middleware.md +175 -0
  254. data/public/docs/authentication/api_reference/index.md +30 -0
  255. data/public/docs/authentication/api_reference/scheme.md +250 -0
  256. data/public/docs/authentication/api_reference/schemes/api_key.md +175 -0
  257. data/public/docs/authentication/api_reference/schemes/google_service_account.md +221 -0
  258. data/public/docs/authentication/api_reference/schemes/http_bearer.md +169 -0
  259. data/public/docs/authentication/api_reference/schemes/oauth2.md +343 -0
  260. data/public/docs/authentication/api_reference/schemes/oidc.md +73 -0
  261. data/public/docs/authentication/api_reference/schemes/openid_connect.md +311 -0
  262. data/public/docs/authentication/api_reference/schemes/service_account.md +287 -0
  263. data/public/docs/authentication/api_reference/token_manager.md +221 -0
  264. data/public/docs/authentication/api_reference/token_store.md +146 -0
  265. data/public/docs/authentication/api_reference/tool_context_extension.md +166 -0
  266. data/public/docs/authentication/guides/api_key.md +190 -0
  267. data/public/docs/authentication/guides/bearer.md +172 -0
  268. data/public/docs/authentication/guides/configuration.md +255 -0
  269. data/public/docs/authentication/guides/custom_flow.md +523 -0
  270. data/public/docs/authentication/guides/index.md +24 -0
  271. data/public/docs/authentication/guides/migration.md +435 -0
  272. data/public/docs/authentication/guides/oauth2.md +252 -0
  273. data/public/docs/authentication/guides/oidc.md +241 -0
  274. data/public/docs/authentication/guides/overview.md +155 -0
  275. data/public/docs/authentication/guides/secure_storage.md +301 -0
  276. data/public/docs/authentication/guides/service_account.md +228 -0
  277. data/public/docs/authentication/guides/token_lifecycle.md +295 -0
  278. data/public/docs/authentication/guides/web_ui_integration.md +504 -0
  279. data/public/docs/authentication/index.md +58 -0
  280. data/public/docs/authentication/troubleshooting/credential_storage.md +550 -0
  281. data/public/docs/authentication/troubleshooting/environment_variables.md +540 -0
  282. data/public/docs/authentication/troubleshooting/index.md +11 -0
  283. data/public/docs/authentication/troubleshooting/oauth2_issues.md +220 -0
  284. data/public/docs/authentication/troubleshooting/oidc_issues.md +412 -0
  285. data/public/docs/authentication/troubleshooting/token_refresh.md +338 -0
  286. data/public/docs/cli/legate_cli_usage.md +363 -0
  287. data/public/docs/core_concepts/legate_agent_lifecycle.md +124 -0
  288. data/public/docs/core_concepts/legate_architecture_overview.md +110 -0
  289. data/public/docs/core_concepts/legate_configuration.md +116 -0
  290. data/public/docs/core_concepts/legate_definition_store.md +102 -0
  291. data/public/docs/core_concepts/legate_planner.md +94 -0
  292. data/public/docs/core_concepts/legate_session_service.md +104 -0
  293. data/public/docs/error_handling/legate_error_handling.md +122 -0
  294. data/public/docs/examples.md +199 -0
  295. data/public/docs/getting_started.md +111 -0
  296. data/public/docs/guides/agentic_agents.md +137 -0
  297. data/public/docs/guides/ai_code_generators.md +437 -0
  298. data/public/docs/guides/auto_loading.md +326 -0
  299. data/public/docs/guides/configuring_agent_webhooks.md +219 -0
  300. data/public/docs/guides/http_client_usage.md +264 -0
  301. data/public/docs/guides/llm_providers.md +137 -0
  302. data/public/docs/guides/mcp_client_integration.md +232 -0
  303. data/public/docs/guides/mcp_server_exposure.md +206 -0
  304. data/public/docs/guides/rails_integration.md +128 -0
  305. data/public/docs/guides/sending_outbound_webhooks.md +227 -0
  306. data/public/docs/guides/streaming.md +112 -0
  307. data/public/docs/guides/webhooks.md +288 -0
  308. data/public/docs/introduction.md +51 -0
  309. data/public/docs/multi_agent_systems/advanced_features.md +57 -0
  310. data/public/docs/multi_agent_systems/agent_delegation.md +190 -0
  311. data/public/docs/multi_agent_systems/agent_hierarchy.md +49 -0
  312. data/public/docs/multi_agent_systems/state_management.md +47 -0
  313. data/public/docs/multi_agent_systems/workflow_agents.md +72 -0
  314. data/public/docs/tools/legate_built_in_tools.md +332 -0
  315. data/public/docs/tools/legate_tools_and_registry.md +263 -0
  316. data/public/docs/web_ui/legate_web_ui.md +137 -0
  317. metadata +823 -0
@@ -0,0 +1,220 @@
1
+ # Troubleshooting OAuth2 Authentication Issues
2
+
3
+ This guide addresses common issues encountered when implementing OAuth2 authentication in the Legate Ruby library.
4
+
5
+ ## Common Issues and Solutions
6
+
7
+ ### Authorization Flow Not Starting
8
+
9
+ **Symptoms:**
10
+ - No authentication URL is generated
11
+ - No Fiber yield occurs
12
+ - Authentication flow doesn't start
13
+
14
+ **Possible Causes and Solutions:**
15
+
16
+ 1. **Incorrect Scheme Type**
17
+ ```ruby
18
+ # Incorrect: Using the wrong scheme type
19
+ scheme = Legate::Auth::Schemes::ApiKey.new
20
+
21
+ # Correct: Use the OAuth2 scheme
22
+ scheme = Legate::Auth::Schemes::OAuth2.new(
23
+ authorization_url: 'https://provider.com/authorize',
24
+ token_url: 'https://provider.com/token',
25
+ scopes: ['profile', 'email']
26
+ )
27
+ ```
28
+
29
+ 2. **Missing Required Parameters**
30
+ ```ruby
31
+ # Incorrect: Missing required parameters
32
+ scheme = Legate::Auth::Schemes::OAuth2.new
33
+
34
+ # Correct: Include all required parameters
35
+ scheme = Legate::Auth::Schemes::OAuth2.new(
36
+ authorization_url: 'https://provider.com/authorize',
37
+ token_url: 'https://provider.com/token',
38
+ scopes: ['profile', 'email']
39
+ )
40
+ ```
41
+
42
+ 3. **Incorrect Credential Configuration**
43
+ ```ruby
44
+ # Incorrect: Wrong auth_type or missing client_id
45
+ credential = Legate::Auth::Credential.new(
46
+ auth_type: :api_key, # Wrong auth_type
47
+ api_key: 'secret'
48
+ )
49
+
50
+ # Correct: Proper OAuth2 credential
51
+ credential = Legate::Auth::Credential.new(
52
+ auth_type: :oauth2,
53
+ client_id: ENV['CLIENT_ID'],
54
+ client_secret: ENV['CLIENT_SECRET']
55
+ )
56
+ ```
57
+
58
+ ### Token Exchange Failures
59
+
60
+ **Symptoms:**
61
+ - Authorization succeeds but token exchange fails
62
+ - Error messages about invalid grant or client authentication
63
+
64
+ **Possible Causes and Solutions:**
65
+
66
+ 1. **Invalid Redirect URI**
67
+ ```ruby
68
+ # Ensure the redirect URI exactly matches what's registered with the provider
69
+ # The redirect URI in your application must EXACTLY match the one registered
70
+ ```
71
+
72
+ 2. **Client Authentication Issues**
73
+ ```ruby
74
+ # Check if client_secret is correct and properly configured
75
+ credential = Legate::Auth::Credential.new(
76
+ auth_type: :oauth2,
77
+ client_id: ENV['CLIENT_ID'],
78
+ client_secret: ENV['CLIENT_SECRET'] # Verify this is correct
79
+ )
80
+ ```
81
+
82
+ 3. **Authorization Code Expired**
83
+ ```ruby
84
+ # Authorization codes usually expire quickly (30-60 seconds)
85
+ # Ensure your application exchanges the code for a token immediately
86
+ ```
87
+
88
+ 4. **PKCE Issues**
89
+ ```ruby
90
+ # If using PKCE, verify the code_verifier matches what was used for code_challenge
91
+ scheme = Legate::Auth::Schemes::OAuth2.new(
92
+ authorization_url: 'https://provider.com/authorize',
93
+ token_url: 'https://provider.com/token',
94
+ scopes: ['profile', 'email'],
95
+ use_pkce: true # Using PKCE
96
+ )
97
+ ```
98
+
99
+ ### Token Refresh Issues
100
+
101
+ **Symptoms:**
102
+ - Access token expires and refresh fails
103
+ - "Invalid refresh token" errors
104
+
105
+ **Possible Causes and Solutions:**
106
+
107
+ 1. **Expired Refresh Token**
108
+ ```ruby
109
+ # Refresh tokens may have limited lifetimes
110
+ # If expired, you need to restart the full authorization flow
111
+ ```
112
+
113
+ 2. **Revoked Refresh Token**
114
+ ```ruby
115
+ # Providers may revoke refresh tokens if:
116
+ # - Too many refresh attempts occur
117
+ # - User revokes app access
118
+ # - Security policies trigger revocation
119
+ ```
120
+
121
+ 3. **Incorrect Token Storage**
122
+ ```ruby
123
+ # Tokens are cached in scoped session state (as plaintext token.to_h).
124
+ # For at-rest encryption, apply the opt-in Legate::Auth::Encryption module yourself.
125
+ ```
126
+
127
+ ### Scope-Related Issues
128
+
129
+ **Symptoms:**
130
+ - Authentication succeeds but API access fails with permission errors
131
+ - Provider returns scope-related errors
132
+
133
+ **Possible Causes and Solutions:**
134
+
135
+ 1. **Missing Required Scopes**
136
+ ```ruby
137
+ # Incorrect: Missing necessary scopes
138
+ scheme = Legate::Auth::Schemes::OAuth2.new(
139
+ authorization_url: 'https://provider.com/authorize',
140
+ token_url: 'https://provider.com/token',
141
+ scopes: ['profile'] # Missing needed scopes
142
+ )
143
+
144
+ # Correct: Include all required scopes
145
+ scheme = Legate::Auth::Schemes::OAuth2.new(
146
+ authorization_url: 'https://provider.com/authorize',
147
+ token_url: 'https://provider.com/token',
148
+ scopes: ['profile', 'email', 'data:read', 'data:write']
149
+ )
150
+ ```
151
+
152
+ 2. **Scope Approval Issues**
153
+ ```ruby
154
+ # If the user didn't approve all requested scopes:
155
+ # 1. Check the exchanged token's scope field
156
+ # 2. Request only essential scopes
157
+ # 3. Handle graceful degradation if some scopes are denied
158
+ ```
159
+
160
+ ## Provider-Specific Issues
161
+
162
+ ### Google OAuth2
163
+
164
+ - **Invalid Client Error**: Ensure the client ID and redirect URI match exactly what's in the Google Developer Console
165
+ - **Consent Screen Required**: Verify you've configured the OAuth consent screen in the Developer Console
166
+ - **Domain Verification**: For some scopes, domain verification may be required
167
+
168
+ ### GitHub OAuth2
169
+
170
+ - **Application Suspended**: GitHub may suspend OAuth apps that exceed rate limits or violate terms
171
+ - **User-to-Server vs. Server-to-Server**: Different authentication flows for different access types
172
+
173
+ ### Microsoft/Azure OAuth2
174
+
175
+ - **Tenant Configuration**: Ensure proper tenant ID configuration
176
+ - **Admin Consent Required**: Some scopes require admin consent in organizational contexts
177
+
178
+ ## Debugging Techniques
179
+
180
+ ### Enable Detailed Logging
181
+
182
+ Logging verbosity is controlled by the `LEGATE_LOG_LEVEL` environment variable (e.g. `DEBUG`, `INFO`, `WARN`, `ERROR`), not by a configuration setting:
183
+
184
+ ```bash
185
+ # Run with debug-level logging to see authentication details
186
+ LEGATE_LOG_LEVEL=DEBUG bundle exec ...
187
+ ```
188
+
189
+ ### Inspect Token Lifecycle Events
190
+
191
+ ```ruby
192
+ # Register callbacks on the TokenManager to observe token operations.
193
+ # Each callback receives a single data Hash.
194
+ token_manager.on(:refresh_success) do |data|
195
+ puts "Refreshed token for #{data[:scheme]&.scheme_type}"
196
+ end
197
+
198
+ token_manager.on(:refresh_failure) do |data|
199
+ puts "Refresh failed: #{data[:error]&.message}"
200
+ end
201
+ ```
202
+
203
+ ### Inspect the Exchanged Token
204
+
205
+ ```ruby
206
+ # Exchange the authorization code and inspect the resulting credential
207
+ token = scheme.exchange_token(config, credential)
208
+ puts "Access token present: #{!token[:access_token].nil?}"
209
+ puts "Refreshable: #{token.refreshable?}"
210
+ puts "Expires at: #{token[:expires_at]}"
211
+ ```
212
+
213
+ ## Next Steps
214
+
215
+ If you're still experiencing issues after trying these solutions:
216
+
217
+ 1. Check the [OAuth2 Specification](https://oauth.net/2/) for details on the standard
218
+ 2. Review your provider's specific OAuth2 documentation
219
+ 3. Check the Legate Ruby [GitHub Issues](https://github.com/tweibley/legate/issues) for similar problems
220
+ 4. File a detailed bug report with full logs and reproduction steps
@@ -0,0 +1,412 @@
1
+ # OpenID Connect Authentication Issues
2
+
3
+ This guide addresses common issues encountered when implementing and using OpenID Connect (OIDC) authentication in the Legate Ruby library.
4
+
5
+ ## Common OpenID Connect Issues
6
+
7
+ ### Discovery URL Problems
8
+
9
+ **Symptoms:**
10
+ - "Unable to fetch discovery document" errors
11
+ - Connection timeout when initializing OIDC scheme
12
+ - JSON parsing errors during setup
13
+
14
+ **Possible Causes and Solutions:**
15
+
16
+ 1. **Incorrect Discovery URL**
17
+ ```ruby
18
+ # Problem: The discovery URL is incorrect or malformed
19
+
20
+ # Solution: Verify the discovery URL
21
+ scheme = Legate::Auth::Schemes::OpenIDConnect.new(
22
+ # Incorrect URL
23
+ # discovery_url: 'https://accounts.google.com/openid-configuration'
24
+
25
+ # Correct URL
26
+ discovery_url: 'https://accounts.google.com/.well-known/openid-configuration'
27
+ )
28
+ ```
29
+
30
+ 2. **Network/Connectivity Issues**
31
+ ```ruby
32
+ # Problem: Network connectivity issues prevent access to the discovery document
33
+
34
+ # Solution: Implement retry logic or manually specify endpoints
35
+ scheme = Legate::Auth::Schemes::OpenIDConnect.new(
36
+ # Fall back to manual configuration if discovery fails
37
+ authorization_url: 'https://accounts.google.com/o/oauth2/auth',
38
+ token_url: 'https://oauth2.googleapis.com/token',
39
+ userinfo_url: 'https://openidconnect.googleapis.com/v1/userinfo',
40
+ jwks_url: 'https://www.googleapis.com/oauth2/v3/certs',
41
+ scopes: ['openid', 'profile', 'email']
42
+ )
43
+ ```
44
+
45
+ 3. **Provider Service Outage**
46
+ ```ruby
47
+ # Problem: The identity provider's discovery endpoint is down
48
+
49
+ # Solution: Implement caching of discovery document
50
+ # Example implementation of discovery caching
51
+
52
+ def get_cached_discovery(url, cache_ttl = 86400)
53
+ cache_key = "oidc_discovery_#{Digest::MD5.hexdigest(url)}"
54
+ cached = session[:cache]&.dig(cache_key)
55
+
56
+ if cached && cached[:timestamp] > Time.now.to_i - cache_ttl
57
+ return cached[:document]
58
+ end
59
+
60
+ response = HTTP.get(url)
61
+ document = JSON.parse(response.body.to_s)
62
+
63
+ session[:cache] ||= {}
64
+ session[:cache][cache_key] = {
65
+ timestamp: Time.now.to_i,
66
+ document: document
67
+ }
68
+
69
+ document
70
+ end
71
+ ```
72
+
73
+ ### ID Token Validation Issues
74
+
75
+ **Symptoms:**
76
+ - "Invalid token signature" errors
77
+ - "Invalid issuer" or "Invalid audience" errors
78
+ - "Token expired" errors when the token should be valid
79
+
80
+ **Possible Causes and Solutions:**
81
+
82
+ 1. **Signature Verification Issues**
83
+ ```ruby
84
+ # Problem: Unable to verify ID token signature
85
+
86
+ # Solution: Ensure the JWKS endpoint is correctly specified (either via
87
+ # discovery_url or an explicit jwks_url). The scheme caches the JWKS
88
+ # internally with a fixed 5-minute TTL; there is no jwks_cache_ttl option.
89
+ scheme = Legate::Auth::Schemes::OpenIDConnect.new(
90
+ discovery_url: 'https://accounts.google.com/.well-known/openid-configuration'
91
+ )
92
+ # Or provide the JWKS URL explicitly:
93
+ scheme = Legate::Auth::Schemes::OpenIDConnect.new(
94
+ authorization_url: 'https://accounts.google.com/o/oauth2/auth',
95
+ token_url: 'https://oauth2.googleapis.com/token',
96
+ jwks_url: 'https://www.googleapis.com/oauth2/v3/certs'
97
+ )
98
+
99
+ # For debugging, print token details (don't do this in production)
100
+ require 'jwt'
101
+ decoded_token = JWT.decode(id_token, nil, false)[0]
102
+ puts "Token headers: #{JWT.decode(id_token, nil, false)[1]}"
103
+ puts "Token claims: #{decoded_token}"
104
+ ```
105
+
106
+ 2. **Clock Skew Issues**
107
+ ```ruby
108
+ # Problem: Server time is out of sync with the identity provider
109
+
110
+ # Solution: Keep your server clock in sync with the identity provider.
111
+ # ID token claims (exp/iat) are validated during verify_id_token, so an
112
+ # out-of-sync clock is the most common cause of spurious "expired"/"not yet
113
+ # valid" errors.
114
+ # On Linux: sudo ntpdate time.google.com
115
+ # On macOS: sudo sntp -sS time.apple.com
116
+ ```
117
+
118
+ 3. **Incorrect Audience or Issuer**
119
+ ```ruby
120
+ # Problem: The token's audience or issuer doesn't match expected values
121
+
122
+ # Solution: Set the expected issuer on the scheme, and pass the expected
123
+ # audience to verify_id_token. (There is no additional_validation option.)
124
+ scheme = Legate::Auth::Schemes::OpenIDConnect.new(
125
+ discovery_url: 'https://accounts.google.com/.well-known/openid-configuration',
126
+ issuer: 'https://accounts.google.com' # validated when set
127
+ )
128
+
129
+ # verify_id_token(id_token, nonce = nil, audience = nil)
130
+ claims = scheme.verify_id_token(id_token, nonce, ENV['CLIENT_ID'])
131
+ ```
132
+
133
+ ### Scope-Related Issues
134
+
135
+ **Symptoms:**
136
+ - Missing profile information
137
+ - "Invalid scope" errors
138
+ - ID token doesn't contain expected claims
139
+
140
+ **Possible Causes and Solutions:**
141
+
142
+ 1. **Missing 'openid' Scope**
143
+ ```ruby
144
+ # Problem: The 'openid' scope is missing
145
+
146
+ # Solution: Always include 'openid' scope for OIDC
147
+ scheme = Legate::Auth::Schemes::OpenIDConnect.new(
148
+ discovery_url: 'https://accounts.google.com/.well-known/openid-configuration',
149
+ # Incorrect: scopes: ['profile', 'email']
150
+
151
+ # Correct:
152
+ scopes: ['openid', 'profile', 'email'] # 'openid' is required for OIDC
153
+ )
154
+ ```
155
+
156
+ 2. **Insufficient Scopes for Desired Claims**
157
+ ```ruby
158
+ # Problem: The token doesn't contain required user information
159
+
160
+ # Solution: Request additional scopes
161
+ scheme = Legate::Auth::Schemes::OpenIDConnect.new(
162
+ discovery_url: 'https://accounts.google.com/.well-known/openid-configuration',
163
+ scopes: ['openid', 'profile', 'email', 'address', 'phone'] # Request more information
164
+ )
165
+ ```
166
+
167
+ 3. **Provider-Specific Scope Format**
168
+ ```ruby
169
+ # Problem: The provider requires scopes in a specific format
170
+
171
+ # Solution: Format scopes according to provider requirements
172
+
173
+ # For Microsoft Azure AD, use full URL format for scopes
174
+ scheme = Legate::Auth::Schemes::OpenIDConnect.new(
175
+ discovery_url: 'https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration',
176
+ scopes: [
177
+ 'openid',
178
+ 'profile',
179
+ 'email',
180
+ 'https://graph.microsoft.com/User.Read'
181
+ ]
182
+ )
183
+ ```
184
+
185
+ ### Authentication Flow Issues
186
+
187
+ **Symptoms:**
188
+ - Users redirected to incorrect URLs
189
+ - Authentication callbacks failing
190
+ - "State mismatch" errors
191
+ - "Invalid redirect URI" errors
192
+
193
+ **Possible Causes and Solutions:**
194
+
195
+ 1. **Incorrect Redirect URI**
196
+ ```ruby
197
+ # Problem: The redirect URI doesn't match what's registered with the provider
198
+
199
+ # Solution: Use exactly the same redirect URI as registered
200
+ scheme = Legate::Auth::Schemes::OpenIDConnect.new(
201
+ discovery_url: 'https://accounts.google.com/.well-known/openid-configuration',
202
+ scopes: ['openid', 'profile', 'email']
203
+ )
204
+
205
+ # Generate auth config with exact matching redirect URI
206
+ config = scheme.generate_auth_config(
207
+ credential,
208
+ callback_url: 'https://exact-registered-uri.example.com/callback' # Must match exactly
209
+ )
210
+ ```
211
+
212
+ 2. **State Parameter Issues**
213
+ ```ruby
214
+ # Problem: The state parameter is missing or mismatched
215
+
216
+ # Solution: Verify that state is preserved in the callback
217
+ auth_response = {
218
+ auth_request_id: config.auth_request_id,
219
+ auth_response_uri: callback_uri_with_state
220
+ }
221
+
222
+ # For debugging, check the state parameter
223
+ uri = URI.parse(callback_uri_with_state)
224
+ params = CGI.parse(uri.query || '')
225
+ state = params['state']&.first
226
+
227
+ puts "Expected state: #{config.callback_params[:state]}"
228
+ puts "Received state: #{state}"
229
+ ```
230
+
231
+ 3. **Invalid Response Type**
232
+ ```ruby
233
+ # Problem: Using the wrong response type
234
+
235
+ # Solution: Specify the correct response type
236
+ scheme = Legate::Auth::Schemes::OpenIDConnect.new(
237
+ discovery_url: 'https://accounts.google.com/.well-known/openid-configuration',
238
+ scopes: ['openid', 'profile', 'email'],
239
+ additional_params: {
240
+ 'response_type' => 'code' # The standard for authorization code flow
241
+ }
242
+ )
243
+ ```
244
+
245
+ ## Provider-Specific Issues
246
+
247
+ ### Google OpenID Connect
248
+
249
+ 1. **Consent Screen Configuration**
250
+ - Ensure you've configured the OAuth consent screen in the Google Cloud Console
251
+ - Verify that you've added all the scopes you request to the allowed scopes
252
+
253
+ 2. **Refresh Token Issues**
254
+ ```ruby
255
+ # Problem: Not receiving a refresh token
256
+
257
+ # Solution: Request offline access
258
+ scheme = Legate::Auth::Schemes::OpenIDConnect.new(
259
+ discovery_url: 'https://accounts.google.com/.well-known/openid-configuration',
260
+ scopes: ['openid', 'profile', 'email'],
261
+ additional_params: {
262
+ 'access_type' => 'offline',
263
+ 'prompt' => 'consent' # Force consent screen to appear
264
+ }
265
+ )
266
+ ```
267
+
268
+ 3. **Domain Restrictions**
269
+ - Check if your Google Workspace settings restrict access to specific domains
270
+ - Verify that your authorized domains are correctly configured
271
+
272
+ ### Microsoft Azure AD
273
+
274
+ 1. **Tenant Configuration**
275
+ ```ruby
276
+ # Problem: Using the wrong tenant
277
+
278
+ # Solution: Specify the correct tenant ID
279
+ tenant_id = 'common' # Use 'common' for multi-tenant, or a specific tenant ID
280
+
281
+ scheme = Legate::Auth::Schemes::OpenIDConnect.new(
282
+ discovery_url: "https://login.microsoftonline.com/#{tenant_id}/v2.0/.well-known/openid-configuration",
283
+ scopes: ['openid', 'profile', 'email', 'offline_access']
284
+ )
285
+ ```
286
+
287
+ 2. **App Registration Issues**
288
+ - Ensure you've granted admin consent for the required permissions
289
+ - Check that the API permissions in your app registration match your requested scopes
290
+
291
+ 3. **Conditional Access Policies**
292
+ - Conditional access policies might block authentication
293
+ - Verify that your app isn't blocked by conditional access policies
294
+
295
+ ### Auth0
296
+
297
+ 1. **Audience Parameter**
298
+ ```ruby
299
+ # Problem: Missing audience parameter for Auth0
300
+
301
+ # Solution: Specify the audience
302
+ domain = 'your-domain.auth0.com'
303
+
304
+ scheme = Legate::Auth::Schemes::OpenIDConnect.new(
305
+ discovery_url: "https://#{domain}/.well-known/openid-configuration",
306
+ scopes: ['openid', 'profile', 'email'],
307
+ additional_params: {
308
+ 'audience' => 'https://api.example.com' # Your API identifier
309
+ }
310
+ )
311
+ ```
312
+
313
+ 2. **Rules and Actions**
314
+ - Auth0 rules or actions might be modifying the authentication flow
315
+ - Check your Auth0 dashboard for rules that might affect your authentication
316
+
317
+ ## Debugging Techniques
318
+
319
+ ### Inspecting ID Tokens
320
+
321
+ For debugging ID token issues:
322
+
323
+ ```ruby
324
+ # Decode and inspect an ID token
325
+ require 'jwt'
326
+
327
+ # Decode without verification (for debugging only!)
328
+ token_parts = JWT.decode(id_token, nil, false)
329
+ token_payload = token_parts[0]
330
+ token_header = token_parts[1]
331
+
332
+ puts "Token header: #{token_header.inspect}"
333
+ puts "Token payload: #{token_payload.inspect}"
334
+
335
+ # Check critical claims
336
+ puts "Issuer: #{token_payload['iss']}"
337
+ puts "Subject: #{token_payload['sub']}"
338
+ puts "Audience: #{token_payload['aud']}"
339
+ puts "Expiration: #{Time.at(token_payload['exp'])}"
340
+ puts "Issued at: #{Time.at(token_payload['iat'])}"
341
+ puts "Available claims: #{token_payload.keys.join(', ')}"
342
+ ```
343
+
344
+ ### Debugging UserInfo Endpoint
345
+
346
+ For debugging userinfo endpoint issues:
347
+
348
+ ```ruby
349
+ # Manually call the userinfo endpoint
350
+ require 'http'
351
+
352
+ access_token = exchanged_credential[:access_token]
353
+ userinfo_url = scheme.userinfo_url || 'https://provider.example.com/userinfo'
354
+
355
+ response = HTTP.auth("Bearer #{access_token}")
356
+ .get(userinfo_url)
357
+
358
+ if response.status.success?
359
+ user_info = JSON.parse(response.body.to_s)
360
+ puts "UserInfo response: #{user_info.inspect}"
361
+ else
362
+ puts "UserInfo error: #{response.status} - #{response.body}"
363
+ end
364
+ ```
365
+
366
+ ### PKCE Verification
367
+
368
+ For debugging PKCE-related issues:
369
+
370
+ ```ruby
371
+ # Manually trace PKCE parameters
372
+ code_verifier = "random_secure_string_of_at_least_43_characters"
373
+ require 'base64'
374
+ require 'digest'
375
+
376
+ # Generate code_challenge (S256 method)
377
+ code_challenge = Base64.urlsafe_encode64(
378
+ Digest::SHA256.digest(code_verifier)
379
+ ).gsub(/=+$/, '')
380
+
381
+ puts "Code verifier: #{code_verifier}"
382
+ puts "Code challenge: #{code_challenge}"
383
+
384
+ # Use these in manual authorization request
385
+ auth_url = "https://provider.example.com/authorize" \
386
+ "?client_id=#{client_id}" \
387
+ "&response_type=code" \
388
+ "&code_challenge=#{code_challenge}" \
389
+ "&code_challenge_method=S256"
390
+ ```
391
+
392
+ ## When All Else Fails
393
+
394
+ If you've tried all the solutions and still encounter issues:
395
+
396
+ 1. **Check IdP Logs**:
397
+ - Most identity providers offer authentication logs
398
+ - Review logs for your client ID around the time of failure
399
+
400
+ 2. **Use OpenID Connect Debuggers**:
401
+ - [OpenID Connect Debugger](https://oidcdebugger.com/)
402
+ - [Auth0 JWT Debugger](https://jwt.io/)
403
+
404
+ 3. **Contact Provider Support**:
405
+ - Provide detailed error messages and timestamps
406
+ - Share your client ID (but never share client secrets)
407
+
408
+ ## Next Steps
409
+
410
+ - [OAuth2 Troubleshooting](./oauth2_issues): For OAuth2-specific issues
411
+ - [Token Refresh Problems](./token_refresh): For issues with token refresh
412
+ - [Credential Storage Issues](./credential_storage): For issues with storing credentials