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,550 @@
|
|
|
1
|
+
# Credential Storage Issues
|
|
2
|
+
|
|
3
|
+
This guide addresses common issues related to storing and managing authentication credentials in the Legate Ruby library.
|
|
4
|
+
|
|
5
|
+
## Common Credential Storage Issues
|
|
6
|
+
|
|
7
|
+
### Environment Variable Resolution
|
|
8
|
+
|
|
9
|
+
**Symptoms:**
|
|
10
|
+
- "Environment variable not found" errors
|
|
11
|
+
- Authentication failing despite credentials being set
|
|
12
|
+
- Credentials not being loaded correctly
|
|
13
|
+
|
|
14
|
+
**Possible Causes and Solutions:**
|
|
15
|
+
|
|
16
|
+
1. **Missing Environment Variables**
|
|
17
|
+
```ruby
|
|
18
|
+
# Problem: Environment variable referenced but not set
|
|
19
|
+
|
|
20
|
+
# Solution: Ensure environment variables are set before running
|
|
21
|
+
# In your shell:
|
|
22
|
+
export CLIENT_ID="your-client-id"
|
|
23
|
+
export CLIENT_SECRET="your-client-secret"
|
|
24
|
+
|
|
25
|
+
# In Ruby, verify environment variables are set
|
|
26
|
+
if ENV['CLIENT_ID'].nil? || ENV['CLIENT_SECRET'].nil?
|
|
27
|
+
puts "ERROR: Required environment variables are not set"
|
|
28
|
+
exit 1
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Then use in credential
|
|
32
|
+
credential = Legate::Auth::Credential.new(
|
|
33
|
+
auth_type: :oauth2,
|
|
34
|
+
client_id: 'ENV:CLIENT_ID',
|
|
35
|
+
client_secret: 'ENV:CLIENT_SECRET'
|
|
36
|
+
)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
2. **Incorrect Environment Variable References**
|
|
40
|
+
```ruby
|
|
41
|
+
# Problem: Incorrect prefix or format for environment variable reference
|
|
42
|
+
|
|
43
|
+
# Solution: Use the correct format with 'ENV:' prefix
|
|
44
|
+
|
|
45
|
+
# Incorrect:
|
|
46
|
+
credential = Legate::Auth::Credential.new(
|
|
47
|
+
auth_type: :oauth2,
|
|
48
|
+
client_id: ENV['CLIENT_ID'], # Direct reference, no resolution
|
|
49
|
+
client_secret: '${CLIENT_SECRET}' # Incorrect format
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
# Correct:
|
|
53
|
+
credential = Legate::Auth::Credential.new(
|
|
54
|
+
auth_type: :oauth2,
|
|
55
|
+
client_id: 'ENV:CLIENT_ID', # Proper environment variable reference
|
|
56
|
+
client_secret: 'ENV:CLIENT_SECRET'
|
|
57
|
+
)
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
3. **Environment Scope Issues**
|
|
61
|
+
```ruby
|
|
62
|
+
# Problem: Environment variables not accessible to the process
|
|
63
|
+
|
|
64
|
+
# Solution: Verify environment variables are in the correct scope
|
|
65
|
+
|
|
66
|
+
# Check if variables are accessible
|
|
67
|
+
puts "CLIENT_ID: #{ENV['CLIENT_ID'] ? 'Set' : 'Not set'}"
|
|
68
|
+
puts "CLIENT_SECRET: #{ENV['CLIENT_SECRET'] ? 'Set' : 'Not set'}"
|
|
69
|
+
|
|
70
|
+
# For containerized applications, ensure variables are passed to the container
|
|
71
|
+
# docker run -e CLIENT_ID -e CLIENT_SECRET my-legate-app
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Secure Credential Storage
|
|
75
|
+
|
|
76
|
+
**Symptoms:**
|
|
77
|
+
- Security warnings about hardcoded credentials
|
|
78
|
+
- Credentials exposed in logs or errors
|
|
79
|
+
- Unauthorized access to sensitive credentials
|
|
80
|
+
|
|
81
|
+
**Possible Causes and Solutions:**
|
|
82
|
+
|
|
83
|
+
1. **Hardcoded Credentials**
|
|
84
|
+
```ruby
|
|
85
|
+
# Problem: Credentials hardcoded in source code
|
|
86
|
+
|
|
87
|
+
# Solution: Use environment variables or secure credential stores
|
|
88
|
+
|
|
89
|
+
# Incorrect (avoid this):
|
|
90
|
+
credential = Legate::Auth::Credential.new(
|
|
91
|
+
auth_type: :oauth2,
|
|
92
|
+
client_id: 'actual-client-id-123',
|
|
93
|
+
client_secret: 'actual-client-secret-456'
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
# Better:
|
|
97
|
+
credential = Legate::Auth::Credential.new(
|
|
98
|
+
auth_type: :oauth2,
|
|
99
|
+
client_id: 'ENV:CLIENT_ID',
|
|
100
|
+
client_secret: 'ENV:CLIENT_SECRET'
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
# Even better - resolve the secret from your own secrets manager
|
|
104
|
+
# (e.g. Vault, AWS Secrets Manager) before constructing the credential:
|
|
105
|
+
client_secret = MySecretsClient.fetch('oauth2_client_secret')
|
|
106
|
+
|
|
107
|
+
credential = Legate::Auth::Credential.new(
|
|
108
|
+
auth_type: :oauth2,
|
|
109
|
+
client_id: 'ENV:CLIENT_ID',
|
|
110
|
+
client_secret: client_secret
|
|
111
|
+
)
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
2. **Credential Logging**
|
|
115
|
+
```ruby
|
|
116
|
+
# Problem: Sensitive credentials appear in logs
|
|
117
|
+
|
|
118
|
+
# Solution: Implement secure logging and avoid logging credentials
|
|
119
|
+
|
|
120
|
+
# Unsafe:
|
|
121
|
+
logger.info "Using credential: #{credential.to_h}"
|
|
122
|
+
|
|
123
|
+
# Better:
|
|
124
|
+
logger.info "Using credential type: #{credential.auth_type}"
|
|
125
|
+
|
|
126
|
+
# For debugging, sanitize sensitive fields:
|
|
127
|
+
safe_cred = credential.to_h
|
|
128
|
+
safe_cred[:client_secret] = "[REDACTED]" if safe_cred[:client_secret]
|
|
129
|
+
safe_cred[:api_key] = "[REDACTED]" if safe_cred[:api_key]
|
|
130
|
+
logger.debug "Using credential (sanitized): #{safe_cred}"
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
3. **Insecure Storage**
|
|
134
|
+
```ruby
|
|
135
|
+
# Problem: Credentials stored insecurely on disk
|
|
136
|
+
|
|
137
|
+
# Solution: Use proper encryption for saved credentials
|
|
138
|
+
|
|
139
|
+
# Incorrect - storing credentials in plain text:
|
|
140
|
+
File.write('creds.json', JSON.dump(credential.to_h))
|
|
141
|
+
|
|
142
|
+
# Better - use the opt-in Encryption module (module methods, not instances).
|
|
143
|
+
# Requires the rbnacl gem and a Base64 key (see generate_key).
|
|
144
|
+
require 'legate/auth/encryption'
|
|
145
|
+
|
|
146
|
+
key = ENV['LEGATE_AUTH_ENCRYPTION_KEY'] # Base64-encoded; or Encryption.generate_key
|
|
147
|
+
encrypted_data = Legate::Auth::Encryption.encrypt(JSON.dump(credential.to_h), key)
|
|
148
|
+
|
|
149
|
+
# Store encrypted data
|
|
150
|
+
File.write('creds.enc', encrypted_data)
|
|
151
|
+
|
|
152
|
+
# Later, to decrypt:
|
|
153
|
+
encrypted_data = File.read('creds.enc')
|
|
154
|
+
json_data = Legate::Auth::Encryption.decrypt(encrypted_data, key)
|
|
155
|
+
cred_hash = JSON.parse(json_data)
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Session Storage Issues
|
|
159
|
+
|
|
160
|
+
**Symptoms:**
|
|
161
|
+
- Tokens disappearing between requests
|
|
162
|
+
- "Session not found" errors
|
|
163
|
+
- Authentication state not persisting
|
|
164
|
+
|
|
165
|
+
**Possible Causes and Solutions:**
|
|
166
|
+
|
|
167
|
+
1. **Session Configuration**
|
|
168
|
+
```ruby
|
|
169
|
+
# Problem: Session not configured properly
|
|
170
|
+
|
|
171
|
+
# Solution: Configure session with proper settings
|
|
172
|
+
|
|
173
|
+
# In a Sinatra app:
|
|
174
|
+
use Rack::Session::Cookie,
|
|
175
|
+
key: 'legate.session',
|
|
176
|
+
secret: ENV['SESSION_SECRET'],
|
|
177
|
+
expire_after: 86400 # 1 day in seconds
|
|
178
|
+
|
|
179
|
+
# In a Rails app:
|
|
180
|
+
# config/initializers/session_store.rb
|
|
181
|
+
Rails.application.config.session_store :cookie_store,
|
|
182
|
+
key: '_legate_session',
|
|
183
|
+
expire_after: 1.day
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
2. **Token Store Configuration**
|
|
187
|
+
```ruby
|
|
188
|
+
# Problem: Token store not properly initialized
|
|
189
|
+
|
|
190
|
+
# Solution: Ensure token store is initialized with the session
|
|
191
|
+
|
|
192
|
+
# Incorrect:
|
|
193
|
+
token_store = Legate::Auth::TokenStore.new # Missing session service
|
|
194
|
+
|
|
195
|
+
# Correct: pass the session service positionally
|
|
196
|
+
token_store = Legate::Auth::TokenStore.new(session_service)
|
|
197
|
+
|
|
198
|
+
# Note: TokenStore.new takes a single positional argument (the session
|
|
199
|
+
# service). It does not accept namespace/serializer keyword options; tokens
|
|
200
|
+
# are stored under the fixed 'auth' scope.
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
3. **Session Expiration**
|
|
204
|
+
```ruby
|
|
205
|
+
# Problem: Session expires before tokens are used
|
|
206
|
+
|
|
207
|
+
# Solution: Adjust session timeout or implement refresh mechanism
|
|
208
|
+
|
|
209
|
+
# Extend session timeout
|
|
210
|
+
use Rack::Session::Cookie,
|
|
211
|
+
key: 'legate.session',
|
|
212
|
+
secret: ENV['SESSION_SECRET'],
|
|
213
|
+
expire_after: 604800 # 1 week in seconds
|
|
214
|
+
|
|
215
|
+
# Or implement periodic session refresh
|
|
216
|
+
before do
|
|
217
|
+
# Refresh session on each request
|
|
218
|
+
session[:last_activity] = Time.now.to_i
|
|
219
|
+
end
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
## Encryption-Related Issues
|
|
223
|
+
|
|
224
|
+
**Symptoms:**
|
|
225
|
+
- "Decryption failed" errors
|
|
226
|
+
- "Invalid encryption key" errors
|
|
227
|
+
- Tokens cannot be retrieved from storage
|
|
228
|
+
|
|
229
|
+
**Possible Causes and Solutions:**
|
|
230
|
+
|
|
231
|
+
> **Note:** `Legate::Auth::Encryption` is an opt-in module (not wired into `TokenStore`) and is not instantiable — call its module methods directly. It uses rbnacl (libsodium SecretBox) and requires the `rbnacl` gem. Decryption failures raise `ArgumentError`; a missing `rbnacl` gem raises `LoadError`. There is no `EncryptionError` class and no algorithm option.
|
|
232
|
+
|
|
233
|
+
1. **Encryption Key Issues**
|
|
234
|
+
```ruby
|
|
235
|
+
# Problem: Missing or inconsistent encryption key
|
|
236
|
+
|
|
237
|
+
# Solution: Generate a Base64 key with generate_key and keep it consistent
|
|
238
|
+
require 'legate/auth/encryption'
|
|
239
|
+
encryption_key = Legate::Auth::Encryption.generate_key # Base64-encoded
|
|
240
|
+
puts "Generated encryption key: #{encryption_key}"
|
|
241
|
+
|
|
242
|
+
# Store this key securely (e.g. LEGATE_AUTH_ENCRYPTION_KEY) and reuse it
|
|
243
|
+
# consistently across app instances:
|
|
244
|
+
encrypted = Legate::Auth::Encryption.encrypt(data, ENV['LEGATE_AUTH_ENCRYPTION_KEY'])
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
2. **Wrong Key or Tampered Data**
|
|
248
|
+
```ruby
|
|
249
|
+
# Problem: Decryption fails (wrong key, bad format, or tampered ciphertext)
|
|
250
|
+
|
|
251
|
+
# Solution: Decryption failures raise ArgumentError; handle them explicitly
|
|
252
|
+
begin
|
|
253
|
+
plaintext = Legate::Auth::Encryption.decrypt(encrypted_data, key)
|
|
254
|
+
rescue ArgumentError => e
|
|
255
|
+
puts "Decryption failed: #{e.message}"
|
|
256
|
+
end
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
3. **Key Rotation**
|
|
260
|
+
```ruby
|
|
261
|
+
# Problem: Need to rotate encryption keys without losing data
|
|
262
|
+
|
|
263
|
+
# Solution: Implement key rotation with backward compatibility
|
|
264
|
+
def get_token_with_key_rotation(encrypted_data, key, legacy_key = nil)
|
|
265
|
+
begin
|
|
266
|
+
# Try with current key first
|
|
267
|
+
return Legate::Auth::Encryption.decrypt(encrypted_data, key)
|
|
268
|
+
rescue ArgumentError
|
|
269
|
+
raise unless legacy_key
|
|
270
|
+
# Try with legacy key, then re-encrypt with the new key
|
|
271
|
+
decrypted = Legate::Auth::Encryption.decrypt(encrypted_data, legacy_key)
|
|
272
|
+
new_encrypted = Legate::Auth::Encryption.encrypt(decrypted, key)
|
|
273
|
+
save_encrypted_data(new_encrypted)
|
|
274
|
+
decrypted
|
|
275
|
+
end
|
|
276
|
+
end
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
## Service Account Key Issues
|
|
280
|
+
|
|
281
|
+
**Symptoms:**
|
|
282
|
+
- "Invalid key format" errors
|
|
283
|
+
- JSON parsing errors with service account keys
|
|
284
|
+
- Authentication failing with service accounts
|
|
285
|
+
|
|
286
|
+
**Possible Causes and Solutions:**
|
|
287
|
+
|
|
288
|
+
1. **JSON Key Format Issues**
|
|
289
|
+
```ruby
|
|
290
|
+
# Problem: Service account key JSON is malformed
|
|
291
|
+
|
|
292
|
+
# Solution: Ensure JSON is valid and complete
|
|
293
|
+
|
|
294
|
+
# Verify key format
|
|
295
|
+
begin
|
|
296
|
+
key_data = JSON.parse(File.read('service-account-key.json'))
|
|
297
|
+
required_fields = ['type', 'project_id', 'private_key_id', 'private_key',
|
|
298
|
+
'client_email', 'client_id', 'auth_uri', 'token_uri']
|
|
299
|
+
|
|
300
|
+
missing = required_fields - key_data.keys
|
|
301
|
+
if missing.any?
|
|
302
|
+
puts "Invalid key file: Missing fields: #{missing.join(', ')}"
|
|
303
|
+
else
|
|
304
|
+
puts "Key file appears valid"
|
|
305
|
+
end
|
|
306
|
+
rescue JSON::ParserError => e
|
|
307
|
+
puts "Invalid JSON: #{e.message}"
|
|
308
|
+
end
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
2. **Key Environment Variable Issues**
|
|
312
|
+
```ruby
|
|
313
|
+
# Problem: Service account key environment variable is malformed
|
|
314
|
+
|
|
315
|
+
# Solution: Ensure JSON is properly escaped in the environment variable
|
|
316
|
+
|
|
317
|
+
# The environment variable must contain valid JSON:
|
|
318
|
+
# export SERVICE_ACCOUNT_JSON='{"type":"service_account","project_id":"...","private_key":"...","client_email":"..."}'
|
|
319
|
+
|
|
320
|
+
# In your code (pass the raw JSON string via service_account_key, not a
|
|
321
|
+
# parsed Hash; the attribute is service_account_key, not service_account_json):
|
|
322
|
+
service_account_json = ENV['SERVICE_ACCOUNT_JSON']
|
|
323
|
+
begin
|
|
324
|
+
JSON.parse(service_account_json) # validate it parses
|
|
325
|
+
rescue JSON::ParserError => e
|
|
326
|
+
puts "Invalid service account JSON in environment variable: #{e.message}"
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
credential = Legate::Auth::Credential.new(
|
|
330
|
+
auth_type: :service_account,
|
|
331
|
+
service_account_key: service_account_json # raw JSON string
|
|
332
|
+
)
|
|
333
|
+
|
|
334
|
+
# Or reference the env var directly:
|
|
335
|
+
# service_account_key: 'ENV:SERVICE_ACCOUNT_JSON'
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
3. **Key File Permissions**
|
|
339
|
+
```ruby
|
|
340
|
+
# Problem: Key file has incorrect permissions
|
|
341
|
+
|
|
342
|
+
# Solution: Set restrictive file permissions
|
|
343
|
+
|
|
344
|
+
# In your terminal:
|
|
345
|
+
chmod 0600 service-account-key.json # Owner read/write only
|
|
346
|
+
|
|
347
|
+
# In your code, check permissions:
|
|
348
|
+
key_file = 'service-account-key.json'
|
|
349
|
+
permissions = File.stat(key_file).mode & 0777
|
|
350
|
+
|
|
351
|
+
if permissions > 0600
|
|
352
|
+
puts "WARNING: Key file permissions are too permissive: #{permissions.to_s(8)}"
|
|
353
|
+
puts "Consider restricting permissions with: chmod 0600 #{key_file}"
|
|
354
|
+
end
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
## Multi-Environment Configuration
|
|
358
|
+
|
|
359
|
+
**Symptoms:**
|
|
360
|
+
- Credentials work in development but fail in production
|
|
361
|
+
- Different behavior across environments
|
|
362
|
+
- Configuration errors in deployed applications
|
|
363
|
+
|
|
364
|
+
**Possible Causes and Solutions:**
|
|
365
|
+
|
|
366
|
+
1. **Environment-Specific Credentials**
|
|
367
|
+
```ruby
|
|
368
|
+
# Problem: Using the same credentials across environments
|
|
369
|
+
|
|
370
|
+
# Solution: Use environment-specific credential configuration
|
|
371
|
+
|
|
372
|
+
# Load different credentials based on environment
|
|
373
|
+
environment = ENV['RACK_ENV'] || 'development'
|
|
374
|
+
|
|
375
|
+
case environment
|
|
376
|
+
when 'development'
|
|
377
|
+
client_id = 'ENV:DEV_CLIENT_ID'
|
|
378
|
+
client_secret = 'ENV:DEV_CLIENT_SECRET'
|
|
379
|
+
when 'production'
|
|
380
|
+
client_id = 'ENV:PROD_CLIENT_ID'
|
|
381
|
+
client_secret = 'ENV:PROD_CLIENT_SECRET'
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
credential = Legate::Auth::Credential.new(
|
|
385
|
+
auth_type: :oauth2,
|
|
386
|
+
client_id: client_id,
|
|
387
|
+
client_secret: client_secret
|
|
388
|
+
)
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
2. **Configuration Loading Issues**
|
|
392
|
+
```ruby
|
|
393
|
+
# Problem: Configuration not loaded in the correct order
|
|
394
|
+
|
|
395
|
+
# Solution: Establish clear configuration loading order
|
|
396
|
+
|
|
397
|
+
# Example configuration loader
|
|
398
|
+
def load_configuration
|
|
399
|
+
# 1. Load defaults
|
|
400
|
+
config = {
|
|
401
|
+
oauth2: {
|
|
402
|
+
client_id: nil,
|
|
403
|
+
client_secret: nil
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
# 2. Override with environment-specific configuration file
|
|
408
|
+
env_config_file = "config/#{ENV['RACK_ENV'] || 'development'}.yml"
|
|
409
|
+
if File.exist?(env_config_file)
|
|
410
|
+
env_config = YAML.load_file(env_config_file)
|
|
411
|
+
config.deep_merge!(env_config)
|
|
412
|
+
end
|
|
413
|
+
|
|
414
|
+
# 3. Override with environment variables
|
|
415
|
+
config[:oauth2][:client_id] = ENV['OAUTH_CLIENT_ID'] if ENV['OAUTH_CLIENT_ID']
|
|
416
|
+
config[:oauth2][:client_secret] = ENV['OAUTH_CLIENT_SECRET'] if ENV['OAUTH_CLIENT_SECRET']
|
|
417
|
+
|
|
418
|
+
config
|
|
419
|
+
end
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
3. **Environment Variable Names**
|
|
423
|
+
```ruby
|
|
424
|
+
# Problem: Inconsistent environment variable naming across environments
|
|
425
|
+
|
|
426
|
+
# Solution: Standardize environment variable names
|
|
427
|
+
|
|
428
|
+
# Create a mapping between standard names and environment-specific names
|
|
429
|
+
env_var_mapping = {
|
|
430
|
+
development: {
|
|
431
|
+
client_id: 'DEV_CLIENT_ID',
|
|
432
|
+
client_secret: 'DEV_CLIENT_SECRET'
|
|
433
|
+
},
|
|
434
|
+
production: {
|
|
435
|
+
client_id: 'PROD_CLIENT_ID',
|
|
436
|
+
client_secret: 'PROD_CLIENT_SECRET'
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
environment = (ENV['RACK_ENV'] || 'development').to_sym
|
|
441
|
+
mapping = env_var_mapping[environment]
|
|
442
|
+
|
|
443
|
+
client_id = "ENV:#{mapping[:client_id]}"
|
|
444
|
+
client_secret = "ENV:#{mapping[:client_secret]}"
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
## Debugging Techniques
|
|
448
|
+
|
|
449
|
+
### Credential Validation
|
|
450
|
+
|
|
451
|
+
For debugging credential issues:
|
|
452
|
+
|
|
453
|
+
```ruby
|
|
454
|
+
# Validate a credential
|
|
455
|
+
def validate_credential(credential)
|
|
456
|
+
puts "Credential type: #{credential.auth_type}"
|
|
457
|
+
|
|
458
|
+
case credential.auth_type
|
|
459
|
+
when :oauth2, :oidc
|
|
460
|
+
required = [:client_id, :client_secret]
|
|
461
|
+
when :api_key
|
|
462
|
+
required = [:api_key]
|
|
463
|
+
when :service_account, :google_service_account
|
|
464
|
+
# Either service_account_key (raw JSON string) or service_account_key_file
|
|
465
|
+
required = [:service_account_key]
|
|
466
|
+
end
|
|
467
|
+
|
|
468
|
+
missing = required.select { |attr| credential[attr].nil? }
|
|
469
|
+
if missing.any?
|
|
470
|
+
puts "ERROR: Missing required attributes: #{missing.join(', ')}"
|
|
471
|
+
else
|
|
472
|
+
puts "Credential appears valid"
|
|
473
|
+
end
|
|
474
|
+
|
|
475
|
+
# Check environment variable resolution
|
|
476
|
+
required.each do |attr|
|
|
477
|
+
value = credential[attr, resolve_env: false]
|
|
478
|
+
if value.is_a?(String) && value.start_with?('ENV:')
|
|
479
|
+
env_var = value.sub(/^ENV:/, '')
|
|
480
|
+
if ENV[env_var].nil?
|
|
481
|
+
puts "WARNING: Environment variable not set: #{env_var}"
|
|
482
|
+
else
|
|
483
|
+
puts "Environment variable resolved: #{env_var}"
|
|
484
|
+
end
|
|
485
|
+
end
|
|
486
|
+
end
|
|
487
|
+
end
|
|
488
|
+
|
|
489
|
+
# Usage
|
|
490
|
+
credential = Legate::Auth::Credential.new(
|
|
491
|
+
auth_type: :oauth2,
|
|
492
|
+
client_id: 'ENV:CLIENT_ID',
|
|
493
|
+
client_secret: 'ENV:CLIENT_SECRET'
|
|
494
|
+
)
|
|
495
|
+
|
|
496
|
+
validate_credential(credential)
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
### TokenStore Debugging
|
|
500
|
+
|
|
501
|
+
For debugging token store issues:
|
|
502
|
+
|
|
503
|
+
`TokenStore` does not expose a method to enumerate keys (there is no `all_keys`). Debug a specific token by looking it up by its key:
|
|
504
|
+
|
|
505
|
+
```ruby
|
|
506
|
+
# Debug a single token by key
|
|
507
|
+
def debug_token(token_store, key)
|
|
508
|
+
token = token_store.get(key)
|
|
509
|
+
if token
|
|
510
|
+
puts "Token type: #{token.auth_type}"
|
|
511
|
+
puts "Expires at: #{token[:expires_at]}"
|
|
512
|
+
puts "Expired?: #{token.expired?}"
|
|
513
|
+
puts "Refreshable: #{token.refreshable? ? 'Yes' : 'No'}" # use refreshable?, not can_refresh?
|
|
514
|
+
else
|
|
515
|
+
puts "Token not found or expired for key: #{key}"
|
|
516
|
+
end
|
|
517
|
+
rescue => e
|
|
518
|
+
puts "Error retrieving token: #{e.message}"
|
|
519
|
+
end
|
|
520
|
+
|
|
521
|
+
# Usage
|
|
522
|
+
token_store = Legate::Auth::TokenStore.new(session_service)
|
|
523
|
+
debug_token(token_store, 'auth_<hash>')
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
## When to Contact Support
|
|
527
|
+
|
|
528
|
+
If you've tried all the solutions and still encounter issues:
|
|
529
|
+
|
|
530
|
+
1. **Check for Library Updates**:
|
|
531
|
+
- Verify you're using the latest version of the Legate Ruby library
|
|
532
|
+
- Check the changelog for fixes related to credential storage
|
|
533
|
+
|
|
534
|
+
2. **Debug Information to Collect**:
|
|
535
|
+
- Legate Ruby version
|
|
536
|
+
- Ruby version and platform
|
|
537
|
+
- Environment (development, production)
|
|
538
|
+
- Error messages (with sensitive data redacted)
|
|
539
|
+
- Steps to reproduce the issue
|
|
540
|
+
|
|
541
|
+
3. **Contact Legate Support**:
|
|
542
|
+
- Provide collected debug information
|
|
543
|
+
- Describe your credential storage setup
|
|
544
|
+
- Share reproduction steps
|
|
545
|
+
|
|
546
|
+
## Next Steps
|
|
547
|
+
|
|
548
|
+
- [Token Lifecycle Management](../guides/token_lifecycle): Advanced token management techniques
|
|
549
|
+
- [OAuth2 Troubleshooting](./oauth2_issues): For OAuth2-specific authentication issues
|
|
550
|
+
- [Environment Variable Management](./environment_variables): Best practices for handling environment variables
|