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,626 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Custom Authentication Flows Example
|
|
5
|
+
#
|
|
6
|
+
# This example demonstrates advanced authentication patterns including:
|
|
7
|
+
# - Creating custom authentication schemes
|
|
8
|
+
# - Multi-step authentication flows
|
|
9
|
+
# - Custom authentication middleware
|
|
10
|
+
# - Conditional authentication based on request properties
|
|
11
|
+
# - Authentication delegation and chaining
|
|
12
|
+
# - Custom token formats and validation
|
|
13
|
+
#
|
|
14
|
+
# Usage:
|
|
15
|
+
# ruby examples/advanced/auth/custom_auth_flows_example.rb [--flow basic|digest|multi_step|conditional]
|
|
16
|
+
|
|
17
|
+
require 'bundler/setup'
|
|
18
|
+
require 'legate'
|
|
19
|
+
require 'legate/auth'
|
|
20
|
+
require 'base64'
|
|
21
|
+
require 'digest'
|
|
22
|
+
require 'optparse'
|
|
23
|
+
require 'securerandom'
|
|
24
|
+
|
|
25
|
+
# Parse command line options
|
|
26
|
+
options = {
|
|
27
|
+
flow: 'basic',
|
|
28
|
+
verbose: false
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
OptionParser.new do |opts|
|
|
32
|
+
opts.banner = "Usage: ruby #{__FILE__} [options]"
|
|
33
|
+
|
|
34
|
+
opts.on('--flow FLOW', %w[basic digest multi_step conditional], 'Authentication flow to demonstrate') do |flow|
|
|
35
|
+
options[:flow] = flow
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
opts.on('--verbose', 'Enable verbose output') do
|
|
39
|
+
options[:verbose] = true
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
opts.on('--help', 'Show this help') do
|
|
43
|
+
puts opts
|
|
44
|
+
exit
|
|
45
|
+
end
|
|
46
|
+
end.parse!
|
|
47
|
+
|
|
48
|
+
puts '=== Custom Authentication Flows Example ==='
|
|
49
|
+
puts "Flow: #{options[:flow]}"
|
|
50
|
+
puts
|
|
51
|
+
|
|
52
|
+
# 1. Custom Basic Authentication Scheme
|
|
53
|
+
# Extends the basic auth pattern with custom headers and validation
|
|
54
|
+
class CustomBasicAuthScheme < Legate::Auth::Scheme
|
|
55
|
+
def initialize(realm: 'Protected Area', custom_header: nil)
|
|
56
|
+
super()
|
|
57
|
+
@realm = realm
|
|
58
|
+
@custom_header = custom_header
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def scheme_type
|
|
62
|
+
:custom_basic
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def apply_to_request(credential, params = {})
|
|
66
|
+
username = credential[:username] || credential[:username, resolve_env: true]
|
|
67
|
+
password = credential[:password] || credential[:password, resolve_env: true]
|
|
68
|
+
|
|
69
|
+
raise Legate::Auth::Error, 'Username is required for custom basic auth' unless username
|
|
70
|
+
raise Legate::Auth::Error, 'Password is required for custom basic auth' unless password
|
|
71
|
+
|
|
72
|
+
# Create basic auth header
|
|
73
|
+
encoded = Base64.strict_encode64("#{username}:#{password}")
|
|
74
|
+
|
|
75
|
+
headers = params[:headers] || {}
|
|
76
|
+
headers['Authorization'] = "Basic #{encoded}"
|
|
77
|
+
|
|
78
|
+
# Add custom header if specified
|
|
79
|
+
headers[@custom_header] = "Basic-#{@realm}" if @custom_header
|
|
80
|
+
|
|
81
|
+
# Add client identification
|
|
82
|
+
headers['User-Agent'] = 'Legate-CustomAuth/1.0'
|
|
83
|
+
|
|
84
|
+
{ headers: headers }
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def to_h
|
|
88
|
+
{
|
|
89
|
+
scheme_type: scheme_type,
|
|
90
|
+
realm: @realm,
|
|
91
|
+
custom_header: @custom_header
|
|
92
|
+
}
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# 2. Custom Digest Authentication Scheme
|
|
97
|
+
# Implements digest authentication with custom challenge handling
|
|
98
|
+
class CustomDigestAuthScheme < Legate::Auth::Scheme
|
|
99
|
+
def initialize(realm: 'Protected Area')
|
|
100
|
+
super()
|
|
101
|
+
@realm = realm
|
|
102
|
+
@nonce_count = 0
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def scheme_type
|
|
106
|
+
:custom_digest
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def apply_to_request(credential, params = {})
|
|
110
|
+
username = credential[:username] || credential[:username, resolve_env: true]
|
|
111
|
+
password = credential[:password] || credential[:password, resolve_env: true]
|
|
112
|
+
|
|
113
|
+
raise Legate::Auth::Error, 'Username is required for digest auth' unless username
|
|
114
|
+
raise Legate::Auth::Error, 'Password is required for digest auth' unless password
|
|
115
|
+
|
|
116
|
+
# Simulate digest challenge (in real implementation this would come from a 401 response)
|
|
117
|
+
nonce = params[:nonce] || SecureRandom.hex(16)
|
|
118
|
+
uri = params[:uri] || '/'
|
|
119
|
+
method = params[:method] || 'GET'
|
|
120
|
+
|
|
121
|
+
@nonce_count += 1
|
|
122
|
+
nc = format('%08x', @nonce_count)
|
|
123
|
+
cnonce = SecureRandom.hex(8)
|
|
124
|
+
|
|
125
|
+
# Calculate digest response
|
|
126
|
+
ha1 = Digest::MD5.hexdigest("#{username}:#{@realm}:#{password}")
|
|
127
|
+
ha2 = Digest::MD5.hexdigest("#{method}:#{uri}")
|
|
128
|
+
response = Digest::MD5.hexdigest("#{ha1}:#{nonce}:#{nc}:#{cnonce}:auth:#{ha2}")
|
|
129
|
+
|
|
130
|
+
# Build digest header
|
|
131
|
+
digest_header = [
|
|
132
|
+
"Digest username=\"#{username}\"",
|
|
133
|
+
"realm=\"#{@realm}\"",
|
|
134
|
+
"nonce=\"#{nonce}\"",
|
|
135
|
+
"uri=\"#{uri}\"",
|
|
136
|
+
"response=\"#{response}\"",
|
|
137
|
+
'algorithm=MD5',
|
|
138
|
+
'qop=auth',
|
|
139
|
+
"nc=#{nc}",
|
|
140
|
+
"cnonce=\"#{cnonce}\""
|
|
141
|
+
].join(', ')
|
|
142
|
+
|
|
143
|
+
headers = params[:headers] || {}
|
|
144
|
+
headers['Authorization'] = digest_header
|
|
145
|
+
|
|
146
|
+
{ headers: headers }
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def to_h
|
|
150
|
+
{
|
|
151
|
+
scheme_type: scheme_type,
|
|
152
|
+
realm: @realm,
|
|
153
|
+
nonce_count: @nonce_count
|
|
154
|
+
}
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# 3. Multi-Step Authentication Scheme
|
|
159
|
+
# Demonstrates a multi-step authentication flow with state management
|
|
160
|
+
class MultiStepAuthScheme < Legate::Auth::Scheme
|
|
161
|
+
def initialize
|
|
162
|
+
super()
|
|
163
|
+
@auth_state = {}
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def scheme_type
|
|
167
|
+
:multi_step
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def apply_to_request(credential, params = {})
|
|
171
|
+
step = params[:step] || 1
|
|
172
|
+
session_id = params[:session_id] || SecureRandom.hex(16)
|
|
173
|
+
|
|
174
|
+
case step
|
|
175
|
+
when 1
|
|
176
|
+
# Step 1: Initial authentication
|
|
177
|
+
perform_step_1(credential, session_id)
|
|
178
|
+
when 2
|
|
179
|
+
# Step 2: Second factor
|
|
180
|
+
perform_step_2(credential, session_id, params)
|
|
181
|
+
when 3
|
|
182
|
+
# Step 3: Final token
|
|
183
|
+
perform_step_3(credential, session_id, params)
|
|
184
|
+
else
|
|
185
|
+
raise Legate::Auth::Error, "Invalid authentication step: #{step}"
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def to_h
|
|
190
|
+
{
|
|
191
|
+
scheme_type: scheme_type,
|
|
192
|
+
active_sessions: @auth_state.keys.length
|
|
193
|
+
}
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
private
|
|
197
|
+
|
|
198
|
+
def perform_step_1(credential, session_id)
|
|
199
|
+
username = credential[:username] || credential[:username, resolve_env: true]
|
|
200
|
+
password = credential[:password] || credential[:password, resolve_env: true]
|
|
201
|
+
|
|
202
|
+
raise Legate::Auth::Error, 'Username required for step 1' unless username
|
|
203
|
+
raise Legate::Auth::Error, 'Password required for step 1' unless password
|
|
204
|
+
|
|
205
|
+
# Simulate password validation
|
|
206
|
+
raise Legate::Auth::Error, 'Invalid credentials' unless username && password
|
|
207
|
+
|
|
208
|
+
# Store session state
|
|
209
|
+
@auth_state[session_id] = {
|
|
210
|
+
username: username,
|
|
211
|
+
step: 1,
|
|
212
|
+
timestamp: Time.now,
|
|
213
|
+
verified_factors: []
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
step_1_token = Base64.strict_encode64("step1:#{session_id}:#{username}")
|
|
217
|
+
|
|
218
|
+
{
|
|
219
|
+
headers: {
|
|
220
|
+
'X-Auth-Step' => '1',
|
|
221
|
+
'X-Auth-Token' => step_1_token,
|
|
222
|
+
'X-Session-ID' => session_id
|
|
223
|
+
},
|
|
224
|
+
next_step: 2,
|
|
225
|
+
session_id: session_id
|
|
226
|
+
}
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
def perform_step_2(credential, session_id, params)
|
|
230
|
+
session = @auth_state[session_id]
|
|
231
|
+
raise Legate::Auth::Error, 'Invalid session' unless session
|
|
232
|
+
raise Legate::Auth::Error, 'Must complete step 1 first' unless session[:step] >= 1
|
|
233
|
+
|
|
234
|
+
# Second factor (could be TOTP, SMS, etc.)
|
|
235
|
+
second_factor = credential[:second_factor] || params[:second_factor] || '123456'
|
|
236
|
+
|
|
237
|
+
# Simulate second factor validation
|
|
238
|
+
raise Legate::Auth::Error, 'Invalid second factor' unless second_factor == '123456' # Demo validation
|
|
239
|
+
|
|
240
|
+
session[:step] = 2
|
|
241
|
+
session[:verified_factors] << 'password'
|
|
242
|
+
session[:verified_factors] << 'second_factor'
|
|
243
|
+
|
|
244
|
+
step_2_token = Base64.strict_encode64("step2:#{session_id}:#{session[:username]}")
|
|
245
|
+
|
|
246
|
+
{
|
|
247
|
+
headers: {
|
|
248
|
+
'X-Auth-Step' => '2',
|
|
249
|
+
'X-Auth-Token' => step_2_token,
|
|
250
|
+
'X-Session-ID' => session_id
|
|
251
|
+
},
|
|
252
|
+
next_step: 3,
|
|
253
|
+
session_id: session_id
|
|
254
|
+
}
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
def perform_step_3(_credential, session_id, _params)
|
|
258
|
+
session = @auth_state[session_id]
|
|
259
|
+
raise Legate::Auth::Error, 'Invalid session' unless session
|
|
260
|
+
raise Legate::Auth::Error, 'Must complete step 2 first' unless session[:step] >= 2
|
|
261
|
+
|
|
262
|
+
# Generate final access token
|
|
263
|
+
session[:step] = 3
|
|
264
|
+
session[:completed_at] = Time.now
|
|
265
|
+
|
|
266
|
+
final_token = Base64.strict_encode64("final:#{session_id}:#{session[:username]}:#{Time.now.to_i}")
|
|
267
|
+
|
|
268
|
+
{
|
|
269
|
+
headers: {
|
|
270
|
+
'Authorization' => "Bearer #{final_token}",
|
|
271
|
+
'X-Auth-Complete' => 'true'
|
|
272
|
+
},
|
|
273
|
+
access_token: final_token,
|
|
274
|
+
session_id: session_id,
|
|
275
|
+
authenticated: true
|
|
276
|
+
}
|
|
277
|
+
end
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
# 4. Conditional Authentication Scheme
|
|
281
|
+
# Applies different authentication methods based on request context
|
|
282
|
+
class ConditionalAuthScheme < Legate::Auth::Scheme
|
|
283
|
+
def initialize
|
|
284
|
+
super()
|
|
285
|
+
@api_key_scheme = Legate::Auth::Schemes::ApiKey.new
|
|
286
|
+
@bearer_scheme = Legate::Auth::Schemes::HTTPBearer.new
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
def scheme_type
|
|
290
|
+
:conditional
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
def apply_to_request(credential, params = {})
|
|
294
|
+
# Determine authentication method based on request context
|
|
295
|
+
auth_method = determine_auth_method(params)
|
|
296
|
+
|
|
297
|
+
case auth_method
|
|
298
|
+
when :api_key
|
|
299
|
+
puts 'Using API Key authentication for this request'
|
|
300
|
+
@api_key_scheme.apply_to_request(credential, params)
|
|
301
|
+
when :bearer
|
|
302
|
+
puts 'Using Bearer token authentication for this request'
|
|
303
|
+
@bearer_scheme.apply_to_request(credential, params)
|
|
304
|
+
when :none
|
|
305
|
+
puts 'No authentication required for this request'
|
|
306
|
+
{ headers: {} }
|
|
307
|
+
else
|
|
308
|
+
raise Legate::Auth::Error, 'No suitable authentication method available'
|
|
309
|
+
end
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
def to_h
|
|
313
|
+
{
|
|
314
|
+
scheme_type: scheme_type,
|
|
315
|
+
available_methods: %i[api_key bearer none]
|
|
316
|
+
}
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
private
|
|
320
|
+
|
|
321
|
+
def determine_auth_method(params)
|
|
322
|
+
url = params[:url] || ''
|
|
323
|
+
method = params[:method] || 'GET'
|
|
324
|
+
headers = params[:headers] || {}
|
|
325
|
+
|
|
326
|
+
# Public endpoints don't need auth
|
|
327
|
+
return :none if url.include?('/public/') || url.include?('/health')
|
|
328
|
+
|
|
329
|
+
# API endpoints prefer API key
|
|
330
|
+
return :api_key if url.include?('/api/') && method != 'GET'
|
|
331
|
+
|
|
332
|
+
# Authenticated endpoints use bearer tokens
|
|
333
|
+
return :bearer if headers['Accept']&.include?('application/json')
|
|
334
|
+
|
|
335
|
+
# Default to API key
|
|
336
|
+
:api_key
|
|
337
|
+
end
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
# 5. Custom Authentication Middleware
|
|
341
|
+
# Demonstrates custom middleware that can handle multiple schemes
|
|
342
|
+
class CustomAuthMiddleware
|
|
343
|
+
def initialize(app, schemes: {}, default_scheme: nil)
|
|
344
|
+
@app = app
|
|
345
|
+
@schemes = schemes
|
|
346
|
+
@default_scheme = default_scheme
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
def call(env)
|
|
350
|
+
# Extract request information
|
|
351
|
+
request_path = env['PATH_INFO'] || '/'
|
|
352
|
+
request_method = env['REQUEST_METHOD'] || 'GET'
|
|
353
|
+
|
|
354
|
+
# Determine which scheme to use
|
|
355
|
+
scheme_name = determine_scheme(request_path, request_method)
|
|
356
|
+
scheme = @schemes[scheme_name] || @schemes[@default_scheme]
|
|
357
|
+
|
|
358
|
+
if scheme
|
|
359
|
+
# Apply authentication
|
|
360
|
+
begin
|
|
361
|
+
auth_result = scheme.apply_to_request(
|
|
362
|
+
get_credential_for_scheme(scheme_name),
|
|
363
|
+
{
|
|
364
|
+
url: request_path,
|
|
365
|
+
method: request_method,
|
|
366
|
+
headers: extract_headers(env)
|
|
367
|
+
}
|
|
368
|
+
)
|
|
369
|
+
|
|
370
|
+
# Add auth headers to the request
|
|
371
|
+
if auth_result[:headers]
|
|
372
|
+
auth_result[:headers].each do |key, value|
|
|
373
|
+
env["HTTP_#{key.upcase.tr('-', '_')}"] = value
|
|
374
|
+
end
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
puts "Applied #{scheme_name} authentication to #{request_method} #{request_path}"
|
|
378
|
+
rescue Legate::Auth::Error => e
|
|
379
|
+
puts "Authentication failed: #{e.message}"
|
|
380
|
+
return [401, { 'Content-Type' => 'text/plain' }, ['Authentication required']]
|
|
381
|
+
end
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
@app.call(env)
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
private
|
|
388
|
+
|
|
389
|
+
def determine_scheme(path, _method)
|
|
390
|
+
return :multi_step if path.include?('/secure/')
|
|
391
|
+
return :conditional if path.include?('/api/')
|
|
392
|
+
return :custom_digest if path.include?('/digest/')
|
|
393
|
+
|
|
394
|
+
@default_scheme
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
def get_credential_for_scheme(scheme_name)
|
|
398
|
+
# In a real application, you'd retrieve appropriate credentials
|
|
399
|
+
case scheme_name
|
|
400
|
+
when :custom_basic, :custom_digest, :multi_step
|
|
401
|
+
Legate::Auth::Credential.new(
|
|
402
|
+
auth_type: :basic,
|
|
403
|
+
username: 'demo_user',
|
|
404
|
+
password: 'demo_pass'
|
|
405
|
+
)
|
|
406
|
+
when :conditional
|
|
407
|
+
Legate::Auth::Credential.new(
|
|
408
|
+
auth_type: :api_key,
|
|
409
|
+
api_key: 'demo_api_key_123'
|
|
410
|
+
)
|
|
411
|
+
else
|
|
412
|
+
Legate::Auth::Credential.new(
|
|
413
|
+
auth_type: :api_key,
|
|
414
|
+
api_key: 'default_key_456'
|
|
415
|
+
)
|
|
416
|
+
end
|
|
417
|
+
end
|
|
418
|
+
|
|
419
|
+
def extract_headers(env)
|
|
420
|
+
headers = {}
|
|
421
|
+
env.each do |key, value|
|
|
422
|
+
if key.start_with?('HTTP_')
|
|
423
|
+
header_name = key[5..-1].split('_').map(&:capitalize).join('-')
|
|
424
|
+
headers[header_name] = value
|
|
425
|
+
end
|
|
426
|
+
end
|
|
427
|
+
headers
|
|
428
|
+
end
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
# Demonstration based on selected flow
|
|
432
|
+
case options[:flow]
|
|
433
|
+
when 'basic'
|
|
434
|
+
puts '=== Custom Basic Authentication Demo ==='
|
|
435
|
+
|
|
436
|
+
scheme = CustomBasicAuthScheme.new(
|
|
437
|
+
realm: 'Demo Protected Area',
|
|
438
|
+
custom_header: 'X-Custom-Auth'
|
|
439
|
+
)
|
|
440
|
+
|
|
441
|
+
credential = Legate::Auth::Credential.new(
|
|
442
|
+
auth_type: :basic,
|
|
443
|
+
username: 'demo_user',
|
|
444
|
+
password: 'secret_password'
|
|
445
|
+
)
|
|
446
|
+
|
|
447
|
+
puts "Created custom basic auth scheme with realm: 'Demo Protected Area'"
|
|
448
|
+
puts "Scheme type: #{scheme.scheme_type}"
|
|
449
|
+
puts
|
|
450
|
+
|
|
451
|
+
# Apply authentication
|
|
452
|
+
result = scheme.apply_to_request(credential)
|
|
453
|
+
puts 'Authentication applied successfully!'
|
|
454
|
+
puts 'Headers added:'
|
|
455
|
+
result[:headers].each { |k, v| puts " #{k}: #{v}" }
|
|
456
|
+
|
|
457
|
+
when 'digest'
|
|
458
|
+
puts '=== Custom Digest Authentication Demo ==='
|
|
459
|
+
|
|
460
|
+
scheme = CustomDigestAuthScheme.new(realm: 'Digest Protected')
|
|
461
|
+
|
|
462
|
+
credential = Legate::Auth::Credential.new(
|
|
463
|
+
auth_type: :basic,
|
|
464
|
+
username: 'digest_user',
|
|
465
|
+
password: 'digest_pass'
|
|
466
|
+
)
|
|
467
|
+
|
|
468
|
+
puts 'Created custom digest auth scheme'
|
|
469
|
+
puts "Scheme type: #{scheme.scheme_type}"
|
|
470
|
+
puts
|
|
471
|
+
|
|
472
|
+
# Simulate multiple requests with digest auth
|
|
473
|
+
3.times do |i|
|
|
474
|
+
puts "Request #{i + 1}:"
|
|
475
|
+
result = scheme.apply_to_request(
|
|
476
|
+
credential,
|
|
477
|
+
{
|
|
478
|
+
uri: "/protected/resource#{i + 1}",
|
|
479
|
+
method: 'GET',
|
|
480
|
+
nonce: SecureRandom.hex(16)
|
|
481
|
+
}
|
|
482
|
+
)
|
|
483
|
+
|
|
484
|
+
auth_header = result[:headers]['Authorization']
|
|
485
|
+
puts " Authorization: #{auth_header[0..80]}..." if auth_header.length > 80
|
|
486
|
+
puts
|
|
487
|
+
end
|
|
488
|
+
|
|
489
|
+
when 'multi_step'
|
|
490
|
+
puts '=== Multi-Step Authentication Demo ==='
|
|
491
|
+
|
|
492
|
+
scheme = MultiStepAuthScheme.new
|
|
493
|
+
|
|
494
|
+
credential = Legate::Auth::Credential.new(
|
|
495
|
+
auth_type: :basic,
|
|
496
|
+
username: 'multi_user',
|
|
497
|
+
password: 'multi_pass',
|
|
498
|
+
second_factor: '123456'
|
|
499
|
+
)
|
|
500
|
+
|
|
501
|
+
puts 'Created multi-step auth scheme'
|
|
502
|
+
puts "Scheme type: #{scheme.scheme_type}"
|
|
503
|
+
puts
|
|
504
|
+
|
|
505
|
+
session_id = nil
|
|
506
|
+
|
|
507
|
+
# Step 1
|
|
508
|
+
puts 'Step 1: Initial authentication'
|
|
509
|
+
result1 = scheme.apply_to_request(credential, { step: 1 })
|
|
510
|
+
session_id = result1[:session_id]
|
|
511
|
+
puts " Next step: #{result1[:next_step]}"
|
|
512
|
+
puts " Session ID: #{session_id}"
|
|
513
|
+
puts " Headers: #{result1[:headers]}"
|
|
514
|
+
puts
|
|
515
|
+
|
|
516
|
+
# Step 2
|
|
517
|
+
puts 'Step 2: Second factor authentication'
|
|
518
|
+
result2 = scheme.apply_to_request(credential, { step: 2, session_id: session_id })
|
|
519
|
+
puts " Next step: #{result2[:next_step]}"
|
|
520
|
+
puts " Headers: #{result2[:headers]}"
|
|
521
|
+
puts
|
|
522
|
+
|
|
523
|
+
# Step 3
|
|
524
|
+
puts 'Step 3: Final token generation'
|
|
525
|
+
result3 = scheme.apply_to_request(credential, { step: 3, session_id: session_id })
|
|
526
|
+
puts " Authenticated: #{result3[:authenticated]}"
|
|
527
|
+
puts " Access token: #{result3[:access_token][0..20]}..."
|
|
528
|
+
puts " Headers: #{result3[:headers]}"
|
|
529
|
+
|
|
530
|
+
when 'conditional'
|
|
531
|
+
puts '=== Conditional Authentication Demo ==='
|
|
532
|
+
|
|
533
|
+
scheme = ConditionalAuthScheme.new
|
|
534
|
+
|
|
535
|
+
# Create a credential that can work with multiple auth types
|
|
536
|
+
credential = Legate::Auth::Credential.new(
|
|
537
|
+
auth_type: :api_key,
|
|
538
|
+
api_key: 'demo_api_key_789',
|
|
539
|
+
bearer_token: 'demo_bearer_token_456'
|
|
540
|
+
)
|
|
541
|
+
|
|
542
|
+
puts 'Created conditional auth scheme'
|
|
543
|
+
puts "Scheme type: #{scheme.scheme_type}"
|
|
544
|
+
puts
|
|
545
|
+
|
|
546
|
+
# Test different request contexts
|
|
547
|
+
test_requests = [
|
|
548
|
+
{ url: '/public/info', method: 'GET', description: 'Public endpoint' },
|
|
549
|
+
{ url: '/api/users', method: 'POST', description: 'API endpoint (non-GET)' },
|
|
550
|
+
{ url: '/dashboard', method: 'GET', headers: { 'Accept' => 'application/json' }, description: 'JSON API request' },
|
|
551
|
+
{ url: '/health', method: 'GET', description: 'Health check endpoint' },
|
|
552
|
+
{ url: '/api/data', method: 'GET', description: 'API endpoint (GET)' }
|
|
553
|
+
]
|
|
554
|
+
|
|
555
|
+
test_requests.each do |req|
|
|
556
|
+
puts "Testing: #{req[:description]}"
|
|
557
|
+
puts " #{req[:method]} #{req[:url]}"
|
|
558
|
+
|
|
559
|
+
begin
|
|
560
|
+
result = scheme.apply_to_request(credential, req)
|
|
561
|
+
puts " Result: #{result[:headers].any? ? result[:headers] : 'No authentication required'}"
|
|
562
|
+
rescue StandardError => e
|
|
563
|
+
puts " Error: #{e.message}"
|
|
564
|
+
end
|
|
565
|
+
puts
|
|
566
|
+
end
|
|
567
|
+
|
|
568
|
+
else
|
|
569
|
+
puts "Unknown flow: #{options[:flow]}"
|
|
570
|
+
exit 1
|
|
571
|
+
end
|
|
572
|
+
|
|
573
|
+
# Custom middleware demonstration
|
|
574
|
+
puts "\n=== Custom Authentication Middleware Demo ==="
|
|
575
|
+
|
|
576
|
+
# Create a simple WSGI-style app for demonstration
|
|
577
|
+
demo_app = lambda do |env|
|
|
578
|
+
path = env['PATH_INFO']
|
|
579
|
+
method = env['REQUEST_METHOD']
|
|
580
|
+
[200, { 'Content-Type' => 'text/plain' }, ["Success: #{method} #{path}"]]
|
|
581
|
+
end
|
|
582
|
+
|
|
583
|
+
# Create custom middleware with multiple schemes
|
|
584
|
+
middleware = CustomAuthMiddleware.new(
|
|
585
|
+
demo_app,
|
|
586
|
+
schemes: {
|
|
587
|
+
custom_basic: CustomBasicAuthScheme.new,
|
|
588
|
+
custom_digest: CustomDigestAuthScheme.new,
|
|
589
|
+
multi_step: MultiStepAuthScheme.new,
|
|
590
|
+
conditional: ConditionalAuthScheme.new
|
|
591
|
+
},
|
|
592
|
+
default_scheme: :custom_basic
|
|
593
|
+
)
|
|
594
|
+
|
|
595
|
+
# Simulate requests through the middleware
|
|
596
|
+
test_paths = [
|
|
597
|
+
['GET', '/public/info'],
|
|
598
|
+
['GET', '/api/data'],
|
|
599
|
+
['POST', '/secure/upload'],
|
|
600
|
+
['GET', '/digest/protected']
|
|
601
|
+
]
|
|
602
|
+
|
|
603
|
+
puts 'Simulating requests through custom middleware:'
|
|
604
|
+
test_paths.each do |method, path|
|
|
605
|
+
puts "\n#{method} #{path}:"
|
|
606
|
+
env = {
|
|
607
|
+
'REQUEST_METHOD' => method,
|
|
608
|
+
'PATH_INFO' => path,
|
|
609
|
+
'HTTP_ACCEPT' => 'application/json'
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
status, headers, body = middleware.call(env)
|
|
613
|
+
puts " Status: #{status}"
|
|
614
|
+
puts " Response: #{body.first}" if body.first
|
|
615
|
+
end
|
|
616
|
+
|
|
617
|
+
puts "\n=== Custom Authentication Flows Demo Complete ==="
|
|
618
|
+
puts "\nKey concepts demonstrated:"
|
|
619
|
+
puts '• Custom authentication scheme implementation'
|
|
620
|
+
puts '• Multi-step authentication workflows'
|
|
621
|
+
puts '• Conditional authentication based on request context'
|
|
622
|
+
puts '• Custom authentication middleware'
|
|
623
|
+
puts '• State management in authentication flows'
|
|
624
|
+
puts '• Integration of multiple authentication methods'
|
|
625
|
+
puts "\nThese patterns can be used to implement complex authentication"
|
|
626
|
+
puts 'requirements that go beyond standard OAuth2/API key patterns.'
|