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,311 @@
1
+ # Legate::Auth::Schemes::OpenIDConnect
2
+
3
+ The `OpenIDConnect` class implements the OpenID Connect (OIDC) authentication scheme, which provides a layer of identity verification on top of OAuth 2.0 protocols. It extends the `OAuth2` scheme class. It is also available via the `OIDC` alias.
4
+
5
+ ## Overview
6
+
7
+ OpenID Connect is a simple identity layer built on top of the OAuth 2.0 protocol. It allows clients to verify the identity of end-users based on the authentication performed by an authorization server, as well as to obtain basic profile information about the end-user in an interoperable and REST-like manner.
8
+
9
+ ## Class Methods
10
+
11
+ ### `new`
12
+
13
+ Creates a new OpenID Connect authentication scheme.
14
+
15
+ **Parameters:**
16
+ - Inherits all parameters from `OAuth2.new` (authorization_url, token_url, scopes, use_pkce, etc.)
17
+ - `discovery_url` (String, optional keyword): The OIDC discovery endpoint URL
18
+ - `jwks_url` (String, optional keyword): The JSON Web Key Set URI for token validation
19
+ - `userinfo_url` (String, optional keyword): The userinfo endpoint URL
20
+ - `issuer` (String, optional keyword): The OIDC issuer URL
21
+ - `provider_uri` (String, optional keyword): The provider URI
22
+ - `client_id` (String, optional keyword): The client ID for the OIDC provider
23
+ - `**kwargs` (Hash): Additional parameters for the authentication scheme
24
+
25
+ **Examples:**
26
+
27
+ ```ruby
28
+ # Create a basic OIDC scheme with discovery
29
+ scheme = Legate::Auth::Schemes::OpenIDConnect.new(
30
+ discovery_url: 'https://accounts.google.com/.well-known/openid-configuration',
31
+ scopes: ['openid', 'email', 'profile']
32
+ )
33
+
34
+ # With explicit endpoint configuration
35
+ scheme = Legate::Auth::Schemes::OpenIDConnect.new(
36
+ authorization_url: 'https://login.microsoftonline.com/tenant-id/oauth2/v2.0/authorize',
37
+ token_url: 'https://login.microsoftonline.com/tenant-id/oauth2/v2.0/token',
38
+ userinfo_url: 'https://graph.microsoft.com/oidc/userinfo',
39
+ jwks_url: 'https://login.microsoftonline.com/tenant-id/discovery/v2.0/keys',
40
+ scopes: ['openid', 'email', 'profile', 'offline_access']
41
+ )
42
+ ```
43
+
44
+ ## Instance Methods
45
+
46
+ ### `scheme_type`
47
+
48
+ Returns the type of the authentication scheme.
49
+
50
+ **Returns:**
51
+ - Symbol: `:openid_connect`
52
+
53
+ **Examples:**
54
+
55
+ ```ruby
56
+ scheme = Legate::Auth::Schemes::OpenIDConnect.new
57
+ scheme.scheme_type # => :openid_connect
58
+ ```
59
+
60
+ ### `validate!`
61
+
62
+ Validates the scheme configuration.
63
+
64
+ **Raises:**
65
+ - `Legate::Auth::SchemeValidationError`: If the scheme configuration is invalid (e.g. missing `authorization_url` or `token_url`)
66
+
67
+ **Examples:**
68
+
69
+ ```ruby
70
+ scheme = Legate::Auth::Schemes::OpenIDConnect.new(
71
+ discovery_url: 'https://accounts.google.com/.well-known/openid-configuration',
72
+ token_url: 'https://oauth2.googleapis.com/token'
73
+ )
74
+ scheme.validate!
75
+ ```
76
+
77
+ ### `build_authorization_uri`
78
+
79
+ Builds the authorization URL for the OpenID Connect flow. Includes OIDC-specific parameters like nonce.
80
+
81
+ **Parameters:**
82
+ - `config` (Legate::Auth::Config): The authentication configuration
83
+ - `redirect_uri` (String, optional): The redirect URI for the callback
84
+ - `state` (String, optional): The state parameter for CSRF protection
85
+
86
+ **Returns:**
87
+ - String: The authorization URL for the OIDC flow
88
+
89
+ **Examples:**
90
+
91
+ ```ruby
92
+ config = Legate::Auth::Config.new(scheme: scheme, credential: credential)
93
+ auth_url = scheme.build_authorization_uri(
94
+ config,
95
+ 'http://localhost:3000/auth/callback',
96
+ SecureRandom.hex(16)
97
+ )
98
+ ```
99
+
100
+ ### `apply_to_request`
101
+
102
+ Applies OIDC authentication to a request (inherited from OAuth2).
103
+
104
+ **Parameters:**
105
+ - `request` (Hash): The request to authenticate
106
+ - `credential` (Legate::Auth::ExchangedCredential): The exchanged credential
107
+
108
+ **Returns:**
109
+ - Hash: The authenticated request with access token in the Authorization header
110
+
111
+ ### `exchange_token`
112
+
113
+ Exchanges an authorization code for OIDC tokens (access token, ID token, and optional refresh token).
114
+
115
+ **Parameters:**
116
+ - `config` (Legate::Auth::Config): The authentication configuration
117
+ - `credential` (Legate::Auth::Credential): The credential containing client information
118
+
119
+ **Returns:**
120
+ - Legate::Auth::ExchangedCredential: The exchanged tokens
121
+
122
+ **Examples:**
123
+
124
+ ```ruby
125
+ token = scheme.exchange_token(config, credential)
126
+
127
+ puts token[:access_token] # => "access-token"
128
+ puts token[:id_token] # => "id-token-jwt"
129
+ puts token[:refresh_token] # => "refresh-token" (if granted)
130
+ ```
131
+
132
+ ### `discover_endpoints`
133
+
134
+ Discovers OIDC endpoints from the discovery URL.
135
+
136
+ **Returns:**
137
+ - Hash: The discovered endpoint configuration
138
+
139
+ ### `get_userinfo`
140
+
141
+ Retrieves user information from the OIDC userinfo endpoint.
142
+
143
+ **Parameters:**
144
+ - `access_token` (String): The access token to use for the userinfo request
145
+
146
+ **Returns:**
147
+ - Hash: The user information retrieved from the userinfo endpoint
148
+
149
+ **Examples:**
150
+
151
+ ```ruby
152
+ user_info = scheme.get_userinfo(token[:access_token])
153
+
154
+ puts "User ID: #{user_info['sub']}"
155
+ puts "Email: #{user_info['email']}"
156
+ puts "Name: #{user_info['name']}"
157
+ ```
158
+
159
+ ### `verify_id_token`
160
+
161
+ Verifies the ID token from the OIDC provider.
162
+
163
+ **Parameters:**
164
+ - `id_token` (String): The ID token to verify
165
+ - `nonce` (String, optional): The nonce used in the authorization request
166
+ - `audience` (String, optional): The expected audience for the token
167
+
168
+ **Returns:**
169
+ - Hash: The decoded and verified ID token claims
170
+
171
+ **Raises:**
172
+ - `Legate::Auth::TokenVerificationError`: If the ID token is invalid (signature, claim, or nonce mismatch)
173
+
174
+ **Examples:**
175
+
176
+ ```ruby
177
+ scheme = Legate::Auth::Schemes::OpenIDConnect.new(
178
+ discovery_url: 'https://accounts.google.com/.well-known/openid-configuration',
179
+ client_id: 'my-client-id'
180
+ )
181
+
182
+ begin
183
+ claims = scheme.verify_id_token(id_token, 'original-nonce-value')
184
+ puts "Authenticated user: #{claims['sub']}"
185
+ puts "Email: #{claims['email']}"
186
+ rescue Legate::Auth::TokenVerificationError => e
187
+ puts "ID token verification failed: #{e.message}"
188
+ end
189
+ ```
190
+
191
+ ### `to_h`
192
+
193
+ Converts the scheme to a hash representation.
194
+
195
+ **Returns:**
196
+ - Hash: A hash representation of the scheme configuration
197
+
198
+ ## Usage Examples
199
+
200
+ ### Complete OIDC Authorization Flow
201
+
202
+ ```ruby
203
+ require 'securerandom'
204
+
205
+ # Step 1: Create the OIDC scheme
206
+ scheme = Legate::Auth::Schemes::OpenIDConnect.new(
207
+ discovery_url: 'https://accounts.google.com/.well-known/openid-configuration',
208
+ scopes: ['openid', 'email', 'profile']
209
+ )
210
+
211
+ # Step 2: Set up credential with client details
212
+ credential = Legate::Auth::Credential.new(
213
+ auth_type: :oidc,
214
+ client_id: ENV['OIDC_CLIENT_ID'],
215
+ client_secret: ENV['OIDC_CLIENT_SECRET']
216
+ )
217
+
218
+ # Step 3: Create config and build authorization URI
219
+ config = Legate::Auth::Config.new(scheme: scheme, credential: credential)
220
+ state = SecureRandom.hex(16)
221
+ auth_url = config.build_authorization_uri(
222
+ 'http://localhost:3000/auth/callback',
223
+ state
224
+ )
225
+
226
+ # Step 4: Redirect user to auth_url
227
+ # redirect_to auth_url
228
+
229
+ # Step 5: Handle the callback
230
+ config.response_uri = "http://localhost:3000/auth/callback?code=12345&state=#{state}"
231
+ token = scheme.exchange_token(config, credential)
232
+
233
+ # Step 6: Verify ID token
234
+ claims = scheme.verify_id_token(token[:id_token])
235
+
236
+ # Step 7: Get user info
237
+ user_info = scheme.get_userinfo(token[:access_token])
238
+
239
+ # Step 8: Create user session
240
+ session[:user_id] = claims['sub']
241
+ session[:user_email] = claims['email']
242
+ ```
243
+
244
+ ### Using with Token Manager
245
+
246
+ ```ruby
247
+ scheme = Legate::Auth::Schemes::OpenIDConnect.new(
248
+ discovery_url: 'https://accounts.google.com/.well-known/openid-configuration'
249
+ )
250
+
251
+ credential = Legate::Auth::Credential.new(
252
+ auth_type: :oidc,
253
+ client_id: 'ENV:OIDC_CLIENT_ID',
254
+ client_secret: 'ENV:OIDC_CLIENT_SECRET'
255
+ )
256
+
257
+ token_store = Legate::Auth::TokenStore.new(session_service)
258
+ token_manager = Legate::Auth::TokenManager.new(token_store)
259
+
260
+ # Get a token (will auto-refresh if expired)
261
+ token = token_manager.get_token(scheme, credential)
262
+ ```
263
+
264
+ ## Provider-Specific Configurations
265
+
266
+ ### Google
267
+
268
+ ```ruby
269
+ scheme = Legate::Auth::Schemes::OpenIDConnect.new(
270
+ discovery_url: 'https://accounts.google.com/.well-known/openid-configuration',
271
+ scopes: ['openid', 'email', 'profile']
272
+ )
273
+ ```
274
+
275
+ ### Microsoft Azure AD / Entra ID
276
+
277
+ ```ruby
278
+ scheme = Legate::Auth::Schemes::OpenIDConnect.new(
279
+ authorization_url: 'https://login.microsoftonline.com/tenant-id/oauth2/v2.0/authorize',
280
+ token_url: 'https://login.microsoftonline.com/tenant-id/oauth2/v2.0/token',
281
+ userinfo_url: 'https://graph.microsoft.com/oidc/userinfo',
282
+ scopes: ['openid', 'email', 'profile', 'offline_access']
283
+ )
284
+ ```
285
+
286
+ ### Auth0
287
+
288
+ ```ruby
289
+ scheme = Legate::Auth::Schemes::OpenIDConnect.new(
290
+ discovery_url: 'https://your-tenant.auth0.com/.well-known/openid-configuration',
291
+ scopes: ['openid', 'email', 'profile', 'offline_access']
292
+ )
293
+ ```
294
+
295
+ ## Security Considerations
296
+
297
+ - Store tokens securely; for at-rest encryption use the opt-in [`Legate::Auth::Encryption`](../encryption) module (TokenStore does not encrypt)
298
+ - Always validate the ID token's signature and claims
299
+ - Use state parameters to prevent CSRF attacks
300
+ - Use nonce parameters to prevent replay attacks
301
+ - Implement proper token lifecycle management, including expiration
302
+ - Consider using PKCE for additional security
303
+ - Always use HTTPS for all OIDC-related communications
304
+
305
+ ## See Also
306
+
307
+ - [Legate::Auth::Schemes::OAuth2](./oauth2)
308
+ - [Legate::Auth::Credential](../credential)
309
+ - [Legate::Auth::ExchangedCredential](../exchanged_credential)
310
+ - [Legate::Auth::Scheme](../scheme)
311
+ - [Legate::Auth::TokenManager](../token_manager)
@@ -0,0 +1,287 @@
1
+ # Legate::Auth::Schemes::ServiceAccount
2
+
3
+ The `ServiceAccount` class implements service account authentication, which allows applications to authenticate with APIs using key-based credentials rather than user credentials. This authentication scheme is designed for server-to-server and automated workflows where no user interaction is required.
4
+
5
+ ## Overview
6
+
7
+ Service account authentication uses cryptographic key pairs (usually RSA) to sign authentication tokens that are then exchanged for access tokens. The service account scheme in Legate Ruby provides a flexible foundation for implementing various service account authentication methods, with provider-specific implementations available for common cloud services.
8
+
9
+ ## Class Methods
10
+
11
+ ### `new`
12
+
13
+ Creates a new service account authentication scheme.
14
+
15
+ **Parameters:**
16
+ - `token_url` (String, optional keyword): The token endpoint URL where the service account credentials are exchanged for tokens
17
+ - `audience` (String, optional keyword): The target audience for the service account tokens
18
+ - `scopes` (Array<String>, optional keyword): The scopes to request for the service account
19
+ - `token_lifetime` (Integer, optional keyword): The lifetime of the token in seconds (default: 3600)
20
+ - `client_email` (String, optional keyword): The service account client email
21
+ - `private_key` (String, optional keyword): The private key for signing JWTs
22
+ - `private_key_id` (String, optional keyword): The private key ID
23
+ - `config` (Hash, optional keyword): Additional configuration (default: {})
24
+
25
+ **Examples:**
26
+
27
+ ```ruby
28
+ # Create a basic service account scheme
29
+ scheme = Legate::Auth::Schemes::ServiceAccount.new(
30
+ token_url: 'https://provider.com/oauth2/token',
31
+ audience: 'https://api.example.com',
32
+ scopes: ['https://api.example.com/auth/read', 'https://api.example.com/auth/write']
33
+ )
34
+
35
+ # With custom token lifetime
36
+ scheme = Legate::Auth::Schemes::ServiceAccount.new(
37
+ token_url: 'https://provider.com/oauth2/token',
38
+ audience: 'https://api.example.com',
39
+ token_lifetime: 1800 # 30 minutes
40
+ )
41
+ ```
42
+
43
+ ## Instance Methods
44
+
45
+ ### `scheme_type`
46
+
47
+ Returns the type of the authentication scheme.
48
+
49
+ **Returns:**
50
+ - Symbol: `:service_account`
51
+
52
+ **Examples:**
53
+
54
+ ```ruby
55
+ scheme = Legate::Auth::Schemes::ServiceAccount.new
56
+ scheme.scheme_type # => :service_account
57
+ ```
58
+
59
+ ### `validate!`
60
+
61
+ Validates the scheme configuration.
62
+
63
+ **Raises:**
64
+ - `Legate::Auth::SchemeValidationError`: If the scheme configuration is invalid
65
+
66
+ **Examples:**
67
+
68
+ ```ruby
69
+ scheme = Legate::Auth::Schemes::ServiceAccount.new(
70
+ token_url: 'https://provider.com/oauth2/token'
71
+ )
72
+ scheme.validate!
73
+ ```
74
+
75
+ ### `apply_to_request`
76
+
77
+ Applies service account authentication to a request by adding the access token to the Authorization header.
78
+
79
+ **Parameters:**
80
+ - `request` (Hash): The request to authenticate
81
+ - `credential` (Legate::Auth::ExchangedCredential): The exchanged credential containing the access token
82
+
83
+ **Returns:**
84
+ - Hash: The authenticated request with access token in the Authorization header
85
+
86
+ **Examples:**
87
+
88
+ ```ruby
89
+ request = { headers: {} }
90
+ authenticated = scheme.apply_to_request(request, exchanged_credential)
91
+ puts authenticated[:headers]['Authorization'] # => "Bearer [access-token]"
92
+ ```
93
+
94
+ ### `fetch_token`
95
+
96
+ Fetches a token from the token endpoint using a signed JWT.
97
+
98
+ **Parameters:**
99
+ - `credential` (Legate::Auth::Credential): The credential containing the service account information
100
+
101
+ **Returns:**
102
+ - Legate::Auth::ExchangedCredential: The fetched token
103
+
104
+ ### `exchange_token`
105
+
106
+ Exchanges a service account credential for an access token by creating and signing a JWT and exchanging it with the token endpoint.
107
+
108
+ **Parameters:**
109
+ - `credential` (Legate::Auth::Credential): The credential containing the service account information
110
+
111
+ **Returns:**
112
+ - Legate::Auth::ExchangedCredential: The exchanged token
113
+
114
+ **Examples:**
115
+
116
+ ```ruby
117
+ # Create a service account credential
118
+ credential = Legate::Auth::Credential.new(
119
+ auth_type: :service_account,
120
+ service_account_key: File.read('service-account-key.json') # raw JSON string, not a parsed Hash
121
+ )
122
+
123
+ # Exchange for a token
124
+ scheme = Legate::Auth::Schemes::ServiceAccount.new(
125
+ token_url: 'https://provider.com/oauth2/token',
126
+ audience: 'https://api.example.com',
127
+ scopes: ['read', 'write']
128
+ )
129
+ token = scheme.exchange_token(credential)
130
+
131
+ puts token[:access_token] # => "[service-account-access-token]"
132
+ ```
133
+
134
+ ### `supports_refresh?`
135
+
136
+ Returns `true` -- service account schemes always support token refresh by re-exchanging credentials.
137
+
138
+ **Returns:**
139
+ - Boolean: `true`
140
+
141
+ ### `refresh_token`
142
+
143
+ Refreshes an expired service account token by performing a new token exchange.
144
+
145
+ **Parameters:**
146
+ - `token` (Legate::Auth::ExchangedCredential): The token to refresh
147
+ - `credential` (Legate::Auth::Credential): The original credential
148
+
149
+ **Returns:**
150
+ - Legate::Auth::ExchangedCredential: The refreshed token
151
+
152
+ **Examples:**
153
+
154
+ ```ruby
155
+ refreshed_token = scheme.refresh_token(expired_token, credential)
156
+ ```
157
+
158
+ ### `create_signed_jwt`
159
+
160
+ Creates a signed JWT assertion for the service account.
161
+
162
+ **Parameters:**
163
+ - `service_account_key` (Hash, optional): The service account key to use for signing (default: nil, uses configured key)
164
+
165
+ **Returns:**
166
+ - String: The signed JWT assertion
167
+
168
+ **Examples:**
169
+
170
+ ```ruby
171
+ jwt = scheme.create_signed_jwt
172
+ puts jwt # => "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
173
+ ```
174
+
175
+ ### `to_h`
176
+
177
+ Converts the scheme to a hash representation.
178
+
179
+ **Returns:**
180
+ - Hash: A hash representation of the scheme configuration
181
+
182
+ ## Usage Examples
183
+
184
+ ### Basic Authentication Flow
185
+
186
+ ```ruby
187
+ # Create a Service Account scheme
188
+ scheme = Legate::Auth::Schemes::ServiceAccount.new(
189
+ token_url: 'https://provider.com/oauth2/token',
190
+ audience: 'https://api.example.com',
191
+ scopes: ['read', 'write']
192
+ )
193
+
194
+ # Create a credential from a service account key file
195
+ credential = Legate::Auth::Credential.new(
196
+ auth_type: :service_account,
197
+ service_account_key: File.read('service-account-key.json') # raw JSON string, not a parsed Hash
198
+ )
199
+
200
+ # Exchange the credential for a token
201
+ token = scheme.exchange_token(credential)
202
+
203
+ # Apply to a request
204
+ request = { headers: {} }
205
+ authenticated = scheme.apply_to_request(request, token)
206
+ puts authenticated[:headers]['Authorization'] # => "Bearer [access-token]"
207
+ ```
208
+
209
+ ### With Token Manager
210
+
211
+ ```ruby
212
+ # Create a Service Account scheme
213
+ scheme = Legate::Auth::Schemes::ServiceAccount.new(
214
+ token_url: 'https://provider.com/oauth2/token',
215
+ audience: 'https://api.example.com'
216
+ )
217
+
218
+ # Create a credential from a service account key file
219
+ credential = Legate::Auth::Credential.new(
220
+ auth_type: :service_account,
221
+ service_account_key: File.read('service-account-key.json') # raw JSON string, not a parsed Hash
222
+ )
223
+
224
+ # Use with token manager for automatic token management
225
+ token_store = Legate::Auth::TokenStore.new(session_service)
226
+ token_manager = Legate::Auth::TokenManager.new(token_store)
227
+
228
+ # Get a token (this will create, store, and refresh the token as needed)
229
+ token = token_manager.get_token(scheme, credential)
230
+ ```
231
+
232
+ ### Using Key File from Path
233
+
234
+ ```ruby
235
+ # Create a credential using a key file path
236
+ credential = Legate::Auth::Credential.new(
237
+ auth_type: :service_account,
238
+ service_account_key_file: 'path/to/service-account-key.json'
239
+ )
240
+
241
+ # Create the scheme and use as normal
242
+ scheme = Legate::Auth::Schemes::ServiceAccount.new(
243
+ token_url: 'https://provider.com/oauth2/token',
244
+ audience: 'https://api.example.com'
245
+ )
246
+
247
+ # Exchange for a token
248
+ token = scheme.exchange_token(credential)
249
+ ```
250
+
251
+ ## Service Account Key Format
252
+
253
+ A standard service account key JSON file typically contains:
254
+
255
+ ```json
256
+ {
257
+ "type": "service_account",
258
+ "project_id": "your-project-id",
259
+ "private_key_id": "abcdef1234567890",
260
+ "private_key": "-----BEGIN PRIVATE KEY-----\nMIIE...\n-----END PRIVATE KEY-----\n",
261
+ "client_email": "service-account@project-id.iam.gserviceaccount.com",
262
+ "client_id": "123456789012345678901",
263
+ "auth_uri": "https://accounts.google.com/o/oauth2/auth",
264
+ "token_uri": "https://oauth2.googleapis.com/token"
265
+ }
266
+ ```
267
+
268
+ The exact format may vary by service provider.
269
+
270
+ ## Security Considerations
271
+
272
+ - Store service account keys securely and restrict access
273
+ - Grant service accounts the minimum necessary permissions (principle of least privilege)
274
+ - Regularly rotate service account keys
275
+ - Monitor service account usage for suspicious activity
276
+ - Avoid embedding service account keys in client-side code
277
+ - Use environment variables or secure credential stores to manage service account keys
278
+ - Always use HTTPS for transmitting service account tokens
279
+ - Consider using shorter token lifetimes in high-security environments
280
+
281
+ ## See Also
282
+
283
+ - [Legate::Auth::Schemes::GoogleServiceAccount](./google_service_account)
284
+ - [Legate::Auth::Credential](../credential)
285
+ - [Legate::Auth::ExchangedCredential](../exchanged_credential)
286
+ - [Legate::Auth::Scheme](../scheme)
287
+ - [Legate::Auth::TokenManager](../token_manager)