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,514 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Example of using OpenID Connect (OIDC) authentication with Legate
|
|
5
|
+
#
|
|
6
|
+
# This example demonstrates how to use OIDC authentication to make authenticated requests
|
|
7
|
+
# and retrieve user profile information from an identity provider.
|
|
8
|
+
#
|
|
9
|
+
# Usage:
|
|
10
|
+
# ruby examples/advanced/auth/oidc_auth.rb [--with-server]
|
|
11
|
+
# ruby examples/advanced/auth/oidc_auth.rb --client-id=YOUR_CLIENT_ID --client-secret=YOUR_CLIENT_SECRET --provider=google
|
|
12
|
+
#
|
|
13
|
+
# Set these environment variables to use your own OIDC provider:
|
|
14
|
+
# OIDC_CLIENT_ID
|
|
15
|
+
# OIDC_CLIENT_SECRET
|
|
16
|
+
# OIDC_PROVIDER (default: google, supported: google, auth0)
|
|
17
|
+
# OIDC_REDIRECT_URI (default: http://localhost:3000/auth/callback)
|
|
18
|
+
|
|
19
|
+
require 'bundler/setup'
|
|
20
|
+
require 'legate'
|
|
21
|
+
require 'legate/auth'
|
|
22
|
+
require 'legate/auth/runner'
|
|
23
|
+
require 'legate/auth/schemes/openid_connect'
|
|
24
|
+
require 'legate/tool_context'
|
|
25
|
+
require 'legate/web/server'
|
|
26
|
+
require 'launchy'
|
|
27
|
+
require 'securerandom'
|
|
28
|
+
require 'optparse'
|
|
29
|
+
require 'json'
|
|
30
|
+
require 'fileutils'
|
|
31
|
+
|
|
32
|
+
# Parse command line arguments
|
|
33
|
+
options = {
|
|
34
|
+
with_server: false,
|
|
35
|
+
client_id: ENV['OIDC_CLIENT_ID'],
|
|
36
|
+
client_secret: ENV['OIDC_CLIENT_SECRET'],
|
|
37
|
+
provider: ENV['OIDC_PROVIDER'] || 'google',
|
|
38
|
+
redirect_uri: ENV['OIDC_REDIRECT_URI'] || 'http://localhost:3000/auth/callback',
|
|
39
|
+
handle_response: false,
|
|
40
|
+
request_id: nil,
|
|
41
|
+
response_uri: nil,
|
|
42
|
+
use_refresh_token: false,
|
|
43
|
+
verbose: false
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
OptionParser.new do |opts|
|
|
47
|
+
opts.banner = 'Usage: ruby examples/advanced/auth/oidc_auth.rb [options]'
|
|
48
|
+
|
|
49
|
+
opts.on('--with-server', 'Start a local server to handle the OIDC callback') do
|
|
50
|
+
options[:with_server] = true
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
opts.on('--client-id=ID', 'OIDC client ID') do |id|
|
|
54
|
+
options[:client_id] = id
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
opts.on('--client-secret=SECRET', 'OIDC client secret') do |secret|
|
|
58
|
+
options[:client_secret] = secret
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
opts.on('--provider=PROVIDER', 'OIDC provider (google, auth0)') do |provider|
|
|
62
|
+
options[:provider] = provider
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
opts.on('--redirect-uri=URI', 'OIDC redirect URI') do |uri|
|
|
66
|
+
options[:redirect_uri] = uri
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
opts.on('--handle-response', 'Handle an authentication response') do
|
|
70
|
+
options[:handle_response] = true
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
opts.on('--request-id=ID', 'Authentication request ID') do |id|
|
|
74
|
+
options[:request_id] = id
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
opts.on('--response-uri=URI', 'Authentication response URI') do |uri|
|
|
78
|
+
options[:response_uri] = uri
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
opts.on('--use-refresh-token', 'Use a saved refresh token if available') do
|
|
82
|
+
options[:use_refresh_token] = true
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
opts.on('--verbose', 'Enable verbose output') do
|
|
86
|
+
options[:verbose] = true
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
opts.on('--help', 'Show this help message') do
|
|
90
|
+
puts opts
|
|
91
|
+
exit
|
|
92
|
+
end
|
|
93
|
+
end.parse!
|
|
94
|
+
|
|
95
|
+
# Validate required parameters
|
|
96
|
+
if options[:client_id].nil? || options[:client_id].empty?
|
|
97
|
+
puts 'Error: OIDC client ID is required'
|
|
98
|
+
puts 'Please provide it via --client-id or OIDC_CLIENT_ID environment variable'
|
|
99
|
+
exit 1
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
if options[:client_secret].nil? || options[:client_secret].empty?
|
|
103
|
+
puts 'Error: OIDC client secret is required'
|
|
104
|
+
puts 'Please provide it via --client-secret or OIDC_CLIENT_SECRET environment variable'
|
|
105
|
+
exit 1
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Setup the session service
|
|
109
|
+
session_service = Legate::SessionService::InMemory.new
|
|
110
|
+
|
|
111
|
+
# Create a tool context with the session service
|
|
112
|
+
# In a real application, you would use Redis or another persistent storage
|
|
113
|
+
context = Legate::ToolContext.new(session_service: session_service)
|
|
114
|
+
|
|
115
|
+
# Create an OIDC scheme based on the selected provider
|
|
116
|
+
oidc_scheme = case options[:provider]&.downcase
|
|
117
|
+
when 'google'
|
|
118
|
+
Legate::Auth::Schemes::OpenIDConnect.new(
|
|
119
|
+
authorization_url: 'https://accounts.google.com/o/oauth2/auth',
|
|
120
|
+
token_url: 'https://oauth2.googleapis.com/token',
|
|
121
|
+
userinfo_url: 'https://openidconnect.googleapis.com/v1/userinfo',
|
|
122
|
+
jwks_uri: 'https://www.googleapis.com/oauth2/v3/certs',
|
|
123
|
+
scopes: %w[openid email profile],
|
|
124
|
+
fetch_userinfo: true,
|
|
125
|
+
use_pkce: true,
|
|
126
|
+
additional_params: {
|
|
127
|
+
prompt: 'consent',
|
|
128
|
+
access_type: 'offline'
|
|
129
|
+
}
|
|
130
|
+
)
|
|
131
|
+
when 'auth0'
|
|
132
|
+
# Your Auth0 domain - replace with your actual domain
|
|
133
|
+
domain = ENV['AUTH0_DOMAIN'] || 'your-domain.auth0.com'
|
|
134
|
+
|
|
135
|
+
Legate::Auth::Schemes::OpenIDConnect.new(
|
|
136
|
+
authorization_url: "https://#{domain}/authorize",
|
|
137
|
+
token_url: "https://#{domain}/oauth/token",
|
|
138
|
+
userinfo_url: "https://#{domain}/userinfo",
|
|
139
|
+
jwks_uri: "https://#{domain}/.well-known/jwks.json",
|
|
140
|
+
scopes: %w[openid email profile],
|
|
141
|
+
fetch_userinfo: true,
|
|
142
|
+
use_pkce: true
|
|
143
|
+
)
|
|
144
|
+
else
|
|
145
|
+
puts "Warning: Unsupported provider '#{options[:provider]}'. Using Google as default."
|
|
146
|
+
|
|
147
|
+
Legate::Auth::Schemes::OpenIDConnect.new(
|
|
148
|
+
authorization_url: 'https://accounts.google.com/o/oauth2/auth',
|
|
149
|
+
token_url: 'https://oauth2.googleapis.com/token',
|
|
150
|
+
userinfo_url: 'https://openidconnect.googleapis.com/v1/userinfo',
|
|
151
|
+
jwks_uri: 'https://www.googleapis.com/oauth2/v3/certs',
|
|
152
|
+
scopes: %w[openid email profile],
|
|
153
|
+
fetch_userinfo: true,
|
|
154
|
+
use_pkce: true,
|
|
155
|
+
additional_params: {
|
|
156
|
+
prompt: 'consent',
|
|
157
|
+
access_type: 'offline'
|
|
158
|
+
}
|
|
159
|
+
)
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# Create the OIDC credential
|
|
163
|
+
credential = Legate::Auth::Credential.new(
|
|
164
|
+
auth_type: :oidc,
|
|
165
|
+
client_id: options[:client_id],
|
|
166
|
+
client_secret: options[:client_secret]
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
# Token storage file for refresh tokens
|
|
170
|
+
token_storage_file = File.expand_path('~/.oidc_example_token.json')
|
|
171
|
+
|
|
172
|
+
# Helper to save token to file
|
|
173
|
+
def save_token_to_file(token, file_path)
|
|
174
|
+
# Convert token to a hash for storage
|
|
175
|
+
token_data = {
|
|
176
|
+
access_token: token.access_token,
|
|
177
|
+
refresh_token: token.refresh_token,
|
|
178
|
+
token_type: token.token_type,
|
|
179
|
+
expires_at: token.expires_at.to_i,
|
|
180
|
+
scope: token.scope,
|
|
181
|
+
id_token: token[:id_token],
|
|
182
|
+
metadata: token.metadata
|
|
183
|
+
}.compact
|
|
184
|
+
|
|
185
|
+
# Save to file
|
|
186
|
+
FileUtils.mkdir_p(File.dirname(file_path))
|
|
187
|
+
File.write(file_path, JSON.pretty_generate(token_data))
|
|
188
|
+
puts "Token saved to #{file_path}"
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
# Helper to load token from file
|
|
192
|
+
def load_token_from_file(file_path)
|
|
193
|
+
return nil unless File.exist?(file_path)
|
|
194
|
+
|
|
195
|
+
begin
|
|
196
|
+
token_data = JSON.parse(File.read(file_path), symbolize_names: true)
|
|
197
|
+
|
|
198
|
+
# Create an exchanged credential from the stored data
|
|
199
|
+
Legate::Auth::ExchangedCredential.new(
|
|
200
|
+
auth_type: :oidc,
|
|
201
|
+
access_token: token_data[:access_token],
|
|
202
|
+
refresh_token: token_data[:refresh_token],
|
|
203
|
+
token_type: token_data[:token_type],
|
|
204
|
+
expires_at: Time.at(token_data[:expires_at]),
|
|
205
|
+
scope: token_data[:scope],
|
|
206
|
+
id_token: token_data[:id_token],
|
|
207
|
+
metadata: token_data[:metadata]
|
|
208
|
+
)
|
|
209
|
+
rescue StandardError => e
|
|
210
|
+
puts "Error loading token: #{e.message}"
|
|
211
|
+
nil
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
# Create a web server to handle the OIDC callback
|
|
216
|
+
def create_callback_server(context, request_id_holder)
|
|
217
|
+
server = Legate::Web::Server.new(port: 3000)
|
|
218
|
+
|
|
219
|
+
# Add a route to handle the OIDC callback
|
|
220
|
+
server.add_route('GET', '/auth/callback') do |req, res|
|
|
221
|
+
# Extract the authorization code from the query parameters
|
|
222
|
+
code = req.query['code']
|
|
223
|
+
state = req.query['state']
|
|
224
|
+
error = req.query['error']
|
|
225
|
+
|
|
226
|
+
if error
|
|
227
|
+
res.status = 400
|
|
228
|
+
res.body = "Authentication failed: #{error}"
|
|
229
|
+
elsif code
|
|
230
|
+
# Convert the request to a response URI
|
|
231
|
+
response_uri = "http://localhost:3000/auth/callback?#{req.query_string}"
|
|
232
|
+
|
|
233
|
+
# Find the active auth request
|
|
234
|
+
request_id = request_id_holder[:id] || nil
|
|
235
|
+
|
|
236
|
+
if request_id
|
|
237
|
+
# Handle the auth response
|
|
238
|
+
result = context.handle_auth_response(request_id, { 'response_uri' => response_uri })
|
|
239
|
+
|
|
240
|
+
# Show a success page
|
|
241
|
+
res.status = 200
|
|
242
|
+
res.body = <<~HTML
|
|
243
|
+
<!DOCTYPE html>
|
|
244
|
+
<html>
|
|
245
|
+
<head>
|
|
246
|
+
<title>OIDC Authentication Successful</title>
|
|
247
|
+
<style>
|
|
248
|
+
body { font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px; }
|
|
249
|
+
h1 { color: #4CAF50; }
|
|
250
|
+
.box { border: 1px solid #ddd; border-radius: 5px; padding: 20px; margin-top: 20px; }
|
|
251
|
+
</style>
|
|
252
|
+
</head>
|
|
253
|
+
<body>
|
|
254
|
+
<h1>Authentication Successful</h1>
|
|
255
|
+
<p>You have successfully authenticated with the OpenID Connect provider.</p>
|
|
256
|
+
<div class="box">
|
|
257
|
+
<p>You can close this window and return to the application.</p>
|
|
258
|
+
</div>
|
|
259
|
+
</body>
|
|
260
|
+
</html>
|
|
261
|
+
HTML
|
|
262
|
+
else
|
|
263
|
+
res.status = 400
|
|
264
|
+
res.body = 'No active authentication request found'
|
|
265
|
+
end
|
|
266
|
+
else
|
|
267
|
+
res.status = 400
|
|
268
|
+
res.body = 'Missing required parameters'
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
server
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
# Helper to fetch user information
|
|
276
|
+
def fetch_user_info(token, oidc_scheme)
|
|
277
|
+
return {} unless token&.access_token && oidc_scheme.userinfo_url
|
|
278
|
+
|
|
279
|
+
begin
|
|
280
|
+
require 'faraday'
|
|
281
|
+
|
|
282
|
+
conn = Faraday.new do |builder|
|
|
283
|
+
builder.adapter Faraday.default_adapter
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
response = conn.get(oidc_scheme.userinfo_url) do |req|
|
|
287
|
+
req.headers['Authorization'] = "#{token.token_type} #{token.access_token}"
|
|
288
|
+
req.headers['Accept'] = 'application/json'
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
if response.status == 200
|
|
292
|
+
JSON.parse(response.body, symbolize_names: true)
|
|
293
|
+
else
|
|
294
|
+
{ error: "Failed to fetch user info: #{response.status}" }
|
|
295
|
+
end
|
|
296
|
+
rescue StandardError => e
|
|
297
|
+
{ error: "Error fetching user info: #{e.message}" }
|
|
298
|
+
end
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
def display_user_info(user_info)
|
|
302
|
+
return puts 'No user info available' if user_info.nil? || user_info.empty?
|
|
303
|
+
|
|
304
|
+
puts "\nUser Information:"
|
|
305
|
+
puts " Name : #{user_info[:name] || 'N/A'}"
|
|
306
|
+
puts " Email : #{user_info[:email] || 'N/A'}"
|
|
307
|
+
puts " Picture : #{user_info[:picture] ? 'Available' : 'N/A'}"
|
|
308
|
+
puts " Locale : #{user_info[:locale] || 'N/A'}"
|
|
309
|
+
|
|
310
|
+
puts " Subject ID : #{user_info[:sub]}" if user_info[:sub]
|
|
311
|
+
|
|
312
|
+
# Display additional fields if available
|
|
313
|
+
additional_fields = user_info.keys - %i[name email picture locale sub error]
|
|
314
|
+
return unless additional_fields.any?
|
|
315
|
+
|
|
316
|
+
puts ' Additional Information:'
|
|
317
|
+
additional_fields.each do |field|
|
|
318
|
+
puts " #{field}: #{user_info[field]}"
|
|
319
|
+
end
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
def make_authenticated_request(token, oidc_scheme)
|
|
323
|
+
# In a real application, you would make an API call to an OIDC-protected endpoint
|
|
324
|
+
# This is just a simulation to show the token information
|
|
325
|
+
puts "\nMaking an authenticated request with token:"
|
|
326
|
+
puts " Access Token : #{token.access_token[0..9]}...#{token.access_token[-4..-1]}"
|
|
327
|
+
puts " Token Type : #{token.token_type}"
|
|
328
|
+
puts " Expires In : #{(token.expires_at - Time.now).to_i} seconds"
|
|
329
|
+
puts " Scopes : #{token.scope}"
|
|
330
|
+
|
|
331
|
+
# Print ID token if available
|
|
332
|
+
puts " ID Token : #{token[:id_token][0..9]}...#{token[:id_token][-4..-1]}" if token[:id_token]
|
|
333
|
+
|
|
334
|
+
# Print refresh token if available
|
|
335
|
+
puts " Refresh Token: #{token.refresh_token[0..5]}...#{token.refresh_token[-4..-1]}" if token.refresh_token
|
|
336
|
+
|
|
337
|
+
# Get user info from token metadata or fetch it
|
|
338
|
+
user_info = token.metadata&.dig(:userinfo)
|
|
339
|
+
|
|
340
|
+
if !user_info && token.access_token
|
|
341
|
+
puts "\nFetching user information..."
|
|
342
|
+
user_info = fetch_user_info(token, oidc_scheme)
|
|
343
|
+
|
|
344
|
+
# Store user info in token metadata for future use
|
|
345
|
+
if user_info && !user_info[:error]
|
|
346
|
+
token.metadata ||= {}
|
|
347
|
+
token.metadata[:userinfo] = user_info
|
|
348
|
+
end
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
# Display user information
|
|
352
|
+
display_user_info(user_info)
|
|
353
|
+
|
|
354
|
+
puts "\nAuthenticated request successful!"
|
|
355
|
+
|
|
356
|
+
{
|
|
357
|
+
status: 'success',
|
|
358
|
+
authenticated: true,
|
|
359
|
+
auth_method: 'OIDC',
|
|
360
|
+
token_expiry: token.expires_at,
|
|
361
|
+
scopes: token.scope,
|
|
362
|
+
user_info: user_info
|
|
363
|
+
}
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
# Main execution logic
|
|
367
|
+
begin
|
|
368
|
+
# Try to use saved refresh token
|
|
369
|
+
saved_token = nil
|
|
370
|
+
if options[:use_refresh_token] && File.exist?(token_storage_file)
|
|
371
|
+
puts 'Found saved token, attempting to use it...'
|
|
372
|
+
saved_token = load_token_from_file(token_storage_file)
|
|
373
|
+
|
|
374
|
+
if saved_token
|
|
375
|
+
if saved_token.expired?
|
|
376
|
+
puts 'Saved token is expired, attempting to refresh it...'
|
|
377
|
+
|
|
378
|
+
# Create a token store
|
|
379
|
+
token_store = Legate::Auth::TokenStore.new(session_service: session_service)
|
|
380
|
+
|
|
381
|
+
# Create a token manager
|
|
382
|
+
token_manager = Legate::Auth::TokenManager.new(token_store)
|
|
383
|
+
|
|
384
|
+
# Refresh the token
|
|
385
|
+
begin
|
|
386
|
+
refreshed_token = token_manager.refresh_token(oidc_scheme, credential, saved_token)
|
|
387
|
+
puts 'Token refreshed successfully!'
|
|
388
|
+
|
|
389
|
+
# Save the refreshed token
|
|
390
|
+
save_token_to_file(refreshed_token, token_storage_file)
|
|
391
|
+
|
|
392
|
+
# Make a request with the refreshed token
|
|
393
|
+
result = make_authenticated_request(refreshed_token, oidc_scheme)
|
|
394
|
+
puts "\nAuthentication via refresh token successful!"
|
|
395
|
+
exit 0
|
|
396
|
+
rescue StandardError => e
|
|
397
|
+
puts "Token refresh failed: #{e.message}"
|
|
398
|
+
puts 'Will proceed with interactive authentication...'
|
|
399
|
+
# Continue with interactive flow below
|
|
400
|
+
end
|
|
401
|
+
else
|
|
402
|
+
puts 'Saved token is still valid, using it...'
|
|
403
|
+
result = make_authenticated_request(saved_token, oidc_scheme)
|
|
404
|
+
puts "\nAuthentication with saved token successful!"
|
|
405
|
+
exit 0
|
|
406
|
+
end
|
|
407
|
+
end
|
|
408
|
+
end
|
|
409
|
+
|
|
410
|
+
# Handle authentication response if requested
|
|
411
|
+
if options[:handle_response]
|
|
412
|
+
unless options[:request_id] && options[:response_uri]
|
|
413
|
+
puts 'Error: Both --request-id and --response-uri are required with --handle-response'
|
|
414
|
+
exit 1
|
|
415
|
+
end
|
|
416
|
+
|
|
417
|
+
puts "Handling authentication response for request ID: #{options[:request_id]}"
|
|
418
|
+
|
|
419
|
+
# Create a response object
|
|
420
|
+
response = { 'response_uri' => options[:response_uri] }
|
|
421
|
+
|
|
422
|
+
# Handle the authentication response
|
|
423
|
+
result = context.handle_auth_response(options[:request_id], response)
|
|
424
|
+
|
|
425
|
+
if result[:status] == :completed
|
|
426
|
+
puts 'Authentication completed successfully!'
|
|
427
|
+
token = result[:credential]
|
|
428
|
+
|
|
429
|
+
# Save the token for future use
|
|
430
|
+
save_token_to_file(token, token_storage_file)
|
|
431
|
+
|
|
432
|
+
# Make a request with the token
|
|
433
|
+
make_authenticated_request(token, oidc_scheme)
|
|
434
|
+
else
|
|
435
|
+
puts 'Authentication not completed:'
|
|
436
|
+
puts result.inspect
|
|
437
|
+
end
|
|
438
|
+
|
|
439
|
+
exit 0
|
|
440
|
+
end
|
|
441
|
+
|
|
442
|
+
# Start server if requested
|
|
443
|
+
if options[:with_server]
|
|
444
|
+
puts 'Starting local server to handle OIDC callback...'
|
|
445
|
+
request_id_holder = { id: nil }
|
|
446
|
+
server = create_callback_server(context, request_id_holder)
|
|
447
|
+
|
|
448
|
+
begin
|
|
449
|
+
# Start the server
|
|
450
|
+
server_thread = Thread.new { server.start }
|
|
451
|
+
|
|
452
|
+
# Use with_authentication to create a fiber context with auth support
|
|
453
|
+
result = context.with_authentication do
|
|
454
|
+
# This callback will be called when an authentication request is yielded
|
|
455
|
+
|
|
456
|
+
# Authenticate with the scheme and credential
|
|
457
|
+
token = context.auth_session(
|
|
458
|
+
oidc_scheme,
|
|
459
|
+
credential,
|
|
460
|
+
redirect_uri: options[:redirect_uri]
|
|
461
|
+
)
|
|
462
|
+
|
|
463
|
+
if token
|
|
464
|
+
# Save the token for future use
|
|
465
|
+
save_token_to_file(token, token_storage_file)
|
|
466
|
+
|
|
467
|
+
# Make authenticated request
|
|
468
|
+
make_authenticated_request(token, oidc_scheme)
|
|
469
|
+
else
|
|
470
|
+
puts 'Failed to get authentication token'
|
|
471
|
+
{ status: :error, message: 'Authentication failed' }
|
|
472
|
+
end
|
|
473
|
+
end
|
|
474
|
+
|
|
475
|
+
puts "Result: #{result.inspect}"
|
|
476
|
+
ensure
|
|
477
|
+
# Stop the server
|
|
478
|
+
server.stop
|
|
479
|
+
# Wait for server thread to finish
|
|
480
|
+
server_thread.join if server_thread
|
|
481
|
+
end
|
|
482
|
+
else
|
|
483
|
+
# Manual flow without a local server
|
|
484
|
+
puts 'Starting OIDC authentication process...'
|
|
485
|
+
|
|
486
|
+
# Create an auth runner
|
|
487
|
+
auth_runner = Legate::Auth::Runner.new(session_service: session_service)
|
|
488
|
+
|
|
489
|
+
# Start authentication
|
|
490
|
+
auth_result = auth_runner.authenticate(oidc_scheme, credential,
|
|
491
|
+
redirect_uri: options[:redirect_uri])
|
|
492
|
+
|
|
493
|
+
# Check if we got a token immediately (unlikely for OIDC)
|
|
494
|
+
if auth_result.is_a?(Legate::Auth::ExchangedCredential)
|
|
495
|
+
puts 'Received token immediately (unusual for OIDC):'
|
|
496
|
+
make_authenticated_request(auth_result, oidc_scheme)
|
|
497
|
+
else
|
|
498
|
+
# We should have an authentication request
|
|
499
|
+
request_id = auth_result[:request_id]
|
|
500
|
+
auth_request = auth_result[:auth_request]
|
|
501
|
+
|
|
502
|
+
puts 'Authentication required. Please visit this URL to authorize:'
|
|
503
|
+
puts " #{auth_request[:url]}"
|
|
504
|
+
puts
|
|
505
|
+
puts 'After authorization, you will be redirected. Copy the entire URL from your browser'
|
|
506
|
+
puts 'and run this command to complete authentication:'
|
|
507
|
+
puts
|
|
508
|
+
puts " ruby #{__FILE__} --handle-response --request-id #{request_id} --response-uri 'PASTE_URL_HERE'"
|
|
509
|
+
end
|
|
510
|
+
end
|
|
511
|
+
rescue StandardError => e
|
|
512
|
+
puts "Error: #{e.class}: #{e.message}"
|
|
513
|
+
puts e.backtrace.join("\n") if options[:verbose]
|
|
514
|
+
end
|