From ebb780bfc8909552eb70d2f7b761f810e2d97393 Mon Sep 17 00:00:00 2001 From: Chris Arcand Date: Wed, 4 Feb 2026 09:49:26 -0600 Subject: [PATCH] Add support for custom mount paths in AppRole authentication Adds optional mount parameter to approle() method to support AppRole authentication against non-default mount points. Usage: # Default mount point Vault.auth.approle(role_id, secret_id) # Custom mount point Vault.auth.approle(role_id, secret_id, mount: "my-approle") The implementation uses an options hash (mount:) to maintain consistency with other auth methods like userpass and ldap, and to allow for future extensibility. Fully backward compatible - defaults to 'approle' mount. Changes: - Updated approle() to accept options hash with :mount parameter - Fixed deprecated JSON.fast_generate to JSON.generate - Added integration test for custom mount path - Added documentation examples for both default and custom mounts - Updated CHANGELOG Co-authored-by: Laurent Lafage --- CHANGELOG.md | 1 + lib/vault/api/auth.rb | 17 ++++++++++++++--- spec/integration/api/auth_spec.rb | 27 +++++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 457e68a..9c981bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ IMPROVEMENTS - Added `cluster_address` field to `LeaderStatus` response from `sys/leader` endpoint [GH-204] - Updated AppRole `set_role` documentation to include modern parameters like `secret_id_bound_cidrs`, `token_bound_cidrs`, and `token_policies`. Added reference to official Vault API docs for complete parameter list. [GH-220] +- Added support for custom mount paths in AppRole authentication via `mount:` option [GH-292] BUG FIXES diff --git a/lib/vault/api/auth.rb b/lib/vault/api/auth.rb index b90420e..0069aec 100644 --- a/lib/vault/api/auth.rb +++ b/lib/vault/api/auth.rb @@ -81,21 +81,32 @@ def app_id(app_id, user_id, options = {}) # successful, the resulting token will be stored on the client and used for # future requests. # - # @example + # @example Default mount point # Vault.auth.approle( # "db02de05-fa39-4855-059b-67221c5c2f63", # "6a174c20-f6de-a53c-74d2-6018fcceff64", # ) #=> # # + # @example Custom mount point + # Vault.auth.approle( + # "db02de05-fa39-4855-059b-67221c5c2f63", + # "6a174c20-f6de-a53c-74d2-6018fcceff64", + # mount: "my-approle" + # ) #=> # + # # @param [String] role_id # @param [String] secret_id (default: nil) # It is required when `bind_secret_id` is enabled for the specified role_id + # @param [Hash] options + # @option options [String] :mount (default: "approle") + # The path where the approle auth backend is mounted # # @return [Secret] - def approle(role_id, secret_id=nil) + def approle(role_id, secret_id=nil, options = {}) + mount = options[:mount] || 'approle' payload = { role_id: role_id } payload[:secret_id] = secret_id if secret_id - json = client.post("/v1/auth/approle/login", JSON.generate(payload)) + json = client.post("/v1/auth/#{CGI.escape(mount)}/login", JSON.generate(payload)) secret = Secret.decode(json) client.token = secret.auth.client_token return secret diff --git a/spec/integration/api/auth_spec.rb b/spec/integration/api/auth_spec.rb index f387bb6..cf1c0c1 100644 --- a/spec/integration/api/auth_spec.rb +++ b/spec/integration/api/auth_spec.rb @@ -86,6 +86,33 @@ module Vault expect(subject.token).to eq(result.auth.client_token) end end + + context "when using a custom mount path" do + before(:context) do + @custom_path = "custom-approle" + @custom_approle = "custom-role" + vault_test_client.sys.enable_auth(@custom_path, "approle", nil) + + # Use logical.write to configure the role on the custom mount + vault_test_client.logical.write("auth/#{@custom_path}/role/#{@custom_approle}", { + secret_id_ttl: "10m", + token_ttl: "20m", + }) + + @role_id = vault_test_client.logical.read("auth/#{@custom_path}/role/#{@custom_approle}/role-id").data[:role_id] + @secret_id = vault_test_client.logical.write("auth/#{@custom_path}/role/#{@custom_approle}/secret-id", {}).data[:secret_id] + end + + after(:context) do + vault_test_client.logical.delete("auth/#{@custom_path}/role/#{@custom_approle}") + vault_test_client.sys.disable_auth(@custom_path) + end + + it "authenticates using the custom mount path" do + result = subject.auth.approle(@role_id, @secret_id, mount: @custom_path) + expect(subject.token).to eq(result.auth.client_token) + end + end end describe "#userpass" do