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,228 @@
1
+ # Service Account Authentication
2
+
3
+ Service accounts provide a way to authenticate applications without user interaction, typically for server-to-server communication. The Legate Ruby library supports service account authentication with various cloud providers.
4
+
5
+ ## Overview
6
+
7
+ Service account authentication uses a key-based approach where:
8
+
9
+ 1. The application creates a signed JWT (JSON Web Token) using the service account's private key
10
+ 2. This JWT is exchanged for an access token from the authorization server
11
+ 3. The access token is then used to authenticate API requests
12
+
13
+ This flow is ideal for:
14
+ - Server-to-server integrations
15
+ - Background processing
16
+ - Automated tasks and scheduled jobs
17
+ - Services that run without user interaction
18
+
19
+ ## Configuration
20
+
21
+ ### Creating a Service Account Scheme
22
+
23
+ ```ruby
24
+ # Basic service account scheme
25
+ scheme = Legate::Auth::Schemes::ServiceAccount.new(
26
+ token_url: 'https://oauth2.googleapis.com/token',
27
+ audience: 'https://oauth2.googleapis.com/token',
28
+ scopes: ['https://www.googleapis.com/auth/cloud-platform']
29
+ )
30
+
31
+ # With additional options
32
+ scheme = Legate::Auth::Schemes::ServiceAccount.new(
33
+ token_url: 'https://oauth2.googleapis.com/token',
34
+ audience: 'https://oauth2.googleapis.com/token',
35
+ scopes: ['https://www.googleapis.com/auth/cloud-platform'],
36
+ token_lifetime: 3600 # Access token lifetime in seconds (default: 3600)
37
+ )
38
+ ```
39
+
40
+ ### Creating a Service Account Credential
41
+
42
+ There are several ways to provide the service account key information:
43
+
44
+ #### From a JSON Key File
45
+
46
+ ```ruby
47
+ # Using a JSON key file. service_account_key takes a raw JSON string
48
+ # (not a parsed Hash); or use service_account_key_file with a path.
49
+ credential = Legate::Auth::Credential.new(
50
+ auth_type: :service_account,
51
+ service_account_key: File.read('service-account-key.json')
52
+ )
53
+
54
+ # Or point at the file directly:
55
+ credential = Legate::Auth::Credential.new(
56
+ auth_type: :service_account,
57
+ service_account_key_file: '/path/to/service-account-key.json'
58
+ )
59
+ ```
60
+
61
+ #### From Environment Variable
62
+
63
+ ```ruby
64
+ # Store the entire JSON key contents in an environment variable (raw string)
65
+ credential = Legate::Auth::Credential.new(
66
+ auth_type: :service_account,
67
+ service_account_key: ENV['SERVICE_ACCOUNT_JSON']
68
+ )
69
+
70
+ # Or reference it lazily with the ENV: prefix:
71
+ credential = Legate::Auth::Credential.new(
72
+ auth_type: :service_account,
73
+ service_account_key: 'ENV:SERVICE_ACCOUNT_JSON'
74
+ )
75
+ ```
76
+
77
+ ## Authentication Flow
78
+
79
+ Service account authentication is non-interactive and happens automatically when the tool is executed:
80
+
81
+ ```ruby
82
+ # 1. Configure the service account scheme
83
+ scheme = Legate::Auth::Schemes::ServiceAccount.new(
84
+ token_url: 'https://oauth2.googleapis.com/token',
85
+ audience: 'https://oauth2.googleapis.com/token',
86
+ scopes: ['https://www.googleapis.com/auth/cloud-platform']
87
+ )
88
+
89
+ # 2. Configure the credential with the service account key (raw JSON string)
90
+ credential = Legate::Auth::Credential.new(
91
+ auth_type: :service_account,
92
+ service_account_key: File.read('service-account-key.json')
93
+ )
94
+
95
+ # 3. Exchange the credential for an access token
96
+ token = scheme.exchange_token(credential)
97
+
98
+ # 4. Apply the token to outbound requests
99
+ connection = Legate::Auth.create_connection('https://api.example.com',
100
+ scheme: scheme,
101
+ credential: token
102
+ )
103
+ result = connection.get(path: '/resource')
104
+ ```
105
+
106
+ ## Provider-Specific Configurations
107
+
108
+ ### Google Cloud Service Account
109
+
110
+ ```ruby
111
+ # Using the GoogleServiceAccount scheme (recommended for Google Cloud)
112
+ scheme = Legate::Auth::Schemes::GoogleServiceAccount.new(
113
+ scopes: [
114
+ 'https://www.googleapis.com/auth/cloud-platform',
115
+ 'https://www.googleapis.com/auth/bigquery'
116
+ ]
117
+ )
118
+
119
+ credential = Legate::Auth::Credential.new(
120
+ auth_type: :google_service_account,
121
+ service_account_key: File.read('google-service-account.json')
122
+ )
123
+ ```
124
+
125
+ ### AWS Service Account
126
+
127
+ ```ruby
128
+ # AWS STS token exchange
129
+ scheme = Legate::Auth::Schemes::ServiceAccount.new(
130
+ token_url: 'https://sts.amazonaws.com',
131
+ audience: 'aws.amazonaws.com'
132
+ )
133
+
134
+ credential = Legate::Auth::Credential.new(
135
+ auth_type: :service_account,
136
+ service_account_key: File.read('aws-credentials.json')
137
+ )
138
+ ```
139
+
140
+ ### Azure Service Account
141
+
142
+ ```ruby
143
+ # Azure service principal
144
+ scheme = Legate::Auth::Schemes::ServiceAccount.new(
145
+ token_url: 'https://login.microsoftonline.com/{tenant-id}/oauth2/v2.0/token',
146
+ audience: 'https://management.azure.com/'
147
+ )
148
+
149
+ credential = Legate::Auth::Credential.new(
150
+ auth_type: :service_account,
151
+ service_account_key: ENV['AZURE_SERVICE_ACCOUNT_JSON'] # raw JSON string
152
+ )
153
+ ```
154
+
155
+ > The base `ServiceAccount` scheme reads its key material from the credential's
156
+ > `service_account_key` (raw JSON string) or `service_account_key_file` (path).
157
+ > Provider-specific client_id/secret/tenant fields are not read directly by the
158
+ > base scheme.
159
+
160
+ ## Token Management
161
+
162
+ When used with a `TokenManager`, service account tokens are managed for you:
163
+
164
+ - **Token Acquisition**: The JWT assertion is exchanged for an access token
165
+ - **Token Storage**: The access token is cached in scoped session state (plaintext; apply `Legate::Auth::Encryption` yourself for at-rest encryption)
166
+ - **Token Refresh**: Service account schemes support refresh — an expired token triggers a new exchange (`supports_refresh?` is `true`)
167
+
168
+ ## Security Best Practices
169
+
170
+ 1. **Secure Key Storage**:
171
+ - Never commit service account keys to source control
172
+ - Use environment variables or secret management services
173
+ - Restrict file permissions on key files
174
+
175
+ 2. **Principle of Least Privilege**:
176
+ - Create service accounts with minimal required permissions
177
+ - Request only necessary scopes
178
+ - Use different service accounts for different purposes
179
+
180
+ 3. **Key Rotation**:
181
+ - Regularly rotate service account keys
182
+ - Monitor key usage for suspicious activity
183
+
184
+ 4. **Secure Transport**:
185
+ - Always use HTTPS for token exchange (token URLs are SSRF-checked by `Legate::Auth::UrlGuard`)
186
+ - HTTPS encrypts tokens in transit; for at-rest encryption use the opt-in `Legate::Auth::Encryption` module
187
+
188
+ ## Creating Service Account Keys
189
+
190
+ ### Google Cloud
191
+
192
+ 1. Go to the [Google Cloud Console](https://console.cloud.google.com/)
193
+ 2. Navigate to IAM & Admin > Service Accounts
194
+ 3. Select or create a service account
195
+ 4. Click "Keys" > "Add Key" > "Create new key"
196
+ 5. Choose JSON format and click "Create"
197
+ 6. Save the downloaded key file securely
198
+
199
+ ### AWS
200
+
201
+ 1. Go to the [AWS Management Console](https://console.aws.amazon.com/)
202
+ 2. Navigate to IAM > Users
203
+ 3. Create a new user or select an existing one
204
+ 4. Click "Security credentials" > "Create access key"
205
+ 5. Save the Access Key ID and Secret Access Key securely
206
+
207
+ ### Azure
208
+
209
+ 1. Go to the [Azure Portal](https://portal.azure.com/)
210
+ 2. Navigate to Azure Active Directory > App registrations
211
+ 3. Create a new registration or select an existing one
212
+ 4. Go to "Certificates & secrets" > "New client secret"
213
+ 5. Create a secret and save the value securely
214
+
215
+ ## Troubleshooting
216
+
217
+ If you encounter issues with service account authentication:
218
+
219
+ - Verify that the service account key is valid and correctly formatted
220
+ - Check that the service account has the necessary permissions
221
+ - Ensure the scopes requested are allowed for the service account
222
+ - Verify that the audience value matches what the provider expects
223
+ - Check that the token URL is correct for your provider
224
+
225
+ ## Related Topics
226
+ - [Token Lifecycle Management](./token_lifecycle) - Advanced token management techniques
227
+ - [Secure Credential Storage](./secure_storage) - Best practices for credential security
228
+ - [OAuth2 Authentication](./oauth2) - Learn about OAuth2 authentication for user-based flows
@@ -0,0 +1,295 @@
1
+ # Token Lifecycle Management
2
+
3
+ Authentication tokens have a lifecycle that includes acquisition, refresh, and eventual expiration or revocation. The Legate Ruby library provides a comprehensive token management system to handle these aspects automatically.
4
+
5
+ ## Overview
6
+
7
+ The token lifecycle includes several key phases:
8
+
9
+ 1. **Token Acquisition**: The initial exchange of credentials for a token
10
+ 2. **Token Storage**: Secure storage of the token for subsequent requests
11
+ 3. **Token Usage**: Using the token to authenticate API requests
12
+ 4. **Token Refresh**: Renewing the token before it expires
13
+ 5. **Token Expiration**: Handling token expiration gracefully
14
+ 6. **Token Revocation**: Explicitly revoking tokens when no longer needed
15
+
16
+ ## Token Manager
17
+
18
+ The `Legate::Auth::TokenManager` class handles all aspects of token lifecycle management:
19
+
20
+ ```ruby
21
+ # Create a token manager with a token store
22
+ token_store = Legate::Auth::TokenStore.new(session)
23
+ token_manager = Legate::Auth::TokenManager.new(token_store)
24
+
25
+ # Get a token (automatically handles acquisition and refresh)
26
+ token = token_manager.get_token(scheme, credential)
27
+
28
+ # Force refresh a token
29
+ refreshed_token = token_manager.get_token(scheme, credential, force_refresh: true)
30
+
31
+ # Invalidate a token in the store
32
+ token_manager.invalidate_token(cache_key)
33
+
34
+ # Revoke a token with the provider
35
+ token_manager.revoke_token(scheme, credential, token)
36
+ ```
37
+
38
+ ## Token Acquisition
39
+
40
+ Tokens are acquired through different mechanisms depending on the authentication scheme:
41
+
42
+ ### OAuth2/OIDC
43
+
44
+ ```ruby
45
+ # OAuth2 token acquisition via authorization code
46
+ scheme = Legate::Auth::Schemes::OAuth2.new(
47
+ authorization_url: 'https://auth.example.com/authorize',
48
+ token_url: 'https://auth.example.com/token',
49
+ scopes: ['profile', 'email']
50
+ )
51
+
52
+ credential = Legate::Auth::Credential.new(
53
+ auth_type: :oauth2,
54
+ client_id: ENV['CLIENT_ID'],
55
+ client_secret: ENV['CLIENT_SECRET']
56
+ )
57
+
58
+ # The token is acquired through the Fiber yield/resume mechanism
59
+ ```
60
+
61
+ ### Service Account
62
+
63
+ ```ruby
64
+ # Service account token acquisition
65
+ scheme = Legate::Auth::Schemes::ServiceAccount.new(
66
+ token_url: 'https://oauth2.googleapis.com/token',
67
+ audience: 'https://oauth2.googleapis.com/token',
68
+ scopes: ['https://www.googleapis.com/auth/cloud-platform']
69
+ )
70
+
71
+ credential = Legate::Auth::Credential.new(
72
+ auth_type: :service_account,
73
+ service_account_key: File.read('service-account-key.json') # raw JSON string
74
+ )
75
+
76
+ # The token is acquired automatically when needed
77
+ token = token_manager.get_token(scheme, credential)
78
+ ```
79
+
80
+ ### API Key
81
+
82
+ ```ruby
83
+ # API key "token" creation
84
+ scheme = Legate::Auth::Schemes::ApiKey.new
85
+
86
+ credential = Legate::Auth::Credential.new(
87
+ auth_type: :api_key,
88
+ api_key: ENV['API_KEY']
89
+ )
90
+
91
+ # Creates a wrapper token that never expires
92
+ token = token_manager.get_token(scheme, credential)
93
+ ```
94
+
95
+ ## Token Storage
96
+
97
+ Tokens are cached in scoped session state:
98
+
99
+ ```ruby
100
+ # The TokenStore caches tokens under the 'auth' scope
101
+ token_store = Legate::Auth::TokenStore.new(session_service)
102
+
103
+ # Store a token
104
+ token_store.store(cache_key, token)
105
+
106
+ # Retrieve a token (returns nil if missing or expired)
107
+ token = token_store.get(cache_key)
108
+
109
+ # Clear a token
110
+ token_store.clear(cache_key)
111
+ ```
112
+
113
+ ### Security Considerations
114
+
115
+ - Tokens are stored as plaintext (`token.to_h`) in scoped state; `TokenStore` does **not** encrypt them
116
+ - For at-rest encryption, apply the opt-in `Legate::Auth::Encryption` module yourself (requires the `rbnacl` gem and a Base64 key in `LEGATE_AUTH_ENCRYPTION_KEY`)
117
+ - Sensitive token information should never be logged
118
+
119
+ ## Token Refresh
120
+
121
+ Most tokens have an expiration time and need to be refreshed:
122
+
123
+ ```ruby
124
+ # Configure token refresh settings
125
+ token_manager = Legate::Auth::TokenManager.new(token_store, {
126
+ refresh_buffer: 60, # Refresh 60 seconds before expiration
127
+ retry_max_attempts: 3, # Try up to 3 times
128
+ retry_delay: 2, # Wait 2 seconds between attempts
129
+ retry_backoff: 1.5, # Increase wait time by 1.5x each try
130
+ auto_refresh: true # Enable automatic refresh
131
+ })
132
+
133
+ # Token refresh is automatic when get_token is called
134
+ token = token_manager.get_token(scheme, credential)
135
+ ```
136
+
137
+ ### Refresh Behavior by Scheme Type
138
+
139
+ - **OAuth2/OIDC**: Uses the refresh token to obtain a new access token
140
+ - **Service Account**: Creates a new JWT assertion and exchanges it for a new token
141
+ - **API Key**: No refresh needed (API keys don't expire)
142
+ - **Bearer Token**: Can't be refreshed (must re-authenticate)
143
+
144
+ ## Token Expiration
145
+
146
+ The Legate Ruby library handles token expiration gracefully:
147
+
148
+ ```ruby
149
+ # Check if a token is expired
150
+ if token.expired?
151
+ # Handle expiration
152
+ end
153
+
154
+ # Check if a token is expired with a buffer time (positional argument)
155
+ if token.expired?(60)
156
+ # Token expires within the next 60 seconds
157
+ end
158
+
159
+ # ExchangedCredential includes expiration information
160
+ token = Legate::Auth::ExchangedCredential.new(
161
+ auth_type: :oauth2,
162
+ access_token: 'access-token',
163
+ refresh_token: 'refresh-token',
164
+ expires_in: 3600 # Token expires in 3600 seconds
165
+ )
166
+
167
+ # Get expiration time
168
+ expiry = token.expires_at # Time object representing expiration
169
+ ```
170
+
171
+ ## Token Revocation
172
+
173
+ When a token is no longer needed, it can be explicitly revoked:
174
+
175
+ ```ruby
176
+ # Revoke a token with the provider
177
+ token_manager.revoke_token(scheme, credential, token)
178
+
179
+ # Just invalidate it in the store without provider revocation
180
+ token_manager.invalidate_token(cache_key)
181
+ ```
182
+
183
+ ### Revocation Support by Scheme Type
184
+
185
+ - **OAuth2/OIDC**: Supported if the provider has a revocation endpoint
186
+ - **Service Account**: Generally not supported by providers
187
+ - **API Key**: Not applicable (API keys must be deleted from the provider's management interface)
188
+
189
+ ## Event Callbacks
190
+
191
+ The token manager supports callbacks for token lifecycle events:
192
+
193
+ Each callback receives a single data Hash (keys include `:event`, `:token`,
194
+ `:scheme`, `:credential`, plus `:error` for `:refresh_failure` and `:cache_key`
195
+ for `:invalidated`).
196
+
197
+ ```ruby
198
+ # Register a callback for token refresh
199
+ token_manager.on(:refresh_success) do |data|
200
+ # data[:token] is the new token
201
+ end
202
+
203
+ # Register a callback for token refresh failure
204
+ token_manager.on(:refresh_failure) do |data|
205
+ # data[:error] is the failure
206
+ end
207
+
208
+ # Register a callback for approaching expiration
209
+ token_manager.on(:before_expiry) do |data|
210
+ # data[:token] is approaching expiry
211
+ end
212
+
213
+ # Register a callback for token invalidation
214
+ token_manager.on(:invalidated) do |data|
215
+ # data[:cache_key] is the invalidated key
216
+ end
217
+ ```
218
+
219
+ ## Advanced Configuration
220
+
221
+ ### Custom Token Store
222
+
223
+ ```ruby
224
+ # Create a custom token store
225
+ class CustomTokenStore
226
+ def store(key, token)
227
+ # Store the token
228
+ end
229
+
230
+ def get(key)
231
+ # Retrieve the token
232
+ end
233
+
234
+ def clear(key)
235
+ # Clear the token
236
+ end
237
+ end
238
+
239
+ # Use the custom token store
240
+ token_store = CustomTokenStore.new
241
+ token_manager = Legate::Auth::TokenManager.new(token_store)
242
+ ```
243
+
244
+ ### Background Token Refresh
245
+
246
+ ```ruby
247
+ # Enable background token refresh
248
+ token_manager = Legate::Auth::TokenManager.new(token_store, {
249
+ background_refresh: true
250
+ })
251
+
252
+ # This will refresh tokens in a background thread
253
+ ```
254
+
255
+ ## Best Practices
256
+
257
+ 1. **Minimize Token Requests**: Cache tokens until they're close to expiration
258
+ 2. **Handle Refresh Failures**: Implement retry logic with backoff
259
+ 3. **Secure Storage**: Tokens are not encrypted by `TokenStore`; apply the opt-in `Legate::Auth::Encryption` module if you need at-rest encryption
260
+ 4. **Revoke Unused Tokens**: Explicitly revoke tokens when they're no longer needed
261
+ 5. **Monitor Token Usage**: Track token usage for security auditing
262
+
263
+ ## Troubleshooting
264
+
265
+ ### Token Refresh Failures
266
+
267
+ If token refresh fails, check:
268
+ - The refresh token hasn't expired or been revoked
269
+ - The token endpoint is accessible
270
+ - The client credentials are still valid
271
+ - Network connectivity to the provider
272
+
273
+ ### Token Storage Issues
274
+
275
+ If tokens aren't being properly stored:
276
+ - Verify the session service is properly initialized and passed to `TokenStore.new`
277
+ - Ensure session persistence between requests
278
+ - If you added opt-in `Legate::Auth::Encryption`, verify the key is consistent across instances
279
+
280
+ ### Performance Considerations
281
+
282
+ - Token refresh can add latency to requests
283
+ - Use the refresh buffer to refresh tokens before they expire
284
+ - Consider background refresh for critical applications
285
+
286
+ ## Next Steps
287
+
288
+ - [OAuth2 Authentication](./oauth2) - Learn more about OAuth2 authentication flows
289
+ - [Service Account Authentication](./service_account) - Use service accounts for server-to-server authentication
290
+ - [Secure Credential Storage](./secure_storage) - Best practices for credential security
291
+
292
+ ## Related Topics
293
+ - [OAuth2 Authentication](./oauth2) - Learn more about OAuth2 authentication flows
294
+ - [Service Account Authentication](./service_account) - Use service accounts for server-to-server authentication
295
+ - [Secure Credential Storage](./secure_storage) - Best practices for credential security