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.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +345 -0
- data/bin/legate +13 -0
- data/examples/00_quickstart.rb +51 -0
- data/examples/01_simple_agent.rb +105 -0
- data/examples/02_multi_tool_agent.rb +140 -0
- data/examples/03_custom_tool.rb +93 -0
- data/examples/04_agent_instructions.rb +84 -0
- data/examples/05_state_and_sessions.rb +91 -0
- data/examples/06_callbacks.rb +186 -0
- data/examples/07_async_jobs.rb +112 -0
- data/examples/08_loop_agent.rb +197 -0
- data/examples/09_sequential_workflow.rb +40 -0
- data/examples/10_parallel_workflow.rb +34 -0
- data/examples/11_agent_delegation.rb +24 -0
- data/examples/12_http_client_tool.rb +156 -0
- data/examples/13_authentication.rb +220 -0
- data/examples/14_mcp_client.rb +154 -0
- data/examples/15_mcp_server.rb +79 -0
- data/examples/16_webhooks.rb +91 -0
- data/examples/README_sequential_agents.md +164 -0
- data/examples/advanced/auth/cookie_auth_tool.rb +146 -0
- data/examples/advanced/auth/custom_auth_flows_example.rb +626 -0
- data/examples/advanced/auth/excon_middleware.rb +317 -0
- data/examples/advanced/auth/excon_middleware_auth.rb +399 -0
- data/examples/advanced/auth/fiber_auth_example.rb +281 -0
- data/examples/advanced/auth/fiber_oidc_example.rb +403 -0
- data/examples/advanced/auth/httpbin_bearer_tool.rb +159 -0
- data/examples/advanced/auth/oauth2_auth.rb +419 -0
- data/examples/advanced/auth/oidc_auth.rb +514 -0
- data/examples/advanced/auth/openweather_api.rb +251 -0
- data/examples/advanced/auth/openweather_tool.rb +153 -0
- data/examples/advanced/auth/query_param_middleware_test.rb +138 -0
- data/examples/advanced/auth/service_account.rb +135 -0
- data/examples/advanced/auth/test_with_httpbin.rb +202 -0
- data/examples/advanced/auth/token_lifecycle_example.rb +428 -0
- data/examples/advanced/callback_monitoring.rb +679 -0
- data/examples/advanced/mas/fixed_delegation_example.rb +191 -0
- data/examples/advanced/mas/loop_workflow.rb +28 -0
- data/examples/advanced/mas/mock_planner.rb +77 -0
- data/examples/advanced/mas/proper_delegation_example.rb +276 -0
- data/examples/advanced/mcp/legate_mcp_server_resource_example.rb +182 -0
- data/examples/advanced/mcp/mcp_resource_server_example.rb +309 -0
- data/examples/advanced/mcp/mcp_server_async.rb +76 -0
- data/examples/advanced/mcp/mcp_server_async_tools.rb +122 -0
- data/examples/advanced/mcp/mcp_server_legate_agent.rb +95 -0
- data/examples/advanced/mcp/mcp_server_rack.rb +89 -0
- data/examples/advanced/random_calculator.rb +104 -0
- data/examples/advanced/sleep_agent.rb +153 -0
- data/examples/advanced/webhooks/webhook_e2e_runner.rb +110 -0
- data/examples/advanced/webhooks/webhook_receiver_agent.rb +58 -0
- data/examples/advanced/workflows/task_refinement_loop_agent.rb +278 -0
- data/examples/advanced/workflows/travel_planner_auto_sequential.rb +444 -0
- data/examples/advanced/workflows/travel_planner_parallel.rb +656 -0
- data/examples/advanced/workflows/travel_planner_sequential.rb +512 -0
- data/examples/tools/oauth2_example.rb +136 -0
- data/examples/tools/sleepy_tool.rb +42 -0
- data/lib/legate/activity_log.rb +71 -0
- data/lib/legate/agent.rb +959 -0
- data/lib/legate/agent_code_generator.rb +185 -0
- data/lib/legate/agent_definition.rb +812 -0
- data/lib/legate/agentic/decision.rb +49 -0
- data/lib/legate/agentic/loop.rb +134 -0
- data/lib/legate/agentic.rb +5 -0
- data/lib/legate/agents/loop_agent.rb +248 -0
- data/lib/legate/agents/parallel_agent.rb +163 -0
- data/lib/legate/agents/sequential_agent.rb +190 -0
- data/lib/legate/agents.rb +14 -0
- data/lib/legate/auth/config.rb +148 -0
- data/lib/legate/auth/coordinator.rb +218 -0
- data/lib/legate/auth/coordinators/oauth2_coordinator.rb +99 -0
- data/lib/legate/auth/coordinators/oidc_coordinator.rb +68 -0
- data/lib/legate/auth/coordinators/service_account_coordinator.rb +122 -0
- data/lib/legate/auth/credential.rb +157 -0
- data/lib/legate/auth/encryption.rb +108 -0
- data/lib/legate/auth/error.rb +94 -0
- data/lib/legate/auth/exchanged_credential.rb +180 -0
- data/lib/legate/auth/excon_middleware.rb +285 -0
- data/lib/legate/auth/http_client_utils.rb +364 -0
- data/lib/legate/auth/manager.rb +531 -0
- data/lib/legate/auth/manager_store.rb +394 -0
- data/lib/legate/auth/middleware_factory.rb +290 -0
- data/lib/legate/auth/runner.rb +279 -0
- data/lib/legate/auth/scheme.rb +125 -0
- data/lib/legate/auth/schemes/api_key.rb +212 -0
- data/lib/legate/auth/schemes/google_service_account.rb +108 -0
- data/lib/legate/auth/schemes/http_bearer.rb +98 -0
- data/lib/legate/auth/schemes/oauth2.rb +396 -0
- data/lib/legate/auth/schemes/openid_connect.rb +346 -0
- data/lib/legate/auth/schemes/service_account.rb +388 -0
- data/lib/legate/auth/schemes.rb +40 -0
- data/lib/legate/auth/token_manager.rb +362 -0
- data/lib/legate/auth/token_store.rb +86 -0
- data/lib/legate/auth/tool_context_extension.rb +97 -0
- data/lib/legate/auth/tool_integration.rb +188 -0
- data/lib/legate/auth/url_guard.rb +81 -0
- data/lib/legate/auth.rb +453 -0
- data/lib/legate/callbacks/callback_context.rb +71 -0
- data/lib/legate/cli/agent_commands.rb +950 -0
- data/lib/legate/cli/auth_commands.rb +520 -0
- data/lib/legate/cli/base_command.rb +24 -0
- data/lib/legate/cli/deployment_commands.rb +934 -0
- data/lib/legate/cli/output_helper.rb +108 -0
- data/lib/legate/cli/session_commands.rb +138 -0
- data/lib/legate/cli/skaffold_commands.rb +223 -0
- data/lib/legate/cli/tool_commands.rb +261 -0
- data/lib/legate/cli/web_commands.rb +182 -0
- data/lib/legate/cli.rb +40 -0
- data/lib/legate/configuration/webhooks.rb +113 -0
- data/lib/legate/configuration.rb +39 -0
- data/lib/legate/definition_store.rb +23 -0
- data/lib/legate/errors.rb +118 -0
- data/lib/legate/event.rb +161 -0
- data/lib/legate/gemini_ai_beta_patch.rb +39 -0
- data/lib/legate/generators/agent_generator.rb +412 -0
- data/lib/legate/generators/code_validator.rb +48 -0
- data/lib/legate/generators/legate/install_generator.rb +35 -0
- data/lib/legate/generators/legate/templates/create_legate_tables.rb.tt +36 -0
- data/lib/legate/generators/legate/templates/initializer.rb +18 -0
- data/lib/legate/generators/runtime_tool_loader.rb +76 -0
- data/lib/legate/generators/tool_generator.rb +408 -0
- data/lib/legate/generators.rb +11 -0
- data/lib/legate/global_definition_registry.rb +506 -0
- data/lib/legate/global_tool_manager.rb +135 -0
- data/lib/legate/llm/adapter.rb +69 -0
- data/lib/legate/llm/gemini.rb +172 -0
- data/lib/legate/llm/ollama.rb +80 -0
- data/lib/legate/llm.rb +34 -0
- data/lib/legate/mcp/client.rb +320 -0
- data/lib/legate/mcp/connection/sse.rb +292 -0
- data/lib/legate/mcp/connection/stdio.rb +273 -0
- data/lib/legate/mcp/connection_manager.rb +103 -0
- data/lib/legate/mcp/server/legate_agent_adapter.rb +170 -0
- data/lib/legate/mcp/server/legate_direct_agent_adapter.rb +140 -0
- data/lib/legate/mcp/server/legate_tool_adapter.rb +119 -0
- data/lib/legate/mcp/tool_wrapper.rb +138 -0
- data/lib/legate/mcp/util/schema_converter.rb +134 -0
- data/lib/legate/mcp.rb +23 -0
- data/lib/legate/plan_executor.rb +375 -0
- data/lib/legate/planner.rb +839 -0
- data/lib/legate/rails/railtie.rb +43 -0
- data/lib/legate/rails.rb +9 -0
- data/lib/legate/redaction.rb +32 -0
- data/lib/legate/session.rb +299 -0
- data/lib/legate/session_service/active_record.rb +300 -0
- data/lib/legate/session_service/base.rb +68 -0
- data/lib/legate/session_service/event_broadcast.rb +74 -0
- data/lib/legate/session_service/in_memory.rb +188 -0
- data/lib/legate/tool/metadata_dsl.rb +122 -0
- data/lib/legate/tool.rb +276 -0
- data/lib/legate/tool_code_generator.rb +103 -0
- data/lib/legate/tool_context.rb +350 -0
- data/lib/legate/tool_loader.rb +39 -0
- data/lib/legate/tool_registry.rb +73 -0
- data/lib/legate/tool_result.rb +61 -0
- data/lib/legate/tools/agent_tool.rb +187 -0
- data/lib/legate/tools/base/http_client.rb +319 -0
- data/lib/legate/tools/base/safe_url.rb +56 -0
- data/lib/legate/tools/base_async_job_tool.rb +91 -0
- data/lib/legate/tools/calculator.rb +89 -0
- data/lib/legate/tools/cat_facts.rb +81 -0
- data/lib/legate/tools/check_job_status_tool.rb +48 -0
- data/lib/legate/tools/current_time_tool.rb +64 -0
- data/lib/legate/tools/echo.rb +43 -0
- data/lib/legate/tools/http_request_tool.rb +105 -0
- data/lib/legate/tools/random_number_tool.rb +64 -0
- data/lib/legate/tools/read_webpage_tool.rb +92 -0
- data/lib/legate/tools/sleepy_tool.rb +74 -0
- data/lib/legate/tools/webhook_tool.rb +146 -0
- data/lib/legate/version.rb +5 -0
- data/lib/legate/web/app.rb +984 -0
- data/lib/legate/web/public/css/main.css +4980 -0
- data/lib/legate/web/public/images/favicon-256.png +0 -0
- data/lib/legate/web/public/images/favicon-32.png +0 -0
- data/lib/legate/web/public/images/legate-logo-dark.png +0 -0
- data/lib/legate/web/public/images/legate-logo-light.png +0 -0
- data/lib/legate/web/public/js/legate.js +616 -0
- data/lib/legate/web/public/styles/main.scss +4402 -0
- data/lib/legate/web/routes/agent_authentication_routes.rb +530 -0
- data/lib/legate/web/routes/agent_definition_routes.rb +803 -0
- data/lib/legate/web/routes/agent_generator_routes.rb +80 -0
- data/lib/legate/web/routes/agent_interaction_routes.rb +734 -0
- data/lib/legate/web/routes/agent_runtime_routes.rb +323 -0
- data/lib/legate/web/routes/api_routes.rb +56 -0
- data/lib/legate/web/routes/authentication_routes.rb +1541 -0
- data/lib/legate/web/routes/core_routes.rb +111 -0
- data/lib/legate/web/routes/documentation_routes.rb +220 -0
- data/lib/legate/web/routes/tool_generator_routes.rb +81 -0
- data/lib/legate/web/routes/tools_ui_routes.rb +207 -0
- data/lib/legate/web/sass_compiler.rb +73 -0
- data/lib/legate/web/views/_active_session_info.slim +25 -0
- data/lib/legate/web/views/_activity_list.slim +55 -0
- data/lib/legate/web/views/_agent_card.slim +56 -0
- data/lib/legate/web/views/_agent_generator_modal.slim +382 -0
- data/lib/legate/web/views/_agent_status_controls.slim +71 -0
- data/lib/legate/web/views/_agent_tool_table.slim +74 -0
- data/lib/legate/web/views/_chat_message.slim +95 -0
- data/lib/legate/web/views/_display_agent_configuration.slim +26 -0
- data/lib/legate/web/views/_display_agent_description.slim +11 -0
- data/lib/legate/web/views/_display_agent_fallback.slim +15 -0
- data/lib/legate/web/views/_display_agent_hierarchy.slim +93 -0
- data/lib/legate/web/views/_display_agent_instruction.slim +17 -0
- data/lib/legate/web/views/_display_agent_mcp.slim +13 -0
- data/lib/legate/web/views/_display_agent_model.slim +17 -0
- data/lib/legate/web/views/_display_agent_name.slim +42 -0
- data/lib/legate/web/views/_display_agent_output_key.slim +26 -0
- data/lib/legate/web/views/_display_agent_type.slim +65 -0
- data/lib/legate/web/views/_edit_agent_configuration.slim +74 -0
- data/lib/legate/web/views/_edit_agent_description.slim +16 -0
- data/lib/legate/web/views/_edit_agent_fallback.slim +25 -0
- data/lib/legate/web/views/_edit_agent_hierarchy.slim +98 -0
- data/lib/legate/web/views/_edit_agent_instruction.slim +49 -0
- data/lib/legate/web/views/_edit_agent_mcp.slim +33 -0
- data/lib/legate/web/views/_edit_agent_model.slim +23 -0
- data/lib/legate/web/views/_edit_agent_output_key.slim +36 -0
- data/lib/legate/web/views/_edit_agent_tools.slim +40 -0
- data/lib/legate/web/views/_edit_agent_type.slim +67 -0
- data/lib/legate/web/views/_session_error.slim +4 -0
- data/lib/legate/web/views/_skeleton.slim +69 -0
- data/lib/legate/web/views/_tool_card.slim +9 -0
- data/lib/legate/web/views/_tool_generator_modal.slim +311 -0
- data/lib/legate/web/views/agent.slim +436 -0
- data/lib/legate/web/views/agent_auth.slim +562 -0
- data/lib/legate/web/views/agents.slim +369 -0
- data/lib/legate/web/views/auth.slim +112 -0
- data/lib/legate/web/views/auth_credential_detail.slim +327 -0
- data/lib/legate/web/views/auth_credentials.slim +261 -0
- data/lib/legate/web/views/auth_debug.slim +94 -0
- data/lib/legate/web/views/auth_mapping_detail.slim +151 -0
- data/lib/legate/web/views/auth_mapping_new.slim +123 -0
- data/lib/legate/web/views/auth_mappings.slim +120 -0
- data/lib/legate/web/views/auth_scheme_detail.slim +274 -0
- data/lib/legate/web/views/auth_schemes.slim +259 -0
- data/lib/legate/web/views/auth_test.slim +418 -0
- data/lib/legate/web/views/chat.slim +192 -0
- data/lib/legate/web/views/docs_index.slim +105 -0
- data/lib/legate/web/views/docs_show.slim +105 -0
- data/lib/legate/web/views/error_404.slim +5 -0
- data/lib/legate/web/views/index.slim +148 -0
- data/lib/legate/web/views/layout.slim +144 -0
- data/lib/legate/web/views/tool_detail.slim +87 -0
- data/lib/legate/web/views/tools.slim +50 -0
- data/lib/legate/web/webhook_listener.rb +367 -0
- data/lib/legate/web.rb +9 -0
- data/lib/legate.rb +220 -0
- data/public/docs/advanced/callbacks.md +828 -0
- data/public/docs/advanced/mcp_schema_conversion.md +59 -0
- data/public/docs/authentication/api_reference/config.md +210 -0
- data/public/docs/authentication/api_reference/credential.md +246 -0
- data/public/docs/authentication/api_reference/encryption.md +218 -0
- data/public/docs/authentication/api_reference/exchanged_credential.md +271 -0
- data/public/docs/authentication/api_reference/excon_middleware.md +175 -0
- data/public/docs/authentication/api_reference/index.md +30 -0
- data/public/docs/authentication/api_reference/scheme.md +250 -0
- data/public/docs/authentication/api_reference/schemes/api_key.md +175 -0
- data/public/docs/authentication/api_reference/schemes/google_service_account.md +221 -0
- data/public/docs/authentication/api_reference/schemes/http_bearer.md +169 -0
- data/public/docs/authentication/api_reference/schemes/oauth2.md +343 -0
- data/public/docs/authentication/api_reference/schemes/oidc.md +73 -0
- data/public/docs/authentication/api_reference/schemes/openid_connect.md +311 -0
- data/public/docs/authentication/api_reference/schemes/service_account.md +287 -0
- data/public/docs/authentication/api_reference/token_manager.md +221 -0
- data/public/docs/authentication/api_reference/token_store.md +146 -0
- data/public/docs/authentication/api_reference/tool_context_extension.md +166 -0
- data/public/docs/authentication/guides/api_key.md +190 -0
- data/public/docs/authentication/guides/bearer.md +172 -0
- data/public/docs/authentication/guides/configuration.md +255 -0
- data/public/docs/authentication/guides/custom_flow.md +523 -0
- data/public/docs/authentication/guides/index.md +24 -0
- data/public/docs/authentication/guides/migration.md +435 -0
- data/public/docs/authentication/guides/oauth2.md +252 -0
- data/public/docs/authentication/guides/oidc.md +241 -0
- data/public/docs/authentication/guides/overview.md +155 -0
- data/public/docs/authentication/guides/secure_storage.md +301 -0
- data/public/docs/authentication/guides/service_account.md +228 -0
- data/public/docs/authentication/guides/token_lifecycle.md +295 -0
- data/public/docs/authentication/guides/web_ui_integration.md +504 -0
- data/public/docs/authentication/index.md +58 -0
- data/public/docs/authentication/troubleshooting/credential_storage.md +550 -0
- data/public/docs/authentication/troubleshooting/environment_variables.md +540 -0
- data/public/docs/authentication/troubleshooting/index.md +11 -0
- data/public/docs/authentication/troubleshooting/oauth2_issues.md +220 -0
- data/public/docs/authentication/troubleshooting/oidc_issues.md +412 -0
- data/public/docs/authentication/troubleshooting/token_refresh.md +338 -0
- data/public/docs/cli/legate_cli_usage.md +363 -0
- data/public/docs/core_concepts/legate_agent_lifecycle.md +124 -0
- data/public/docs/core_concepts/legate_architecture_overview.md +110 -0
- data/public/docs/core_concepts/legate_configuration.md +116 -0
- data/public/docs/core_concepts/legate_definition_store.md +102 -0
- data/public/docs/core_concepts/legate_planner.md +94 -0
- data/public/docs/core_concepts/legate_session_service.md +104 -0
- data/public/docs/error_handling/legate_error_handling.md +122 -0
- data/public/docs/examples.md +199 -0
- data/public/docs/getting_started.md +111 -0
- data/public/docs/guides/agentic_agents.md +137 -0
- data/public/docs/guides/ai_code_generators.md +437 -0
- data/public/docs/guides/auto_loading.md +326 -0
- data/public/docs/guides/configuring_agent_webhooks.md +219 -0
- data/public/docs/guides/http_client_usage.md +264 -0
- data/public/docs/guides/llm_providers.md +137 -0
- data/public/docs/guides/mcp_client_integration.md +232 -0
- data/public/docs/guides/mcp_server_exposure.md +206 -0
- data/public/docs/guides/rails_integration.md +128 -0
- data/public/docs/guides/sending_outbound_webhooks.md +227 -0
- data/public/docs/guides/streaming.md +112 -0
- data/public/docs/guides/webhooks.md +288 -0
- data/public/docs/introduction.md +51 -0
- data/public/docs/multi_agent_systems/advanced_features.md +57 -0
- data/public/docs/multi_agent_systems/agent_delegation.md +190 -0
- data/public/docs/multi_agent_systems/agent_hierarchy.md +49 -0
- data/public/docs/multi_agent_systems/state_management.md +47 -0
- data/public/docs/multi_agent_systems/workflow_agents.md +72 -0
- data/public/docs/tools/legate_built_in_tools.md +332 -0
- data/public/docs/tools/legate_tools_and_registry.md +263 -0
- data/public/docs/web_ui/legate_web_ui.md +137 -0
- 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
|