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,241 @@
|
|
|
1
|
+
# OpenID Connect Authentication
|
|
2
|
+
|
|
3
|
+
OpenID Connect (OIDC) is an identity layer built on top of OAuth 2.0 that allows clients to verify the identity of end-users. The Legate Ruby library provides comprehensive support for OpenID Connect authentication.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
OpenID Connect extends OAuth 2.0 with identity verification functionality, allowing applications to:
|
|
8
|
+
|
|
9
|
+
- Authenticate users with an identity provider
|
|
10
|
+
- Obtain basic profile information about the user
|
|
11
|
+
- Receive verified identity information via a JWT (JSON Web Token) called an ID token
|
|
12
|
+
- Access additional user information via standardized endpoints
|
|
13
|
+
|
|
14
|
+
## Configuration
|
|
15
|
+
|
|
16
|
+
### Creating an OpenID Connect Scheme
|
|
17
|
+
|
|
18
|
+
There are two main ways to configure an OpenID Connect scheme:
|
|
19
|
+
|
|
20
|
+
#### Using Discovery
|
|
21
|
+
|
|
22
|
+
The simplest approach is to use OpenID Connect Discovery, which automatically fetches configuration:
|
|
23
|
+
|
|
24
|
+
```ruby
|
|
25
|
+
# Configure using the provider's discovery URL
|
|
26
|
+
scheme = Legate::Auth::Schemes::OpenIDConnect.new(
|
|
27
|
+
discovery_url: 'https://accounts.google.com/.well-known/openid-configuration'
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
# Alternatively, specify the provider URI
|
|
31
|
+
scheme = Legate::Auth::Schemes::OpenIDConnect.new(
|
|
32
|
+
provider_uri: 'https://accounts.google.com'
|
|
33
|
+
)
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
#### Manual Configuration
|
|
37
|
+
|
|
38
|
+
You can also manually specify all the necessary endpoints:
|
|
39
|
+
|
|
40
|
+
```ruby
|
|
41
|
+
# Configure by explicitly providing all endpoints
|
|
42
|
+
scheme = Legate::Auth::Schemes::OpenIDConnect.new(
|
|
43
|
+
authorization_url: 'https://accounts.google.com/o/oauth2/auth',
|
|
44
|
+
token_url: 'https://oauth2.googleapis.com/token',
|
|
45
|
+
userinfo_url: 'https://openidconnect.googleapis.com/v1/userinfo',
|
|
46
|
+
jwks_url: 'https://www.googleapis.com/oauth2/v3/certs',
|
|
47
|
+
scopes: ['openid', 'profile', 'email']
|
|
48
|
+
)
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Creating an OpenID Connect Credential
|
|
52
|
+
|
|
53
|
+
```ruby
|
|
54
|
+
# Basic OpenID Connect credential (use :oidc — :openid_connect is not a
|
|
55
|
+
# valid credential auth_type and would raise CredentialError)
|
|
56
|
+
credential = Legate::Auth::Credential.new(
|
|
57
|
+
auth_type: :oidc,
|
|
58
|
+
client_id: ENV['CLIENT_ID'],
|
|
59
|
+
client_secret: ENV['CLIENT_SECRET']
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
# With additional options
|
|
63
|
+
credential = Legate::Auth::Credential.new(
|
|
64
|
+
auth_type: :oidc,
|
|
65
|
+
client_id: ENV['CLIENT_ID'],
|
|
66
|
+
client_secret: ENV['CLIENT_SECRET'],
|
|
67
|
+
additional_params: {
|
|
68
|
+
'prompt' => 'login' # Force user to re-authenticate
|
|
69
|
+
}
|
|
70
|
+
)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Authentication Flow
|
|
74
|
+
|
|
75
|
+
The OpenID Connect authentication flow is similar to the OAuth 2.0 authorization code flow, with added identity verification:
|
|
76
|
+
|
|
77
|
+
```ruby
|
|
78
|
+
# 1. Configure the OpenID Connect scheme using discovery
|
|
79
|
+
scheme = Legate::Auth::Schemes::OpenIDConnect.new(
|
|
80
|
+
discovery_url: 'https://accounts.google.com/.well-known/openid-configuration',
|
|
81
|
+
scopes: ['openid', 'profile', 'email']
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
# 2. Configure the credential (use :oidc — :openid_connect is not a valid
|
|
85
|
+
# credential auth_type and would raise CredentialError)
|
|
86
|
+
credential = Legate::Auth::Credential.new(
|
|
87
|
+
auth_type: :oidc,
|
|
88
|
+
client_id: ENV['CLIENT_ID'],
|
|
89
|
+
client_secret: ENV['CLIENT_SECRET']
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
# 3. Build the authorization URI via a Config and redirect the user
|
|
93
|
+
config = Legate::Auth::Config.new(scheme: scheme, credential: credential)
|
|
94
|
+
state = SecureRandom.hex(16)
|
|
95
|
+
auth_url = config.build_authorization_uri('https://your-app.com/callback', state)
|
|
96
|
+
# redirect_to auth_url
|
|
97
|
+
|
|
98
|
+
# 4. On the callback, set the response URI on the config
|
|
99
|
+
config.response_uri = 'https://your-app.com/callback?code=12345&state=abcde'
|
|
100
|
+
|
|
101
|
+
# 5. Exchange the authorization code for tokens (includes an ID token)
|
|
102
|
+
token = scheme.exchange_token(config, credential)
|
|
103
|
+
puts token[:access_token]
|
|
104
|
+
puts token[:id_token]
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
> This shows the direct `Config`/scheme flow. Tools can also drive OIDC
|
|
108
|
+
> interactively via `context.with_authentication` (see
|
|
109
|
+
> [`ToolContextExtension`](../api_reference/tool_context_extension)).
|
|
110
|
+
|
|
111
|
+
## Key OpenID Connect Features
|
|
112
|
+
|
|
113
|
+
### ID Token
|
|
114
|
+
|
|
115
|
+
The ID token is a JWT containing verified information about the user:
|
|
116
|
+
|
|
117
|
+
```ruby
|
|
118
|
+
# The token exchange process returns an ID token automatically
|
|
119
|
+
# In an ExchangedCredential, you can access it as:
|
|
120
|
+
id_token = exchanged_credential[:id_token]
|
|
121
|
+
|
|
122
|
+
# To decode and verify the token
|
|
123
|
+
require 'jwt'
|
|
124
|
+
decoded_token = JWT.decode(id_token, nil, false)[0]
|
|
125
|
+
|
|
126
|
+
# To access claims
|
|
127
|
+
user_email = decoded_token['email']
|
|
128
|
+
name = decoded_token['name']
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### UserInfo Endpoint
|
|
132
|
+
|
|
133
|
+
For more detailed user information, you can call the UserInfo endpoint:
|
|
134
|
+
|
|
135
|
+
```ruby
|
|
136
|
+
# Get an access token
|
|
137
|
+
access_token = exchanged_credential[:access_token]
|
|
138
|
+
|
|
139
|
+
# Create the scheme (or use existing one)
|
|
140
|
+
scheme = Legate::Auth::Schemes::OpenIDConnect.new(
|
|
141
|
+
discovery_url: 'https://accounts.google.com/.well-known/openid-configuration'
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
# Fetch user information
|
|
145
|
+
user_info = scheme.get_userinfo(access_token)
|
|
146
|
+
|
|
147
|
+
# Access user data
|
|
148
|
+
email = user_info['email']
|
|
149
|
+
name = user_info['name']
|
|
150
|
+
picture = user_info['picture']
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Provider-Specific Configurations
|
|
154
|
+
|
|
155
|
+
### Google OpenID Connect
|
|
156
|
+
|
|
157
|
+
```ruby
|
|
158
|
+
scheme = Legate::Auth::Schemes::OpenIDConnect.new(
|
|
159
|
+
discovery_url: 'https://accounts.google.com/.well-known/openid-configuration',
|
|
160
|
+
scopes: ['openid', 'profile', 'email'],
|
|
161
|
+
additional_params: {
|
|
162
|
+
'prompt' => 'consent',
|
|
163
|
+
'access_type' => 'offline'
|
|
164
|
+
}
|
|
165
|
+
)
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Microsoft Azure OpenID Connect
|
|
169
|
+
|
|
170
|
+
```ruby
|
|
171
|
+
tenant_id = 'common' # Use 'common' for multi-tenant, or a specific tenant ID
|
|
172
|
+
|
|
173
|
+
scheme = Legate::Auth::Schemes::OpenIDConnect.new(
|
|
174
|
+
discovery_url: "https://login.microsoftonline.com/#{tenant_id}/v2.0/.well-known/openid-configuration",
|
|
175
|
+
scopes: ['openid', 'profile', 'email', 'offline_access']
|
|
176
|
+
)
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Auth0
|
|
180
|
+
|
|
181
|
+
```ruby
|
|
182
|
+
domain = 'your-domain.auth0.com'
|
|
183
|
+
|
|
184
|
+
scheme = Legate::Auth::Schemes::OpenIDConnect.new(
|
|
185
|
+
discovery_url: "https://#{domain}/.well-known/openid-configuration",
|
|
186
|
+
scopes: ['openid', 'profile', 'email']
|
|
187
|
+
)
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## Security Considerations
|
|
191
|
+
|
|
192
|
+
- **Nonce Verification**: The library automatically adds a nonce parameter to prevent replay attacks
|
|
193
|
+
- **ID Token Validation**: Always verify the ID token signature, issuer, audience, and expiration
|
|
194
|
+
- **Scopes**: Request only the scopes your application needs
|
|
195
|
+
- **Secure Storage**: Tokens are cached in scoped session state as plaintext; for at-rest encryption, apply the opt-in `Legate::Auth::Encryption` module yourself
|
|
196
|
+
|
|
197
|
+
## Advanced Features
|
|
198
|
+
|
|
199
|
+
### PKCE (Proof Key for Code Exchange)
|
|
200
|
+
|
|
201
|
+
```ruby
|
|
202
|
+
# PKCE is enabled by default
|
|
203
|
+
scheme = Legate::Auth::Schemes::OpenIDConnect.new(
|
|
204
|
+
discovery_url: 'https://accounts.google.com/.well-known/openid-configuration',
|
|
205
|
+
use_pkce: true # This is the default
|
|
206
|
+
)
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### Prompt Parameter
|
|
210
|
+
|
|
211
|
+
Control the authentication experience using the prompt parameter:
|
|
212
|
+
|
|
213
|
+
```ruby
|
|
214
|
+
scheme = Legate::Auth::Schemes::OpenIDConnect.new(
|
|
215
|
+
discovery_url: 'https://accounts.google.com/.well-known/openid-configuration',
|
|
216
|
+
additional_params: {
|
|
217
|
+
'prompt' => 'login' # Options: none, login, consent, select_account
|
|
218
|
+
}
|
|
219
|
+
)
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
## Troubleshooting
|
|
223
|
+
|
|
224
|
+
If you encounter issues with OpenID Connect authentication:
|
|
225
|
+
|
|
226
|
+
- Ensure 'openid' is included in the requested scopes
|
|
227
|
+
- Verify that your client is properly registered with the identity provider
|
|
228
|
+
- Check that redirect URIs exactly match those registered with the provider
|
|
229
|
+
- See the [OpenID Connect Troubleshooting Guide](../troubleshooting/oidc_issues) for detailed solutions
|
|
230
|
+
|
|
231
|
+
## Related Topics
|
|
232
|
+
|
|
233
|
+
- [OAuth2 Authentication](./oauth2) - Learn more about the underlying OAuth2 protocol
|
|
234
|
+
- [Service Account Authentication](./service_account) - Use service accounts for server-to-server authentication
|
|
235
|
+
- [Token Lifecycle Management](./token_lifecycle) - Advanced token management techniques
|
|
236
|
+
|
|
237
|
+
## Next Steps
|
|
238
|
+
|
|
239
|
+
- [OAuth2 Authentication](./oauth2): Learn more about the underlying OAuth2 protocol
|
|
240
|
+
- [Service Account Authentication](./service_account): Use service accounts for server-to-server authentication
|
|
241
|
+
- [Token Lifecycle Management](./token_lifecycle): Advanced token management techniques
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# Authentication Overview
|
|
2
|
+
|
|
3
|
+
## Introduction
|
|
4
|
+
|
|
5
|
+
The Legate Ruby authentication system provides a comprehensive framework for handling authentication with external APIs. It supports various authentication methods including:
|
|
6
|
+
|
|
7
|
+
- API Key authentication
|
|
8
|
+
- HTTP Bearer token authentication
|
|
9
|
+
- OAuth2 authentication
|
|
10
|
+
- OpenID Connect (OIDC) authentication
|
|
11
|
+
- Service Account authentication
|
|
12
|
+
|
|
13
|
+
The system is designed to handle both interactive authentication flows (like OAuth2, which requires user consent) and non-interactive flows (like API Keys), with a unified interface.
|
|
14
|
+
|
|
15
|
+
## Core Concepts
|
|
16
|
+
|
|
17
|
+
### Authentication Schemes
|
|
18
|
+
|
|
19
|
+
An authentication scheme (`Legate::Auth::Scheme`) defines how an API expects credentials to be provided. Each scheme implements:
|
|
20
|
+
|
|
21
|
+
- How to apply authentication to requests
|
|
22
|
+
- How to exchange initial credentials for tokens (if applicable)
|
|
23
|
+
- How to refresh tokens (if applicable)
|
|
24
|
+
- How to build authorization URIs for interactive flows
|
|
25
|
+
|
|
26
|
+
The Legate Ruby library includes the following authentication schemes:
|
|
27
|
+
|
|
28
|
+
- `Legate::Auth::Schemes::ApiKey`: For API key authentication (in header, query, or cookie)
|
|
29
|
+
- `Legate::Auth::Schemes::HTTPBearer`: For Bearer token authentication
|
|
30
|
+
- `Legate::Auth::Schemes::OAuth2`: For OAuth2 authentication flows
|
|
31
|
+
- `Legate::Auth::Schemes::OpenIDConnect`: For OpenID Connect authentication
|
|
32
|
+
- `Legate::Auth::Schemes::ServiceAccount`: For service account authentication
|
|
33
|
+
- `Legate::Auth::Schemes::GoogleServiceAccount`: For Google Cloud service accounts
|
|
34
|
+
|
|
35
|
+
### Credentials
|
|
36
|
+
|
|
37
|
+
A credential (`Legate::Auth::Credential`) contains the initial information needed to start authentication:
|
|
38
|
+
|
|
39
|
+
- API Keys
|
|
40
|
+
- OAuth2 client ID and client secret
|
|
41
|
+
- Bearer tokens
|
|
42
|
+
- Service account keys
|
|
43
|
+
|
|
44
|
+
Credentials can be provided directly or through environment variables, which is recommended for sensitive information.
|
|
45
|
+
|
|
46
|
+
### Token Exchange
|
|
47
|
+
|
|
48
|
+
For authentication methods like OAuth2, the initial credential must be exchanged for a token:
|
|
49
|
+
|
|
50
|
+
1. The initial credential (e.g., client ID and secret) is used to start the authentication flow
|
|
51
|
+
2. The flow results in an exchanged credential (`Legate::Auth::ExchangedCredential`)
|
|
52
|
+
3. The exchanged credential contains access tokens, refresh tokens, and expiry information
|
|
53
|
+
|
|
54
|
+
### Authentication Flows
|
|
55
|
+
|
|
56
|
+
#### Non-Interactive Flow (API Key, Bearer Token)
|
|
57
|
+
|
|
58
|
+
```ruby
|
|
59
|
+
# Create an API Key scheme (no constructor arguments)
|
|
60
|
+
scheme = Legate::Auth::Schemes::ApiKey.new
|
|
61
|
+
|
|
62
|
+
# Create a credential with the API key. The key's location ('header',
|
|
63
|
+
# 'query', or 'cookie') and name live on the credential, not the scheme.
|
|
64
|
+
credential = Legate::Auth::Credential.new(
|
|
65
|
+
auth_type: :api_key,
|
|
66
|
+
api_key: ENV['API_KEY'],
|
|
67
|
+
location: 'header', # default
|
|
68
|
+
name: 'X-API-Key' # default
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
# Attach the scheme/credential to your outbound HTTP client. The simplest
|
|
72
|
+
# path is the Excon connection helper, which applies the API key for you:
|
|
73
|
+
connection = Legate::Auth.create_connection('https://api.example.com',
|
|
74
|
+
scheme: scheme,
|
|
75
|
+
credential: credential
|
|
76
|
+
)
|
|
77
|
+
response = connection.get(path: '/protected-resource')
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
#### Interactive Flow (OAuth2, OIDC)
|
|
81
|
+
|
|
82
|
+
```ruby
|
|
83
|
+
# Create an OAuth2 scheme
|
|
84
|
+
scheme = Legate::Auth::Schemes::OAuth2.new(
|
|
85
|
+
authorization_url: 'https://auth.example.com/authorize',
|
|
86
|
+
token_url: 'https://auth.example.com/token',
|
|
87
|
+
scopes: ['profile', 'email']
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
# Create a credential with client ID and secret
|
|
91
|
+
credential = Legate::Auth::Credential.new(
|
|
92
|
+
auth_type: :oauth2,
|
|
93
|
+
client_id: ENV['CLIENT_ID'],
|
|
94
|
+
client_secret: ENV['CLIENT_SECRET']
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
# Drive the interactive flow via Config:
|
|
98
|
+
# 1. Build the authorization URI and redirect the user to it
|
|
99
|
+
config = Legate::Auth::Config.new(scheme: scheme, credential: credential)
|
|
100
|
+
state = SecureRandom.hex(16)
|
|
101
|
+
auth_uri = config.build_authorization_uri('https://your-app.com/callback', state)
|
|
102
|
+
# redirect_to auth_uri
|
|
103
|
+
|
|
104
|
+
# 2. When the user is redirected back, set the response URI on the config
|
|
105
|
+
config.response_uri = 'https://your-app.com/callback?code=12345&state=abcde'
|
|
106
|
+
|
|
107
|
+
# 3. Exchange the authorization code for tokens
|
|
108
|
+
token = scheme.exchange_token(config, credential)
|
|
109
|
+
|
|
110
|
+
# `token` is an ExchangedCredential you can store and apply to requests.
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
> The fiber-based, tool-driven flow (where a tool's `with_authentication`
|
|
114
|
+
> block yields an auth request to the caller) is also supported; see
|
|
115
|
+
> [`Legate::Auth::ToolContextExtension`](../api_reference/tool_context_extension).
|
|
116
|
+
|
|
117
|
+
#### Service Account Flow
|
|
118
|
+
|
|
119
|
+
```ruby
|
|
120
|
+
# Create a Service Account scheme
|
|
121
|
+
scheme = Legate::Auth::Schemes::GoogleServiceAccount.new(
|
|
122
|
+
scopes: ['https://www.googleapis.com/auth/cloud-platform']
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
# Create a credential with the service account key (raw JSON string)
|
|
126
|
+
credential = Legate::Auth::Credential.new(
|
|
127
|
+
auth_type: :google_service_account,
|
|
128
|
+
service_account_key: File.read('service-account.json')
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
# Exchange for an access token and apply it to outbound requests
|
|
132
|
+
token = scheme.exchange_token(credential)
|
|
133
|
+
connection = Legate::Auth.create_connection('https://api.example.com',
|
|
134
|
+
scheme: scheme,
|
|
135
|
+
credential: token
|
|
136
|
+
)
|
|
137
|
+
result = connection.get(path: '/resource')
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Security Considerations
|
|
141
|
+
|
|
142
|
+
The Legate Ruby library implements several security measures:
|
|
143
|
+
|
|
144
|
+
- Tokens are cached in scoped session state; for at-rest encryption, the opt-in `Legate::Auth::Encryption` module is available (it is not applied automatically)
|
|
145
|
+
- Access tokens have limited lifetimes, with automatic expiry checks
|
|
146
|
+
- Refresh tokens are handled by the `TokenManager`
|
|
147
|
+
- Environment variable resolution (the `ENV:` prefix) keeps sensitive values out of source code
|
|
148
|
+
- Outbound auth/token URLs are validated by `Legate::Auth::UrlGuard` to block SSRF to private/loopback addresses
|
|
149
|
+
|
|
150
|
+
## Next Steps
|
|
151
|
+
|
|
152
|
+
- [Authentication Configuration](./configuration) - How to configure authentication for different scenarios
|
|
153
|
+
- [API Key Authentication](./api_key) - Detailed guide for API key authentication
|
|
154
|
+
- [OAuth2 Authentication](./oauth2) - Complete guide for implementing OAuth2 flows
|
|
155
|
+
- [Token Lifecycle Management](./token_lifecycle) - Managing token expiration and refresh
|
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
# Secure Credential Storage
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
Secure storage of authentication credentials and tokens is critical for maintaining the security of your Legate Ruby applications. This guide explains best practices for storing and handling sensitive authentication data.
|
|
6
|
+
|
|
7
|
+
## Security Considerations
|
|
8
|
+
|
|
9
|
+
When working with authentication, you need to protect several types of sensitive information:
|
|
10
|
+
|
|
11
|
+
- **API Keys**: Direct access tokens that grant API access
|
|
12
|
+
- **Client Secrets**: OAuth2/OIDC secrets used to authenticate your application
|
|
13
|
+
- **Bearer Tokens**: Tokens that provide access to protected resources
|
|
14
|
+
- **Access Tokens**: Short-lived tokens obtained through authentication flows
|
|
15
|
+
- **Refresh Tokens**: Long-lived tokens used to obtain new access tokens
|
|
16
|
+
- **Service Account Keys**: JSON credentials that authenticate as a service account
|
|
17
|
+
|
|
18
|
+
## Legate Security Architecture
|
|
19
|
+
|
|
20
|
+
The Legate Ruby library provides several measures to help protect sensitive authentication data:
|
|
21
|
+
|
|
22
|
+
1. **Optional Encryption**: An opt-in `Legate::Auth::Encryption` module is available for encrypting data at rest
|
|
23
|
+
2. **Scoped Storage**: Tokens are cached in scoped session state via the token store
|
|
24
|
+
3. **Environment Variables**: Credential values can be sourced from environment variables (the `ENV:` prefix)
|
|
25
|
+
4. **Minimal Exposure**: Access tokens are short-lived and refreshed automatically by the `TokenManager`
|
|
26
|
+
|
|
27
|
+
## Encryption in Legate
|
|
28
|
+
|
|
29
|
+
> **Important:** Encryption is **opt-in** and is **not** wired into `TokenStore`. `TokenStore#store` persists plaintext token data (`token.to_h`) in scoped state. To encrypt at rest, call the `Legate::Auth::Encryption` module yourself.
|
|
30
|
+
|
|
31
|
+
The `Legate::Auth::Encryption` module uses the `rbnacl` gem (libsodium SecretBox) for authenticated encryption. `rbnacl` is an optional dependency: the module lazily requires it and raises `LoadError` if it (or libsodium) is missing.
|
|
32
|
+
|
|
33
|
+
### Using the Encryption Module
|
|
34
|
+
|
|
35
|
+
`Encryption` is a module (not instantiable). Call its module methods directly. Keys are Base64-encoded; with no key argument it reads `LEGATE_AUTH_ENCRYPTION_KEY`.
|
|
36
|
+
|
|
37
|
+
```ruby
|
|
38
|
+
require 'legate/auth/encryption'
|
|
39
|
+
|
|
40
|
+
# Generate a Base64 key once and store it securely
|
|
41
|
+
key = Legate::Auth::Encryption.generate_key
|
|
42
|
+
|
|
43
|
+
# Encrypt / decrypt (output is "LGTAUTH" + Base64)
|
|
44
|
+
ciphertext = Legate::Auth::Encryption.encrypt(JSON.dump(token.to_h), key)
|
|
45
|
+
plaintext = Legate::Auth::Encryption.decrypt(ciphertext, key)
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Applying Encryption to Stored Tokens
|
|
49
|
+
|
|
50
|
+
Because `TokenStore` does not encrypt, apply encryption in your own storage layer:
|
|
51
|
+
|
|
52
|
+
```ruby
|
|
53
|
+
key = ENV['LEGATE_AUTH_ENCRYPTION_KEY']
|
|
54
|
+
|
|
55
|
+
# Encrypt before persisting through storage you control
|
|
56
|
+
ciphertext = Legate::Auth::Encryption.encrypt(JSON.dump(token.to_h), key)
|
|
57
|
+
|
|
58
|
+
# Decrypt on read, then rebuild the token
|
|
59
|
+
data = JSON.parse(Legate::Auth::Encryption.decrypt(ciphertext, key), symbolize_names: true)
|
|
60
|
+
token = Legate::Auth::ExchangedCredential.from_h(data)
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Environment Variable Handling
|
|
64
|
+
|
|
65
|
+
The Legate Ruby library supports sourcing credential values from environment variables, which is a security best practice.
|
|
66
|
+
|
|
67
|
+
### Direct Environment Variable Usage
|
|
68
|
+
|
|
69
|
+
```ruby
|
|
70
|
+
# Read environment variables at construction time
|
|
71
|
+
api_key_credential = Legate::Auth::Credential.new(
|
|
72
|
+
auth_type: :api_key,
|
|
73
|
+
api_key: ENV['API_KEY']
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
oauth2_credential = Legate::Auth::Credential.new(
|
|
77
|
+
auth_type: :oauth2,
|
|
78
|
+
client_id: ENV['OAUTH2_CLIENT_ID'],
|
|
79
|
+
client_secret: ENV['OAUTH2_CLIENT_SECRET']
|
|
80
|
+
)
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Environment Variable References
|
|
84
|
+
|
|
85
|
+
The only reference mechanism is the `ENV:` prefix inside a string value
|
|
86
|
+
(resolved lazily when the attribute is read). There are no `*_env` attributes.
|
|
87
|
+
|
|
88
|
+
```ruby
|
|
89
|
+
api_key_credential = Legate::Auth::Credential.new(
|
|
90
|
+
auth_type: :api_key,
|
|
91
|
+
api_key: 'ENV:API_KEY'
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
oauth2_credential = Legate::Auth::Credential.new(
|
|
95
|
+
auth_type: :oauth2,
|
|
96
|
+
client_id: 'ENV:OAUTH2_CLIENT_ID',
|
|
97
|
+
client_secret: 'ENV:OAUTH2_CLIENT_SECRET'
|
|
98
|
+
)
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Secure Token Storage
|
|
102
|
+
|
|
103
|
+
The Legate Ruby library provides the `TokenStore` class for secure storage of authentication tokens.
|
|
104
|
+
|
|
105
|
+
### Creating a Token Store
|
|
106
|
+
|
|
107
|
+
```ruby
|
|
108
|
+
# Create a token store with the session service (positional argument)
|
|
109
|
+
token_store = Legate::Auth::TokenStore.new(session_service)
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Storing Tokens
|
|
113
|
+
|
|
114
|
+
`store(key, token)` accepts a string key and an `ExchangedCredential` (it
|
|
115
|
+
serializes `token.to_h` into scoped state; it does not encrypt):
|
|
116
|
+
|
|
117
|
+
```ruby
|
|
118
|
+
token = Legate::Auth::ExchangedCredential.new(
|
|
119
|
+
auth_type: :oauth2,
|
|
120
|
+
access_token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
|
|
121
|
+
refresh_token: 'rtok_abc123...',
|
|
122
|
+
expires_in: 3600
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
token_store.store('client123', token)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Retrieving Tokens
|
|
129
|
+
|
|
130
|
+
```ruby
|
|
131
|
+
# Returns an ExchangedCredential, or nil if missing/expired
|
|
132
|
+
token = token_store.get('client123')
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Service Account Key Security
|
|
136
|
+
|
|
137
|
+
Service account keys (especially Google Service Account JSON keys) require special security consideration.
|
|
138
|
+
|
|
139
|
+
### Store Service Account Keys Securely
|
|
140
|
+
|
|
141
|
+
```ruby
|
|
142
|
+
# Option 1: Store the JSON in an environment variable (recommended).
|
|
143
|
+
# service_account_key takes the raw JSON string.
|
|
144
|
+
ENV['GOOGLE_SERVICE_ACCOUNT_JSON'] = '{"type":"service_account","project_id":"..."}'
|
|
145
|
+
|
|
146
|
+
credential = Legate::Auth::Credential.new(
|
|
147
|
+
auth_type: :google_service_account,
|
|
148
|
+
service_account_key: ENV['GOOGLE_SERVICE_ACCOUNT_JSON']
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
# Option 2: Reference the env var lazily with the ENV: prefix
|
|
152
|
+
credential = Legate::Auth::Credential.new(
|
|
153
|
+
auth_type: :google_service_account,
|
|
154
|
+
service_account_key: 'ENV:GOOGLE_SERVICE_ACCOUNT_JSON'
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
# Option 3: Reference a file path with service_account_key_file
|
|
158
|
+
credential = Legate::Auth::Credential.new(
|
|
159
|
+
auth_type: :google_service_account,
|
|
160
|
+
service_account_key_file: '/secure/path/to/service-account.json'
|
|
161
|
+
)
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## Deployment Security Best Practices
|
|
165
|
+
|
|
166
|
+
### Environment Variables in Production
|
|
167
|
+
|
|
168
|
+
For production environments, consider these best practices for handling environment variables:
|
|
169
|
+
|
|
170
|
+
1. **Use a secrets manager**: Store secrets in a dedicated secrets manager like HashiCorp Vault, AWS Secrets Manager, or Google Secret Manager
|
|
171
|
+
2. **Inject at runtime**: Inject secrets into your application at runtime rather than storing them in configuration files
|
|
172
|
+
3. **Use minimal permissions**: For service accounts, use the principle of least privilege
|
|
173
|
+
4. **Rotate credentials**: Regularly rotate credentials and tokens
|
|
174
|
+
|
|
175
|
+
### Example with Docker Compose
|
|
176
|
+
|
|
177
|
+
```yaml
|
|
178
|
+
# docker-compose.yml
|
|
179
|
+
version: '3'
|
|
180
|
+
services:
|
|
181
|
+
app:
|
|
182
|
+
build: .
|
|
183
|
+
environment:
|
|
184
|
+
- OAUTH2_CLIENT_ID=${OAUTH2_CLIENT_ID}
|
|
185
|
+
- OAUTH2_CLIENT_SECRET=${OAUTH2_CLIENT_SECRET}
|
|
186
|
+
- API_KEY=${API_KEY}
|
|
187
|
+
env_file:
|
|
188
|
+
- .env.production
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### Example with Kubernetes
|
|
192
|
+
|
|
193
|
+
```yaml
|
|
194
|
+
# deployment.yaml
|
|
195
|
+
apiVersion: v1
|
|
196
|
+
kind: Secret
|
|
197
|
+
metadata:
|
|
198
|
+
name: auth-credentials
|
|
199
|
+
type: Opaque
|
|
200
|
+
data:
|
|
201
|
+
oauth2-client-id: <base64-encoded-client-id>
|
|
202
|
+
oauth2-client-secret: <base64-encoded-client-secret>
|
|
203
|
+
api-key: <base64-encoded-api-key>
|
|
204
|
+
---
|
|
205
|
+
apiVersion: apps/v1
|
|
206
|
+
kind: Deployment
|
|
207
|
+
metadata:
|
|
208
|
+
name: legate-app
|
|
209
|
+
spec:
|
|
210
|
+
template:
|
|
211
|
+
spec:
|
|
212
|
+
containers:
|
|
213
|
+
- name: legate-app
|
|
214
|
+
image: legate-app:latest
|
|
215
|
+
env:
|
|
216
|
+
- name: OAUTH2_CLIENT_ID
|
|
217
|
+
valueFrom:
|
|
218
|
+
secretKeyRef:
|
|
219
|
+
name: auth-credentials
|
|
220
|
+
key: oauth2-client-id
|
|
221
|
+
- name: OAUTH2_CLIENT_SECRET
|
|
222
|
+
valueFrom:
|
|
223
|
+
secretKeyRef:
|
|
224
|
+
name: auth-credentials
|
|
225
|
+
key: oauth2-client-secret
|
|
226
|
+
- name: API_KEY
|
|
227
|
+
valueFrom:
|
|
228
|
+
secretKeyRef:
|
|
229
|
+
name: auth-credentials
|
|
230
|
+
key: api-key
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
## Development Security Best Practices
|
|
234
|
+
|
|
235
|
+
### Local Environment Variables
|
|
236
|
+
|
|
237
|
+
For development environments, consider these approaches:
|
|
238
|
+
|
|
239
|
+
1. **Use a .env file**: Store environment variables in a .env file that is not committed to source control
|
|
240
|
+
2. **Use a development-only secrets manager**: Set up a development instance of your secrets manager
|
|
241
|
+
3. **Use dummy credentials for development**: Use separate credentials for development environments
|
|
242
|
+
|
|
243
|
+
### Example .env File
|
|
244
|
+
|
|
245
|
+
```
|
|
246
|
+
# .env (add to .gitignore)
|
|
247
|
+
OAUTH2_CLIENT_ID=dev-client-id
|
|
248
|
+
OAUTH2_CLIENT_SECRET=dev-client-secret
|
|
249
|
+
API_KEY=dev-api-key
|
|
250
|
+
GOOGLE_SERVICE_ACCOUNT_JSON={"type":"service_account","project_id":"..."}
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
### Loading Environment Variables
|
|
254
|
+
|
|
255
|
+
```ruby
|
|
256
|
+
# Load environment variables
|
|
257
|
+
require 'dotenv'
|
|
258
|
+
Dotenv.load
|
|
259
|
+
|
|
260
|
+
# Use in credential creation
|
|
261
|
+
oauth2_credential = Legate::Auth::Credential.new(
|
|
262
|
+
auth_type: :oauth2,
|
|
263
|
+
client_id: ENV['OAUTH2_CLIENT_ID'],
|
|
264
|
+
client_secret: ENV['OAUTH2_CLIENT_SECRET']
|
|
265
|
+
)
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
## Session Service Security
|
|
269
|
+
|
|
270
|
+
The Legate session service stores authentication tokens in memory as plaintext (unless you apply opt-in encryption yourself). Ensure proper security:
|
|
271
|
+
|
|
272
|
+
1. **Use HTTPS**: Ensure all communication is over HTTPS
|
|
273
|
+
2. **Session isolation**: Use proper session management practices
|
|
274
|
+
3. **Process lifecycle**: Be aware that in-memory sessions are lost on process restart
|
|
275
|
+
|
|
276
|
+
```ruby
|
|
277
|
+
# Create the in-memory session service
|
|
278
|
+
session_service = Legate::SessionService::InMemory.new
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
## Security Checklist
|
|
282
|
+
|
|
283
|
+
Use this checklist to ensure you're following security best practices:
|
|
284
|
+
|
|
285
|
+
- [ ] Store credentials in environment variables, not in code
|
|
286
|
+
- [ ] Use HTTPS for all API and authentication endpoints
|
|
287
|
+
- [ ] Configure proper token expiration and refresh thresholds
|
|
288
|
+
- [ ] Implement proper error handling for authentication failures
|
|
289
|
+
- [ ] Ensure session service is properly configured
|
|
290
|
+
- [ ] Rotate credentials regularly
|
|
291
|
+
- [ ] Use the principle of least privilege for service accounts
|
|
292
|
+
- [ ] Implement proper logging for authentication events (but don't log sensitive data)
|
|
293
|
+
- [ ] Keep the Legate Ruby library updated to get security fixes
|
|
294
|
+
|
|
295
|
+
## Related Topics
|
|
296
|
+
- [Authentication Configuration](./configuration)
|
|
297
|
+
- [Token Lifecycle Management](./token_lifecycle)
|
|
298
|
+
- [OAuth2 Authentication](./oauth2)
|
|
299
|
+
- [Service Account Authentication](./service_account)
|
|
300
|
+
- [`Legate::Auth::TokenStore` API Reference](../api_reference/token_store)
|
|
301
|
+
- [`Legate::Auth::Encryption` API Reference](../api_reference/encryption)
|