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,435 @@
|
|
|
1
|
+
# Migrating from Earlier Versions
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
This guide helps you migrate your Legate Ruby applications from earlier versions to the current version, focusing on authentication-related changes.
|
|
6
|
+
|
|
7
|
+
## Migrating from v0.4.x
|
|
8
|
+
|
|
9
|
+
### Breaking Changes
|
|
10
|
+
|
|
11
|
+
Version 0.5.0 introduced a new authentication system that replaces the previous authentication mechanisms. The following breaking changes were introduced:
|
|
12
|
+
|
|
13
|
+
1. **Unified Authentication System**: Replaced multiple authentication strategies with a single unified system
|
|
14
|
+
2. **Authentication Schemes**: Introduced the concept of authentication schemes to define how APIs expect credentials
|
|
15
|
+
3. **Token Management**: Added comprehensive token lifecycle management
|
|
16
|
+
4. **Interactive Authentication**: Implemented Fiber-based control flow for interactive authentication
|
|
17
|
+
|
|
18
|
+
### Migration Steps
|
|
19
|
+
|
|
20
|
+
#### Step 1: Update Dependencies
|
|
21
|
+
|
|
22
|
+
```ruby
|
|
23
|
+
# Old (v0.4.x)
|
|
24
|
+
gem 'legate', '~> 0.4.0'
|
|
25
|
+
|
|
26
|
+
# New (v0.5.x)
|
|
27
|
+
gem 'legate', '~> 0.5.0'
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
#### Step 2: Migrate API Key Authentication
|
|
31
|
+
|
|
32
|
+
```ruby
|
|
33
|
+
# Old (v0.4.x)
|
|
34
|
+
toolset = Legate::Tool::OpenAPIToolset.new(
|
|
35
|
+
spec_path: 'path/to/openapi_spec.json',
|
|
36
|
+
api_key: ENV['API_KEY']
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
# New (v0.5.x)
|
|
40
|
+
# ApiKey.new takes no arguments; location/name live on the credential
|
|
41
|
+
api_key_scheme = Legate::Auth::Schemes::ApiKey.new
|
|
42
|
+
|
|
43
|
+
api_key_credential = Legate::Auth::Credential.new(
|
|
44
|
+
auth_type: :api_key,
|
|
45
|
+
api_key: ENV['API_KEY'],
|
|
46
|
+
location: 'header',
|
|
47
|
+
name: 'X-API-Key'
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
# Attach the scheme/credential to an outbound connection
|
|
51
|
+
connection = Legate::Auth.create_connection('https://api.example.com',
|
|
52
|
+
scheme: api_key_scheme,
|
|
53
|
+
credential: api_key_credential
|
|
54
|
+
)
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
#### Step 3: Migrate OAuth2 Authentication
|
|
58
|
+
|
|
59
|
+
```ruby
|
|
60
|
+
# Old (v0.4.x)
|
|
61
|
+
toolset = Legate::Tool::OpenAPIToolset.new(
|
|
62
|
+
spec_path: 'path/to/openapi_spec.json',
|
|
63
|
+
oauth2_client_id: ENV['OAUTH2_CLIENT_ID'],
|
|
64
|
+
oauth2_client_secret: ENV['OAUTH2_CLIENT_SECRET'],
|
|
65
|
+
oauth2_auth_url: 'https://auth.example.com/authorize',
|
|
66
|
+
oauth2_token_url: 'https://auth.example.com/token',
|
|
67
|
+
oauth2_scopes: ['read', 'write']
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
# New (v0.5.x) — note: authorization_url (not auth_url)
|
|
71
|
+
oauth2_scheme = Legate::Auth::Schemes::OAuth2.new(
|
|
72
|
+
authorization_url: 'https://auth.example.com/authorize',
|
|
73
|
+
token_url: 'https://auth.example.com/token',
|
|
74
|
+
scopes: ['read', 'write']
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
oauth2_credential = Legate::Auth::Credential.new(
|
|
78
|
+
auth_type: :oauth2,
|
|
79
|
+
client_id: ENV['OAUTH2_CLIENT_ID'],
|
|
80
|
+
client_secret: ENV['OAUTH2_CLIENT_SECRET']
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
# Drive the flow via Config (build_authorization_uri / exchange_token)
|
|
84
|
+
config = Legate::Auth::Config.new(scheme: oauth2_scheme, credential: oauth2_credential)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
#### Step 4: Migrate Custom Function Tools
|
|
88
|
+
|
|
89
|
+
```ruby
|
|
90
|
+
# Old (v0.4.x)
|
|
91
|
+
class MyApiTool < Legate::Tool::FunctionTool
|
|
92
|
+
def call(context, **params)
|
|
93
|
+
api_key = ENV['API_KEY']
|
|
94
|
+
response = Excon.get(
|
|
95
|
+
'https://api.example.com/data',
|
|
96
|
+
headers: { 'X-API-Key' => api_key }
|
|
97
|
+
)
|
|
98
|
+
JSON.parse(response.body)
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# New (v0.5.x)
|
|
103
|
+
class MyApiTool < Legate::Tool
|
|
104
|
+
tool_description 'A tool that interacts with an API'
|
|
105
|
+
|
|
106
|
+
def perform_execution(params, context)
|
|
107
|
+
api_key_scheme = Legate::Auth::Schemes::ApiKey.new
|
|
108
|
+
api_key_credential = Legate::Auth::Credential.new(
|
|
109
|
+
auth_type: :api_key,
|
|
110
|
+
api_key: ENV['API_KEY'],
|
|
111
|
+
location: 'header',
|
|
112
|
+
name: 'X-API-Key'
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
connection = Legate::Auth.create_connection(
|
|
116
|
+
'https://api.example.com',
|
|
117
|
+
scheme: api_key_scheme,
|
|
118
|
+
credential: api_key_credential
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
response = connection.get(path: '/data')
|
|
122
|
+
{ status: :success, result: JSON.parse(response.body) }
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
#### Step 5: Migrate Interactive Authentication Flows
|
|
128
|
+
|
|
129
|
+
```ruby
|
|
130
|
+
# Old (v0.4.x)
|
|
131
|
+
def call(context, **params)
|
|
132
|
+
oauth2_client = OAuth2::Client.new(
|
|
133
|
+
ENV['OAUTH2_CLIENT_ID'],
|
|
134
|
+
ENV['OAUTH2_CLIENT_SECRET'],
|
|
135
|
+
site: 'https://auth.example.com',
|
|
136
|
+
authorize_url: '/authorize',
|
|
137
|
+
token_url: '/token'
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
if context.session[:oauth2_access_token]
|
|
141
|
+
# Use cached token
|
|
142
|
+
access_token = OAuth2::AccessToken.from_hash(
|
|
143
|
+
oauth2_client,
|
|
144
|
+
JSON.parse(context.session[:oauth2_access_token])
|
|
145
|
+
)
|
|
146
|
+
else
|
|
147
|
+
# Request authorization
|
|
148
|
+
auth_url = oauth2_client.auth_code.authorize_url(
|
|
149
|
+
redirect_uri: 'http://localhost:8080/callback',
|
|
150
|
+
scope: 'read write'
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
# Yield for user interaction
|
|
154
|
+
auth_code = yield(auth_url)
|
|
155
|
+
|
|
156
|
+
# Exchange code for token
|
|
157
|
+
access_token = oauth2_client.auth_code.get_token(
|
|
158
|
+
auth_code,
|
|
159
|
+
redirect_uri: 'http://localhost:8080/callback'
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
# Cache the token
|
|
163
|
+
context.session[:oauth2_access_token] = access_token.to_hash.to_json
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# Use the token
|
|
167
|
+
response = access_token.get('/api/data')
|
|
168
|
+
JSON.parse(response.body)
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
# New (v0.5.x)
|
|
172
|
+
# Interactive flows are driven by Config (build the auth URI, then exchange the
|
|
173
|
+
# code) or, inside a tool, via context.with_authentication which yields the auth
|
|
174
|
+
# request to your application. A direct Config-based example:
|
|
175
|
+
def perform_execution(params, context)
|
|
176
|
+
scheme = Legate::Auth::Schemes::OAuth2.new(
|
|
177
|
+
authorization_url: 'https://auth.example.com/authorize',
|
|
178
|
+
token_url: 'https://auth.example.com/token',
|
|
179
|
+
scopes: %w[read write]
|
|
180
|
+
)
|
|
181
|
+
credential = Legate::Auth::Credential.new(
|
|
182
|
+
auth_type: :oauth2,
|
|
183
|
+
client_id: ENV['OAUTH2_CLIENT_ID'],
|
|
184
|
+
client_secret: ENV['OAUTH2_CLIENT_SECRET']
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
config = Legate::Auth::Config.new(scheme: scheme, credential: credential)
|
|
188
|
+
state = SecureRandom.hex(16)
|
|
189
|
+
auth_uri = config.build_authorization_uri('http://localhost:8080/callback', state)
|
|
190
|
+
|
|
191
|
+
# Yield the auth URI to the caller for the user to complete, then on the
|
|
192
|
+
# callback set config.response_uri and exchange:
|
|
193
|
+
# config.response_uri = '...callback?code=...&state=...'
|
|
194
|
+
# token = scheme.exchange_token(config, credential)
|
|
195
|
+
|
|
196
|
+
{ status: :success, result: { auth_uri: auth_uri } }
|
|
197
|
+
end
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
#### Step 6: Migrate Session Storage
|
|
201
|
+
|
|
202
|
+
```ruby
|
|
203
|
+
# Sessions are now always in-memory (Redis session service has been removed)
|
|
204
|
+
session_service = Legate::SessionService::InMemory.new
|
|
205
|
+
|
|
206
|
+
# Create a token store for authentication (positional argument)
|
|
207
|
+
token_store = Legate::Auth::TokenStore.new(session_service)
|
|
208
|
+
|
|
209
|
+
# Create a token manager (positional token store; optional positional config Hash).
|
|
210
|
+
# The TokenManager does not take a scheme — the scheme is passed per call to
|
|
211
|
+
# get_token(scheme, credential).
|
|
212
|
+
token_manager = Legate::Auth::TokenManager.new(token_store)
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
## Migrating from v0.3.x
|
|
216
|
+
|
|
217
|
+
### Major Changes from v0.3.x to v0.5.x
|
|
218
|
+
|
|
219
|
+
Version 0.3.x had limited authentication support, using direct API key or OAuth2 configuration. Version 0.5.x introduces a comprehensive authentication system with the following improvements:
|
|
220
|
+
|
|
221
|
+
1. **Unified Authentication**: A single authentication system for all authentication methods
|
|
222
|
+
2. **Improved Security**: Scoped token storage, optional opt-in at-rest encryption, and SSRF-guarded auth URLs
|
|
223
|
+
3. **Interactive Flows**: Support for OAuth2, OIDC, and custom interactive flows
|
|
224
|
+
4. **Token Lifecycle**: Automatic token refresh and invalidation
|
|
225
|
+
5. **Middleware Support**: Excon middleware for authentication
|
|
226
|
+
|
|
227
|
+
### Migration Steps
|
|
228
|
+
|
|
229
|
+
#### Step 1: Update Dependencies
|
|
230
|
+
|
|
231
|
+
```ruby
|
|
232
|
+
# Old (v0.3.x)
|
|
233
|
+
gem 'legate', '~> 0.3.0'
|
|
234
|
+
|
|
235
|
+
# New (v0.5.x)
|
|
236
|
+
gem 'legate', '~> 0.5.0'
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
#### Step 2: Migrate Basic Authentication
|
|
240
|
+
|
|
241
|
+
```ruby
|
|
242
|
+
# Old (v0.3.x)
|
|
243
|
+
toolset = Legate::Tool::OpenAPIToolset.new(
|
|
244
|
+
spec_path: 'path/to/openapi_spec.json',
|
|
245
|
+
auth: {
|
|
246
|
+
type: 'basic',
|
|
247
|
+
username: ENV['USERNAME'],
|
|
248
|
+
password: ENV['PASSWORD']
|
|
249
|
+
}
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
# New (v0.5.x)
|
|
253
|
+
# For Basic Auth, use HTTP Bearer with Basic encoded token
|
|
254
|
+
require 'base64'
|
|
255
|
+
basic_token = Base64.strict_encode64("#{ENV['USERNAME']}:#{ENV['PASSWORD']}")
|
|
256
|
+
|
|
257
|
+
bearer_scheme = Legate::Auth::Schemes::HTTPBearer.new
|
|
258
|
+
|
|
259
|
+
bearer_credential = Legate::Auth::Credential.new(
|
|
260
|
+
auth_type: :http_bearer,
|
|
261
|
+
bearer_token: "Basic #{basic_token}"
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
connection = Legate::Auth.create_connection('https://api.example.com',
|
|
265
|
+
scheme: bearer_scheme,
|
|
266
|
+
credential: bearer_credential
|
|
267
|
+
)
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
#### Step 3: Migrate API Key Authentication
|
|
271
|
+
|
|
272
|
+
```ruby
|
|
273
|
+
# Old (v0.3.x)
|
|
274
|
+
toolset = Legate::Tool::OpenAPIToolset.new(
|
|
275
|
+
spec_path: 'path/to/openapi_spec.json',
|
|
276
|
+
auth: {
|
|
277
|
+
type: 'api_key',
|
|
278
|
+
in: 'header',
|
|
279
|
+
name: 'X-API-Key',
|
|
280
|
+
value: ENV['API_KEY']
|
|
281
|
+
}
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
# New (v0.5.x) — ApiKey.new takes no args; location/name on the credential
|
|
285
|
+
api_key_scheme = Legate::Auth::Schemes::ApiKey.new
|
|
286
|
+
|
|
287
|
+
api_key_credential = Legate::Auth::Credential.new(
|
|
288
|
+
auth_type: :api_key,
|
|
289
|
+
api_key: ENV['API_KEY'],
|
|
290
|
+
location: 'header',
|
|
291
|
+
name: 'X-API-Key'
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
connection = Legate::Auth.create_connection('https://api.example.com',
|
|
295
|
+
scheme: api_key_scheme,
|
|
296
|
+
credential: api_key_credential
|
|
297
|
+
)
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
## Additional Migration Notes
|
|
301
|
+
|
|
302
|
+
### Environment Variables
|
|
303
|
+
|
|
304
|
+
The new authentication system encourages the use of environment variables for credential values:
|
|
305
|
+
|
|
306
|
+
```ruby
|
|
307
|
+
# Reference environment variables with the ENV: prefix (no *_env attributes)
|
|
308
|
+
oauth2_credential = Legate::Auth::Credential.new(
|
|
309
|
+
auth_type: :oauth2,
|
|
310
|
+
client_id: 'ENV:OAUTH2_CLIENT_ID',
|
|
311
|
+
client_secret: 'ENV:OAUTH2_CLIENT_SECRET'
|
|
312
|
+
)
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
### Web UI Integration
|
|
316
|
+
|
|
317
|
+
If you're using the Legate Web UI, you'll need to update your authentication integration:
|
|
318
|
+
|
|
319
|
+
```ruby
|
|
320
|
+
# Old (v0.4.x)
|
|
321
|
+
get '/auth/callback' do
|
|
322
|
+
code = params[:code]
|
|
323
|
+
session[:auth_code] = code
|
|
324
|
+
# Close popup window
|
|
325
|
+
'<script>window.close();</script>'
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
# New (v0.5.x)
|
|
329
|
+
get '/auth/callback' do
|
|
330
|
+
# Verify state parameter
|
|
331
|
+
client_state = params[:state]
|
|
332
|
+
server_state = session[:oauth2_state]
|
|
333
|
+
halt 403, 'Invalid state parameter' unless client_state && client_state == server_state
|
|
334
|
+
|
|
335
|
+
# Get the authorization code
|
|
336
|
+
code = params[:code]
|
|
337
|
+
halt 400, 'Missing authorization code' unless code
|
|
338
|
+
|
|
339
|
+
# Store the authorization code and response URI
|
|
340
|
+
session[:auth_code] = code
|
|
341
|
+
session[:auth_response_uri] = request.url
|
|
342
|
+
|
|
343
|
+
# Close the popup window and notify the parent window
|
|
344
|
+
<<-HTML
|
|
345
|
+
<script>
|
|
346
|
+
window.opener.postMessage({ type: 'auth_callback', code: '#{code}' }, window.location.origin);
|
|
347
|
+
window.close();
|
|
348
|
+
</script>
|
|
349
|
+
HTML
|
|
350
|
+
end
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
### Helper Migration
|
|
354
|
+
|
|
355
|
+
If you've created custom helpers for authentication, you'll need to update them:
|
|
356
|
+
|
|
357
|
+
```ruby
|
|
358
|
+
# Old (v0.4.x)
|
|
359
|
+
def authenticate_api_request(api_key)
|
|
360
|
+
headers = { 'X-API-Key' => api_key }
|
|
361
|
+
# ...
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
# New (v0.5.x)
|
|
365
|
+
def authenticate_api_request(scheme, credential)
|
|
366
|
+
connection = Legate::Auth.create_connection(
|
|
367
|
+
'https://api.example.com',
|
|
368
|
+
scheme: scheme,
|
|
369
|
+
credential: credential
|
|
370
|
+
)
|
|
371
|
+
# ...
|
|
372
|
+
end
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
## Troubleshooting Migration Issues
|
|
376
|
+
|
|
377
|
+
### Missing Authentication Scheme
|
|
378
|
+
|
|
379
|
+
If a scheme is misconfigured, validation raises a `Legate::Auth::SchemeValidationError`
|
|
380
|
+
(a subclass of `ConfigurationError`). Note the auth exception classes are flat under
|
|
381
|
+
`Legate::Auth` (e.g. `SchemeValidationError`, `CredentialError`, `TokenRefreshError`) —
|
|
382
|
+
there is no `Legate::Auth::Error::*` sub-namespace.
|
|
383
|
+
|
|
384
|
+
```
|
|
385
|
+
Legate::Auth::SchemeValidationError: Invalid authentication scheme configuration
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
Make sure you've created the scheme with the required arguments:
|
|
389
|
+
|
|
390
|
+
```ruby
|
|
391
|
+
# ApiKey.new takes no arguments; location/name live on the credential
|
|
392
|
+
api_key_scheme = Legate::Auth::Schemes::ApiKey.new
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
### Missing or Invalid Credential
|
|
396
|
+
|
|
397
|
+
A missing required attribute (or an invalid `auth_type`) raises
|
|
398
|
+
`Legate::Auth::CredentialError`:
|
|
399
|
+
|
|
400
|
+
```
|
|
401
|
+
Legate::Auth::CredentialError: Missing required attributes for api_key: api_key
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
Make sure you've created the credential with `auth_type:` and the required attributes:
|
|
405
|
+
|
|
406
|
+
```ruby
|
|
407
|
+
api_key_credential = Legate::Auth::Credential.new(
|
|
408
|
+
auth_type: :api_key,
|
|
409
|
+
api_key: ENV['API_KEY']
|
|
410
|
+
)
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
### Token Refresh Failures
|
|
414
|
+
|
|
415
|
+
A failed refresh raises `Legate::Auth::TokenRefreshError`:
|
|
416
|
+
|
|
417
|
+
```
|
|
418
|
+
Legate::Auth::TokenRefreshError: Token refresh failed
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
Make sure you've configured the token store and manager correctly (positional args):
|
|
422
|
+
|
|
423
|
+
```ruby
|
|
424
|
+
# Create a token store and manager (positional arguments)
|
|
425
|
+
token_store = Legate::Auth::TokenStore.new(session_service)
|
|
426
|
+
token_manager = Legate::Auth::TokenManager.new(token_store)
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
## Related Topics
|
|
430
|
+
- [Authentication Configuration](./configuration)
|
|
431
|
+
- [Token Lifecycle Management](./token_lifecycle)
|
|
432
|
+
- [Secure Credential Storage](./secure_storage)
|
|
433
|
+
- [OAuth2 Authentication](./oauth2)
|
|
434
|
+
- [OpenID Connect](./oidc)
|
|
435
|
+
- [Service Account Authentication](./service_account)
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
# OAuth2 Authentication
|
|
2
|
+
|
|
3
|
+
OAuth2 is a powerful authentication framework that allows users to authorize applications to access their accounts on other services without sharing their credentials. The Legate Ruby library provides comprehensive support for OAuth2 authentication flows.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
OAuth2 is an industry-standard protocol for authorization that enables secure API access without sharing passwords. The Legate Ruby library supports the following OAuth2 flows:
|
|
8
|
+
|
|
9
|
+
- **Authorization Code Flow**: The most common flow for web applications, involving a browser-based user consent
|
|
10
|
+
- **Client Credentials Flow**: Used for server-to-server authentication
|
|
11
|
+
- **Password Flow**: Used for trusted applications that can collect user credentials directly
|
|
12
|
+
|
|
13
|
+
## Configuration
|
|
14
|
+
|
|
15
|
+
### Creating an OAuth2 Scheme
|
|
16
|
+
|
|
17
|
+
```ruby
|
|
18
|
+
# Basic OAuth2 scheme with required parameters
|
|
19
|
+
scheme = Legate::Auth::Schemes::OAuth2.new(
|
|
20
|
+
authorization_url: 'https://auth.example.com/authorize',
|
|
21
|
+
token_url: 'https://auth.example.com/token',
|
|
22
|
+
scopes: ['profile', 'email', 'data:read']
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
# OAuth2 scheme with additional options
|
|
26
|
+
scheme = Legate::Auth::Schemes::OAuth2.new(
|
|
27
|
+
authorization_url: 'https://auth.example.com/authorize',
|
|
28
|
+
token_url: 'https://auth.example.com/token',
|
|
29
|
+
scopes: ['profile', 'email', 'data:read'],
|
|
30
|
+
use_pkce: true, # Use PKCE for enhanced security (default: true)
|
|
31
|
+
additional_params: {
|
|
32
|
+
'access_type' => 'offline', # Request a refresh token
|
|
33
|
+
'prompt' => 'consent' # Force the consent screen to appear
|
|
34
|
+
},
|
|
35
|
+
revocation_url: 'https://auth.example.com/revoke' # For token revocation
|
|
36
|
+
)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Creating an OAuth2 Credential
|
|
40
|
+
|
|
41
|
+
```ruby
|
|
42
|
+
# Basic OAuth2 credential with client ID and secret
|
|
43
|
+
credential = Legate::Auth::Credential.new(
|
|
44
|
+
auth_type: :oauth2,
|
|
45
|
+
client_id: ENV['CLIENT_ID'],
|
|
46
|
+
client_secret: ENV['CLIENT_SECRET']
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
# With additional options
|
|
50
|
+
credential = Legate::Auth::Credential.new(
|
|
51
|
+
auth_type: :oauth2,
|
|
52
|
+
client_id: ENV['CLIENT_ID'],
|
|
53
|
+
client_secret: ENV['CLIENT_SECRET'],
|
|
54
|
+
additional_params: {
|
|
55
|
+
'client_authentication' => 'body' # Send client credentials in request body
|
|
56
|
+
}
|
|
57
|
+
)
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Authentication Flows
|
|
61
|
+
|
|
62
|
+
### Authorization Code Flow
|
|
63
|
+
|
|
64
|
+
The authorization code flow is an interactive flow that requires user authentication and consent:
|
|
65
|
+
|
|
66
|
+
```ruby
|
|
67
|
+
# 1. Configure the OAuth2 scheme and credential
|
|
68
|
+
scheme = Legate::Auth::Schemes::OAuth2.new(
|
|
69
|
+
authorization_url: 'https://auth.example.com/authorize',
|
|
70
|
+
token_url: 'https://auth.example.com/token',
|
|
71
|
+
scopes: ['profile', 'email']
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
credential = Legate::Auth::Credential.new(
|
|
75
|
+
auth_type: :oauth2,
|
|
76
|
+
client_id: ENV['CLIENT_ID'],
|
|
77
|
+
client_secret: ENV['CLIENT_SECRET']
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
# 2. Build the authorization URI via a Config and redirect the user
|
|
81
|
+
config = Legate::Auth::Config.new(scheme: scheme, credential: credential)
|
|
82
|
+
state = SecureRandom.hex(16)
|
|
83
|
+
auth_url = config.build_authorization_uri('https://your-app.com/callback', state)
|
|
84
|
+
|
|
85
|
+
# 3. Redirect the user to auth_url
|
|
86
|
+
# redirect_to auth_url
|
|
87
|
+
# The user authenticates and is redirected back to your callback URL
|
|
88
|
+
|
|
89
|
+
# 4. On the callback, set the response URI on the config
|
|
90
|
+
config.response_uri = 'https://your-app.com/callback?code=12345&state=abcde'
|
|
91
|
+
|
|
92
|
+
# 5. Exchange the authorization code for tokens (state is verified internally)
|
|
93
|
+
token = scheme.exchange_token(config, credential)
|
|
94
|
+
puts token[:access_token]
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
> This shows the direct `Config`/scheme flow. Tools can also drive OAuth2
|
|
98
|
+
> interactively via `context.with_authentication` (see
|
|
99
|
+
> [`ToolContextExtension`](../api_reference/tool_context_extension)), which
|
|
100
|
+
> yields the auth request to your application for handling.
|
|
101
|
+
|
|
102
|
+
### Client Credentials Flow
|
|
103
|
+
|
|
104
|
+
The client credentials flow is a non-interactive flow used for server-to-server authentication:
|
|
105
|
+
|
|
106
|
+
```ruby
|
|
107
|
+
# 1. Configure the OAuth2 scheme
|
|
108
|
+
scheme = Legate::Auth::Schemes::OAuth2.new(
|
|
109
|
+
token_url: 'https://auth.example.com/token',
|
|
110
|
+
scopes: ['api:access']
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
# 2. Configure the credential
|
|
114
|
+
credential = Legate::Auth::Credential.new(
|
|
115
|
+
auth_type: :oauth2,
|
|
116
|
+
client_id: ENV['CLIENT_ID'],
|
|
117
|
+
client_secret: ENV['CLIENT_SECRET']
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
# 3. Request a token directly using the client credentials grant
|
|
121
|
+
token = scheme.client_credentials_token(credential)
|
|
122
|
+
|
|
123
|
+
# 4. Apply the token to outbound requests
|
|
124
|
+
connection = Legate::Auth.create_connection('https://api.example.com',
|
|
125
|
+
scheme: scheme,
|
|
126
|
+
credential: token
|
|
127
|
+
)
|
|
128
|
+
result = connection.get(path: '/resource')
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Token Refresh
|
|
132
|
+
|
|
133
|
+
OAuth2 access tokens typically expire after a short period. When you use a `TokenManager`, it automatically refreshes refreshable tokens before they expire:
|
|
134
|
+
|
|
135
|
+
```ruby
|
|
136
|
+
# Configure the scheme with a scope that returns a refresh token
|
|
137
|
+
scheme = Legate::Auth::Schemes::OAuth2.new(
|
|
138
|
+
authorization_url: 'https://auth.example.com/authorize',
|
|
139
|
+
token_url: 'https://auth.example.com/token',
|
|
140
|
+
scopes: ['profile', 'email', 'offline_access'], # offline_access requests a refresh token
|
|
141
|
+
additional_params: {
|
|
142
|
+
'access_type' => 'offline' # For Google OAuth2, request a refresh token
|
|
143
|
+
}
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
# A TokenManager will auto-refresh the cached token when it is near expiry
|
|
147
|
+
token_store = Legate::Auth::TokenStore.new(session_service)
|
|
148
|
+
token_manager = Legate::Auth::TokenManager.new(token_store)
|
|
149
|
+
token = token_manager.get_token(scheme, credential)
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Advanced Features
|
|
153
|
+
|
|
154
|
+
### PKCE (Proof Key for Code Exchange)
|
|
155
|
+
|
|
156
|
+
PKCE enhances security for public clients by preventing authorization code interception attacks:
|
|
157
|
+
|
|
158
|
+
```ruby
|
|
159
|
+
# PKCE is enabled by default
|
|
160
|
+
scheme = Legate::Auth::Schemes::OAuth2.new(
|
|
161
|
+
authorization_url: 'https://auth.example.com/authorize',
|
|
162
|
+
token_url: 'https://auth.example.com/token',
|
|
163
|
+
scopes: ['profile', 'email'],
|
|
164
|
+
use_pkce: true # This is the default
|
|
165
|
+
)
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Token Revocation
|
|
169
|
+
|
|
170
|
+
When you no longer need an access token, you can revoke it:
|
|
171
|
+
|
|
172
|
+
```ruby
|
|
173
|
+
# Configure the scheme with a revocation URL
|
|
174
|
+
scheme = Legate::Auth::Schemes::OAuth2.new(
|
|
175
|
+
authorization_url: 'https://auth.example.com/authorize',
|
|
176
|
+
token_url: 'https://auth.example.com/token',
|
|
177
|
+
revocation_url: 'https://auth.example.com/revoke',
|
|
178
|
+
scopes: ['profile', 'email']
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
# Revoke a token
|
|
182
|
+
token_manager = Legate::Auth::TokenManager.new
|
|
183
|
+
token_manager.revoke_token(token, credential)
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## Provider-Specific Configurations
|
|
187
|
+
|
|
188
|
+
### Google OAuth2
|
|
189
|
+
|
|
190
|
+
```ruby
|
|
191
|
+
scheme = Legate::Auth::Schemes::OAuth2.new(
|
|
192
|
+
authorization_url: 'https://accounts.google.com/o/oauth2/auth',
|
|
193
|
+
token_url: 'https://oauth2.googleapis.com/token',
|
|
194
|
+
scopes: ['https://www.googleapis.com/auth/userinfo.email',
|
|
195
|
+
'https://www.googleapis.com/auth/userinfo.profile'],
|
|
196
|
+
additional_params: {
|
|
197
|
+
'access_type' => 'offline',
|
|
198
|
+
'prompt' => 'consent' # Force consent screen to appear
|
|
199
|
+
}
|
|
200
|
+
)
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### GitHub OAuth2
|
|
204
|
+
|
|
205
|
+
```ruby
|
|
206
|
+
scheme = Legate::Auth::Schemes::OAuth2.new(
|
|
207
|
+
authorization_url: 'https://github.com/login/oauth/authorize',
|
|
208
|
+
token_url: 'https://github.com/login/oauth/access_token',
|
|
209
|
+
scopes: ['user', 'repo']
|
|
210
|
+
)
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### Microsoft Azure OAuth2
|
|
214
|
+
|
|
215
|
+
```ruby
|
|
216
|
+
tenant_id = 'common' # Use 'common' for multi-tenant, or a specific tenant ID
|
|
217
|
+
|
|
218
|
+
scheme = Legate::Auth::Schemes::OAuth2.new(
|
|
219
|
+
authorization_url: "https://login.microsoftonline.com/#{tenant_id}/oauth2/v2.0/authorize",
|
|
220
|
+
token_url: "https://login.microsoftonline.com/#{tenant_id}/oauth2/v2.0/token",
|
|
221
|
+
scopes: ['user.read', 'offline_access']
|
|
222
|
+
)
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## Security Considerations
|
|
226
|
+
|
|
227
|
+
- **Client Secret Protection**: Always store client secrets securely, preferably in environment variables.
|
|
228
|
+
- **HTTPS**: Ensure all OAuth2 endpoints use HTTPS to protect token transmission.
|
|
229
|
+
- **Use PKCE**: Enable PKCE (enabled by default) for enhanced security, especially for mobile or desktop applications.
|
|
230
|
+
- **Scoped Tokens**: Request only the scopes your application needs.
|
|
231
|
+
- **Token Storage**: Tokens are cached in scoped session state as plaintext. For at-rest encryption, apply the opt-in `Legate::Auth::Encryption` module yourself.
|
|
232
|
+
|
|
233
|
+
## Troubleshooting
|
|
234
|
+
|
|
235
|
+
If you encounter issues with OAuth2 authentication:
|
|
236
|
+
|
|
237
|
+
- Check that all URLs and credentials are correct
|
|
238
|
+
- Verify that redirect URIs exactly match those registered with the provider
|
|
239
|
+
- Ensure scopes are properly formatted and allowed by the provider
|
|
240
|
+
- See the [OAuth2 Troubleshooting Guide](../troubleshooting/oauth2_issues) for detailed solutions
|
|
241
|
+
|
|
242
|
+
## Related Topics
|
|
243
|
+
|
|
244
|
+
- [OpenID Connect](./oidc) - Learn about OpenID Connect, an identity layer built on top of OAuth2
|
|
245
|
+
- [Token Lifecycle Management](./token_lifecycle) - Advanced token management techniques
|
|
246
|
+
- [Secure Credential Storage](./secure_storage) - Best practices for credential security
|
|
247
|
+
|
|
248
|
+
## Next Steps
|
|
249
|
+
|
|
250
|
+
- [OpenID Connect](./oidc): Learn about OpenID Connect, an identity layer built on top of OAuth2
|
|
251
|
+
- [Token Lifecycle Management](./token_lifecycle): Advanced token management techniques
|
|
252
|
+
- [Secure Credential Storage](./secure_storage): Best practices for credential security
|