Repository: hashicorp/vault
Pull request #30700: UI: (Enterprise) Login form customization feature
Author: login: hellobontempo
Labels: name: ui
name: hashicorp-contributed-pr
description: If the PR is HashiCorp (i.e. not-community) contributed
Created: 2025-05-20T23:17:30Z | Merged: 2025-05-22T22:13:15Z

### Description
Users can simplify the web UI login experience and remove unwanted login methods by choosing a default auth method (and optional backup methods). These settings can be inherited by child namespaces. 

Frontend compliment to https://github.com/hashicorp/vault-enterprise/pull/8097. Closes [9076](https://github.com/hashicorp/vault/issues/9076).

#### _**To encourage best practices, creating custom login rules not supported as a form in the web ui and must happen via the API or CLI.**_ 

<hr>

> #### [listing_visibility](https://developer.hashicorp.com/vault/api-docs/system/auth#listing_visibility-1)
> The resulting login views vary slightly depending on whether or not the selected default (or backup) auth method has any mounts tuned with `listing_visibility="unauth"`. If mount data is returned by the unauthenticated request to [sys/internal/ui/mounts](https://developer.hashicorp.com/vault/api-docs/system/internal-ui-mounts#get-available-visible-mounts), then the web UI uses that path to log in and hides the path `<input>`.
> If no data is returned, the web UI assumes the default login path (which is the same as the method name, i.e. `ldap`), but the custom input is available. 

<hr> 

## ⚙️ Configuration options:
The login form either renders a `dropdown` or `tabs` depending on specific configuration combinations.
In some scenarios, the component supports toggling between a default view and an alternate view. Users can use a direct link to override any login settings or default behavior.

📋 **Dropdown** (default view)
▸ All supported auth methods show in a dropdown.
▸ No alternate view.

🗂️ **Visible mount tabs** (visible (unauth) mount tabs)
▸ Groups visible mounts (`listing_visibility="unauth"`) by type and displays as tabs.
▸ Alternate view: full dropdown of all methods.

🔗 **Direct link** (via `?with=` query param)
▸ If the param references a visible mount, that method renders by default and the mount path is assumed.
↳ Alternate view: full dropdown.
▸ If the param references a method type (legacy behavior), the method is preselected in the dropdown or its tab is selected.
↳ Alternate view: if other methods have visible mounts, the form can toggle between tabs and dropdown. The initial view depends on whether the chosen type is a tab.

🏢 **Enterprise-only login settings**
▸ A namespace can define a default method and/or preferred methods (i.e. "backups") and enable child namespaces to inherit these preferences.
✎ Both set:
▸ Default method shown initially.
▸ Alternate view: preferred methods in tab layout.
✎ Only one set:
▸ No alternate view.

🛠️ Advanced settings toggle reveals the custom path input:
🚫 No visible mounts:
▸ UI defaults to method type as path.
▸ "Advanced settings" shows a path input.
1️⃣ One visible mount:
▸ Path is assumed and hidden.
🔀 Multiple visible mounts:
▸ Path dropdown is shown.

## 📸 Screenshots 
### Default + Backup methods set with mount data

- `Advanced settings` toggle is hidden because method has mount data available
- Clicking "Sign in to view other methods" toggles to view backup methods
  _`Advanced settings` hides/shows for each tab depending on mount visibility_
  
<img alt="Screenshot 1" src="https://github.com/user-attachments/assets/654e5569-9a9b-4406-928b-d55b75d00ab8" width="450" />
<img alt="Screenshot 2" src="https://github.com/user-attachments/assets/2d0d94b9-e940-49f5-b31a-f4c4a4f3c43f" width="450" />

---

### Default + Backup methods set with **no** mount data

- UI assumes default login path for method and `Advanced settings` renders

<img alt="Screenshot 3" src="https://github.com/user-attachments/assets/1c60bc6c-3f5f-408c-915c-085481cd0a4a" width="450" />

### Default only
- Default method is set **without** backups so it is the only option. "Sign in with other methods" does not render

<img alt="Screenshot 4" src="https://github.com/user-attachments/assets/39213827-8e09-4567-b735-3baa4b250526" width="450" />

### Direct links
- `?with=` query param refers to an auth method **type** it is preselected in the dropdown (backward compatibility)

<img width="450" alt="Screenshot 2025-05-20 at 5 44 42 PM" src="https://github.com/user-attachments/assets/32c9e113-a660-49a2-8b6f-32273d64eb8f" />
- `?with=` query param refers to a visible mount path

<img width="450" alt="Screenshot 2025-05-20 at 5 40 46 PM" src="https://github.com/user-attachments/assets/9975a63f-12a1-4523-88ee-0fcabe6e5ae3" />


### TODO only if you're a HashiCorp employee
- [ ] **Backport Labels:** If this fix needs to be backported, use the appropriate `backport/` label that matches the desired release branch. Note that in the CE repo, the latest release branch will look like `backport/x.x.x`, but older release branches will be `backport/ent/x.x.x+ent`.
    - [ ] **LTS**: If this fixes a critical security vulnerability or [severity 1](https://www.hashicorp.com/customer-success/enterprise-support) bug, it will also need to be backported to the current [LTS versions](https://developer.hashicorp.com/vault/docs/enterprise/lts#why-is-there-a-risk-to-updating-to-a-non-lts-vault-enterprise-version) of Vault. To ensure this, use **all** available enterprise labels.
- [ ] **ENT Breakage:** If this PR either 1) removes a public function OR 2) changes the signature
  of a public function, even if that change is in a CE file, _double check_ that
  applying the patch for this PR to the ENT repo and running tests doesn't
  break any tests. Sometimes ENT only tests rely on public functions in CE
  files.
- [ ] **Jira:** If this change has an associated Jira, it's referenced either
  in the PR description, commit message, or branch name.
- [ ] **RFC:** If this change has an associated RFC, please link it in the description.
- [ ] **ENT PR:** If this change has an associated ENT PR, please link it in the
  description. Also, make sure the changelog is in this PR, _not_ in your ENT PR.



Diff:
diff --git a/changelog/30700.txt b/changelog/30700.txt
new file mode 100644
index 00000000000..91f3e925ee0
--- /dev/null
+++ b/changelog/30700.txt
@@ -0,0 +1,3 @@
+```release-note:feature
+**Login form customization** (enterprise): Adds support to choose a default and/or backup auth methods for the web UI login form to streamline the web UI login experience.
+```
\ No newline at end of file
diff --git a/ui/app/components/auth/form-template.hbs b/ui/app/components/auth/form-template.hbs
index b4a24c1c470..c5873a16551 100644
--- a/ui/app/components/auth/form-template.hbs
+++ b/ui/app/components/auth/form-template.hbs
@@ -3,105 +3,103 @@
   SPDX-License-Identifier: BUSL-1.1
 }}
 
-<div {{did-insert this.initializeState}}>
-  {{#if this.formComponent}}
-    {{#let (component this.formComponent) as |AuthFormComponent|}}
-      {{! renders Auth::Form::Base or Auth::Form::<Type>}}
-      <AuthFormComponent
-        @authType={{this.selectedAuthMethod}}
-        @cluster={{@cluster}}
-        @onError={{this.handleError}}
-        @onSuccess={{@onSuccess}}
-      >
-        <:namespace>
-          {{#if (has-feature "Namespaces")}}
-            <Auth::NamespaceInput
-              @disabled={{@oidcProviderQueryParam}}
-              @hvdManagedNamespace={{this.flags.hvdManagedNamespaceRoot}}
-              @namespaceValue={{this.namespaceInput}}
-              @updateNamespace={{this.handleNamespaceUpdate}}
-            />
-          {{/if}}
-        </:namespace>
+{{#if this.formComponent}}
+  {{#let (component this.formComponent) as |AuthFormComponent|}}
+    {{! renders Auth::Form::Base or Auth::Form::<Type>}}
+    <AuthFormComponent
+      @authType={{this.selectedAuthMethod}}
+      @cluster={{@cluster}}
+      @onError={{this.handleError}}
+      @onSuccess={{@onSuccess}}
+    >
+      <:namespace>
+        {{#if (has-feature "Namespaces")}}
+          <Auth::NamespaceInput
+            @disabled={{@oidcProviderQueryParam}}
+            @hvdManagedNamespace={{this.flags.hvdManagedNamespaceRoot}}
+            @namespaceValue={{this.namespaceInput}}
+            @updateNamespace={{this.handleNamespaceUpdate}}
+          />
+        {{/if}}
+      </:namespace>
 
-        <:back>
-          {{#if this.showOtherMethods}}
-            <Hds::Button
-              @text="Back"
-              {{on "click" this.toggleView}}
-              @color="tertiary"
-              @icon="arrow-left"
-              data-test-back-button
+      <:back>
+        {{#if this.showAlternateView}}
+          <Hds::Button
+            @text="Back"
+            {{on "click" this.toggleView}}
+            @color="tertiary"
+            @icon="arrow-left"
+            data-test-back-button
+          />
+        {{/if}}
+      </:back>
+
+      {{! DIRECT LINK, TABS OR DROPDOWN }}
+      <:authSelectOptions>
+        <div class="has-bottom-margin-m">
+          {{#if this.tabData}}
+            <Auth::Tabs
+              @authTabData={{this.tabData}}
+              @handleTabClick={{this.setAuthType}}
+              @selectedAuthMethod={{this.selectedAuthMethod}}
             />
+          {{else}}
+            {{! Fallback is dropdown with all auth methods }}
+            <Hds::Form::Select::Field
+              name="selectedAuthMethod"
+              {{on "input" this.setTypeFromDropdown}}
+              data-test-select="auth type"
+              as |F|
+            >
+              <F.Label class="has-top-margin-m">Method</F.Label>
+              <F.Options>
+                {{#each this.supportedAuthTypes as |type|}}
+                  <option selected={{eq this.selectedAuthMethod type}} value={{type}}>
+                    {{auth-display-name type}}
+                  </option>
+                {{/each}}
+              </F.Options>
+            </Hds::Form::Select::Field>
           {{/if}}
-        </:back>
-
-        {{! DIRECT LINK, TABS OR DROPDOWN }}
-        <:authSelectOptions>
-          <div class="has-bottom-margin-m">
-            {{#if this.showCustomAuthOptions}}
-              <Auth::Tabs
-                @authTabData={{this.tabData}}
-                @handleTabClick={{this.setAuthType}}
-                @selectedAuthMethod={{this.selectedAuthMethod}}
-              />
-            {{else}}
-              {{! fallback view is the dropdown with all auth methods }}
-              <Hds::Form::Select::Field
-                name="selectedAuthMethod"
-                {{on "input" this.setTypeFromDropdown}}
-                data-test-select="auth type"
-                as |F|
-              >
-                <F.Label class="has-top-margin-m">Auth method</F.Label>
-                <F.Options>
-                  {{#each this.availableMethodTypes as |type|}}
-                    <option selected={{eq this.selectedAuthMethod type}} value={{type}}>
-                      {{auth-display-name type}}
-                    </option>
-                  {{/each}}
-                </F.Options>
-              </Hds::Form::Select::Field>
-            {{/if}}
-          </div>
-        </:authSelectOptions>
+        </div>
+      </:authSelectOptions>
 
-        <:error>
-          {{#if this.errorMessage}}
-            <MessageError @errorMessage={{this.errorMessage}} />
-          {{/if}}
-        </:error>
+      <:error>
+        {{#if this.errorMessage}}
+          <MessageError @errorMessage={{this.errorMessage}} />
+        {{/if}}
+      </:error>
 
-        <:advancedSettings>
-          {{! custom auth options render their own mount path inputs and token does not support custom paths }}
-          {{#if (and (not this.showCustomAuthOptions) (not-eq this.selectedAuthMethod "token"))}}
-            <Hds::Reveal @text="Advanced settings" data-test-auth-form-options-toggle class="is-fullwidth">
-              <Hds::Form::TextInput::Field name="path" data-test-input="path" as |F|>
-                <F.Label class="has-top-margin-m">Mount path</F.Label>
-                <F.HelperText>
-                  If this authentication method was mounted using a non-default path, input it below. Otherwise Vault will
-                  assume the default path
-                  <Hds::Text::Code class="code-in-text">{{this.selectedAuthMethod}}</Hds::Text::Code>
-                  .</F.HelperText>
-              </Hds::Form::TextInput::Field>
-            </Hds::Reveal>
-          {{/if}}
-        </:advancedSettings>
+      <:advancedSettings>
+        {{! custom auth options render their own mount path inputs and token does not support custom paths }}
+        {{#unless this.hideAdvancedSettings}}
+          <Hds::Reveal @text="Advanced settings" data-test-auth-form-options-toggle class="is-fullwidth">
+            <Hds::Form::TextInput::Field name="path" data-test-input="path" as |F|>
+              <F.Label class="has-top-margin-m">Mount path</F.Label>
+              <F.HelperText>
+                If this authentication method was mounted using a non-default path, input it below. Otherwise Vault will
+                assume the default path
+                <Hds::Text::Code class="code-in-text">{{this.selectedAuthMethod}}</Hds::Text::Code>
+                .</F.HelperText>
+            </Hds::Form::TextInput::Field>
+          </Hds::Reveal>
+        {{/unless}}
+      </:advancedSettings>
 
-        <:footer>
-          {{#if this.showCustomAuthOptions}}
-            <Hds::Button
-              {{on "click" this.toggleView}}
-              @color="tertiary"
-              @icon="arrow-right"
-              @iconPosition="trailing"
-              @isInline={{true}}
-              @text="Sign in with other methods"
-              data-test-other-methods-button
-            />
-          {{/if}}
-        </:footer>
-      </AuthFormComponent>
-    {{/let}}
-  {{/if}}
-</div>
\ No newline at end of file
+      <:footer>
+        {{#if (and @alternateView (not this.showAlternateView))}}
+          <Hds::Button
+            {{on "click" this.toggleView}}
+            @color="tertiary"
+            @icon="arrow-right"
+            @iconPosition="trailing"
+            @isInline={{true}}
+            @text="Sign in with other methods"
+            data-test-other-methods-button
+          />
+        {{/if}}
+      </:footer>
+    </AuthFormComponent>
+  {{/let}}
+{{/if}}
\ No newline at end of file
diff --git a/ui/app/components/auth/form-template.ts b/ui/app/components/auth/form-template.ts
index e5d22fb5798..a0b400e5f39 100644
--- a/ui/app/components/auth/form-template.ts
+++ b/ui/app/components/auth/form-template.ts
@@ -10,12 +10,10 @@ import { action } from '@ember/object';
 import { supportedTypes } from 'vault/utils/supported-login-methods';
 import { getRelativePath } from 'core/utils/sanitize-path';
 
-import type AuthService from 'vault/vault/services/auth';
 import type FlagsService from 'vault/services/flags';
-import type Store from '@ember-data/store';
 import type VersionService from 'vault/services/version';
 import type ClusterModel from 'vault/models/cluster';
-import type { UnauthMountsByType, AuthTabMountData } from 'vault/vault/auth/form';
+import type { UnauthMountsByType } from 'vault/vault/auth/form';
 import type { HTMLElementEvent } from 'vault/forms';
 
 /**
@@ -29,64 +27,64 @@ import type { HTMLElementEvent } from 'vault/forms';
  * dynamically renders the corresponding form.
  *
  *
+ * @param {object | null} alternateView - if an alternate view exists, this is the `FormView` (see interface below) data to render that view.
  * @param {string} canceledMfaAuth - saved auth type from a cancelled mfa verification
  * @param {object} cluster - The route model which is the ember data cluster model. contains information such as cluster id, name and boolean for if the cluster is in standby
- * @param {object} directLinkData - mount data built from the "with" query param. If param is a mount path and maps to a visible mount, the login form defaults to this mount. Otherwise the form preselects the passed auth type.
+ * @param {object} defaultView - The `FormView` (see the interface below) data to render the initial view.
  * @param {function} handleNamespaceUpdate - callback task that passes user input to the controller and updates the namespace query param in the url
+ * @param {object} initialFormState - sets selectedAuthMethod and showAlternateView based on the login form configuration computed in parent component
  * @param {string} namespaceQueryParam - namespace query param from the url
  * @param {string} oidcProviderQueryParam - oidc provider query param, set in url as "?o=someprovider". if present, disables the namespace input
  * @param {function} onSuccess - callback after the initial authentication request, if an mfa_requirement exists the parent renders the mfa form otherwise it fires the authSuccess action in the auth controller and handles transitioning to the app
- * @param {object} visibleMountsByType - auth methods to render as tabs, contains mount data for any mounts with listing_visibility="unauth"
+ * @param {array} visibleMountTypes - array of auth method types that have mounts with listing_visibility="unauth"
  *
  * */
 
 interface Args {
-  canceledMfaAuth: string;
+  alternateView: FormView | null;
   cluster: ClusterModel;
-  directLinkData: (AuthTabMountData & { isVisibleMount: boolean }) | null;
+  defaultView: FormView;
   handleNamespaceUpdate: CallableFunction;
+  initialFormState: { initialAuthType: string; showAlternate: boolean };
   namespaceQueryParam: string;
   oidcProviderQueryParam: string;
   onSuccess: CallableFunction;
-  visibleMountsByType: UnauthMountsByType;
+  visibleMountTypes: string[];
+}
+
+interface FormView {
+  view: string; // "dropdown" or "tabs"
+  tabData: UnauthMountsByType | null; // tabs to render if view = "tabs"
 }
 
 export default class AuthFormTemplate extends Component<Args> {
-  @service declare readonly auth: AuthService;
   @service declare readonly flags: FlagsService;
-  @service declare readonly store: Store;
   @service declare readonly version: VersionService;
 
-  // true → "Back" button renders, false → "Sign in with other methods→" renders if customizations exist
-  @tracked showOtherMethods = false;
+  supportedAuthTypes: string[];
 
-  // auth login variables
-  @tracked selectedAuthMethod = '';
   @tracked errorMessage = '';
-
-  get tabData() {
-    const { directLinkData } = this.args;
-    // URL contains a "with" query param that references a mount with listing_visibility="unauth"
-    // Treat it as a "preferred" mount and hide all other tabs
-    if (directLinkData?.isVisibleMount && directLinkData?.type) {
-      return { [directLinkData.type]: [this.args.directLinkData] };
-    }
-    return this.args.visibleMountsByType;
-  }
-
-  get authTabTypes() {
-    const visibleMounts = this.args.visibleMountsByType;
-    return visibleMounts ? Object.keys(visibleMounts) : [];
+  @tracked selectedAuthMethod = '';
+  // true → "Back" button renders, false → "Sign in with other methods→" renders if an alternate view exists
+  @tracked showAlternateView = false;
+
+  constructor(owner: unknown, args: Args) {
+    super(owner, args);
+    const { initialAuthType, showAlternate } = this.args.initialFormState;
+    this.selectedAuthMethod = initialAuthType;
+    this.showAlternateView = showAlternate;
+    this.supportedAuthTypes = supportedTypes(this.version.isEnterprise);
   }
 
-  get availableMethodTypes() {
-    return supportedTypes(this.version.isEnterprise);
+  get tabData() {
+    if (this.showAlternateView) return this.args?.alternateView?.tabData;
+    return this.args?.defaultView?.tabData;
   }
 
   get formComponent() {
     const { selectedAuthMethod } = this;
     // isSupported means there is a component file defined for that auth type
-    const isSupported = this.availableMethodTypes.includes(selectedAuthMethod);
+    const isSupported = this.supportedAuthTypes.includes(selectedAuthMethod);
     const formFile = () => (['oidc', 'jwt'].includes(selectedAuthMethod) ? 'oidc-jwt' : selectedAuthMethod);
     const component = isSupported ? formFile() : 'base';
 
@@ -94,6 +92,17 @@ export default class AuthFormTemplate extends Component<Args> {
     return `auth/form/${component}`;
   }
 
+  get hideAdvancedSettings() {
+    // Token does not support custom paths
+    if (this.selectedAuthMethod === 'token') return true;
+
+    // Always show for dropdown mode
+    if (!this.tabData) return false;
+
+    // For remaining scenarios, hide "Advanced settings" if the selected method has visible mount(s)
+    return this.args.visibleMountTypes?.includes(this.selectedAuthMethod);
+  }
+
   get namespaceInput() {
     const namespaceQueryParam = this.args.namespaceQueryParam;
     if (this.flags.hvdManagedNamespaceRoot) {
@@ -105,42 +114,6 @@ export default class AuthFormTemplate extends Component<Args> {
     return namespaceQueryParam;
   }
 
-  get preselectedType() {
-    // Prioritize canceledMfaAuth since it's triggered by user interaction.
-    // Next, check type from directLinkData as it's specified by the URL.
-    // Finally, fall back to the most recently used auth method in localStorage.
-    return this.args.canceledMfaAuth || this.args.directLinkData?.type || this.auth.getAuthType();
-  }
-
-  // The "standard" selection is a dropdown listing all auth methods.
-  // This getter determines whether to render an alternative view (e.g., tabs or a preferred mount).
-  // If `true`, the "Sign in with other methods →" link is shown.
-  get showCustomAuthOptions() {
-    const hasLoginCustomization = this.args?.directLinkData?.isVisibleMount || this.args.visibleMountsByType;
-    // Show if customization exists and the user has NOT clicked "Sign in with other methods →"
-    return hasLoginCustomization && !this.showOtherMethods;
-  }
-
-  @action
-  initializeState() {
-    // SET AUTH TYPE
-    if (this.preselectedType) {
-      this.setAuthType(this.preselectedType);
-    } else {
-      // if nothing has been preselected, select first tab or set to 'token'
-      const authType = this.args.visibleMountsByType ? (this.authTabTypes[0] as string) : 'token';
-      this.setAuthType(authType);
-    }
-
-    // DETERMINES INITIAL RENDER: custom selection (direct link or tabs) vs dropdown
-    if (this.args.visibleMountsByType) {
-      // render tabs if selectedAuthMethod is one, otherwise render dropdown (i.e. showOtherMethods = false)
-      this.showOtherMethods = this.authTabTypes.includes(this.selectedAuthMethod) ? false : true;
-    } else {
-      this.showOtherMethods = false;
-    }
-  }
-
   @action
   setAuthType(authType: string) {
     this.selectedAuthMethod = authType;
@@ -153,15 +126,10 @@ export default class AuthFormTemplate extends Component<Args> {
 
   @action
   toggleView() {
-    this.showOtherMethods = !this.showOtherMethods;
-
-    if (this.showCustomAuthOptions) {
-      const firstTab = this.authTabTypes[0] as string;
-      this.setAuthType(firstTab);
-    } else {
-      // all methods render, reset dropdown
-      this.selectedAuthMethod = this.preselectedType || 'token';
-    }
+    this.showAlternateView = !this.showAlternateView;
+    const firstAuthTab = Object.keys(this.tabData || {})[0];
+    const type = firstAuthTab || this.args.initialFormState.initialAuthType;
+    this.setAuthType(type);
   }
 
   @action
diff --git a/ui/app/components/auth/page.hbs b/ui/app/components/auth/page.hbs
index a0fd536c650..174478ac193 100644
--- a/ui/app/components/auth/page.hbs
+++ b/ui/app/components/auth/page.hbs
@@ -50,14 +50,15 @@
         />
       {{else}}
         <Auth::FormTemplate
-          @canceledMfaAuth={{this.canceledMfaAuth}}
+          @alternateView={{this.formViews.alternateView}}
           @cluster={{@cluster}}
-          @directLinkData={{@directLinkData}}
+          @defaultView={{this.formViews.defaultView}}
           @handleNamespaceUpdate={{@onNamespaceUpdate}}
+          @initialFormState={{this.initialFormState}}
           @namespaceQueryParam={{@namespaceQueryParam}}
           @oidcProviderQueryParam={{@oidcProviderQueryParam}}
           @onSuccess={{this.onAuthResponse}}
-          @visibleMountsByType={{this.visibleMountsByType}}
+          @visibleMountTypes={{this.visibleMountTypes}}
         />
       {{/if}}
     </:content>
diff --git a/ui/app/components/auth/page.ts b/ui/app/components/auth/page.ts
index b0c1c9a3c0d..11bbbffeeb4 100644
--- a/ui/app/components/auth/page.ts
+++ b/ui/app/components/auth/page.ts
@@ -10,28 +10,66 @@ import { action } from '@ember/object';
 
 import type { AuthResponse, AuthResponseWithMfa } from 'vault/vault/services/auth';
 import type { UnauthMountsByType, UnauthMountsResponse } from 'vault/vault/auth/form';
+import type AuthService from 'vault/vault/services/auth';
 import type ClusterModel from 'vault/models/cluster';
 import type CspEventService from 'vault/services/csp-event';
 
 /**
  * @module AuthPage
- * The Auth::Page is the route template for the login splash view. It renders the Auth::FormTemplate or MFA component if an
- * mfa validation is returned from the auth request. It also formats mount data to manage what tabs are rendered in Auth::FormTemplate.
+ * Auth::Page renders the Auth::FormTemplate or MFA component if an mfa validation is returned from the auth request.
+ * It receives configuration settings from the route's model hook and determines the possible form states passed to Auth::FormTemplate.
+ * The model hook refreshes when the namespace input updates and re-requests `sys/internal/ui/mounts` and the login settings endpoint (enterprise only).
+ *
+ * ⚙️ CONFIGURATION OVERVIEW:
+ * The login form either renders a `dropdown` or `tabs` depending on specific configuration combinations.
+ * In some scenarios, the component supports toggling between a default view and an alternate view.
+ *
+ * 📋 Dropdown (default view)
+ *   ▸ All supported auth methods show in a dropdown.
+ *   ▸ No alternate view.
+ *
+ * 🗂️ Visible mount tabs
+ *   ▸ Groups visible mounts (`listing_visibility="unauth"`) by type and displays as tabs.
+ *   ▸ Alternate view: full dropdown of all methods.
+ *
+ * 🔗 Direct link (via `?with=` query param)
+ *   ▸ If the param references a visible mount, that method renders by default and the mount path is assumed.
+ *     ↳ Alternate view: full dropdown.
+ *   ▸ If the param references a method type (legacy behavior), the method is preselected in the dropdown or its tab is selected.
+ *     ↳ Alternate view: if other methods have visible mounts, the form can toggle between tabs and dropdown. The initial view depends on whether the chosen type is a tab.
+ *
+ * 🏢 Login settings * enterprise only *
+ *   ▸ A namespace can define a default method and/or preferred methods (i.e. "backups") and enable child namespaces to inherit these preferences.
+ *     ✎ Both set:
+ *       ▸ Default method shown initially.
+ *       ▸ Alternate view: preferred methods in tab layout.
+ *     ✎ Only one set:
+ *       ▸ No alternate view.
+ *
+ * 🛠️ Advanced settings toggle reveals the custom path input:
+ *   🚫 No visible mounts:
+ *     ▸ UI defaults to method type as path.
+ *     ▸ "Advanced settings" shows a path input.
+ *   1️⃣ One visible mount:
+ *     ▸ Path is assumed and hidden.
+ *   🔀 Multiple visible mounts:
+ *     ▸ Path dropdown is shown.
  *
  * @example
  * <Auth::Page
  *  @cluster={{this.model.clusterModel}}
+ *  @directLinkData={{this.model.directLinkData}}
+ *  @loginSettings={{this.model.loginSettings}}
  *  @namespaceQueryParam={{this.namespaceQueryParam}}
  *  @oidcProviderQueryParam={{this.oidcProvider}}
  *  @onAuthSuccess={{action "authSuccess"}}
  *  @onNamespaceUpdate={{perform this.updateNamespace}}
  *  @visibleAuthMounts={{this.model.visibleAuthMounts}}
- *  @directLinkData={{this.model.directLinkData}}
  * />
  *
  * @param {object} cluster - the ember data cluster model. contains information such as cluster id, name and boolean for if the cluster is in standby
  * @param {object} directLinkData - mount data built from the "with" query param. If param is a mount path and maps to a visible mount, the login form defaults to this mount. Otherwise the form preselects the passed auth type.
- * @param {object} loginSettings - * enterprise only * login settings configured for the namespace
+ * @param {object} loginSettings - * enterprise only * login settings configured for the namespace. If set, specifies a default auth method type and/or backup method types
  * @param {string} namespaceQueryParam - namespace to login with, updated by typing in to the namespace input
  * @param {string} oidcProviderQueryParam - oidc provider query param, set in url as "?o=someprovider"
  * @param {function} onAuthSuccess - callback task in controller that receives the auth response (after MFA, if enabled) when login is successful
@@ -43,18 +81,26 @@ export const CSP_ERROR =
   "This is a standby Vault node but can't communicate with the active node via request forwarding. Sign in at the active node to use the Vault UI.";
 
 interface Args {
-  visibleAuthMounts: UnauthMountsResponse;
   cluster: ClusterModel;
+  directLinkData: { type: string; path?: string } | null; // if "path" key is present then mount data is visible
+  loginSettings: { defaultType: string; backupTypes: string[] | null }; // enterprise only
   onAuthSuccess: CallableFunction;
+  visibleAuthMounts: UnauthMountsResponse;
 }
 
 interface MfaAuthData {
   mfa_requirement: object;
-  selectedAuth: string;
   path: string;
+  selectedAuth: string;
+}
+
+enum FormView {
+  DROPDOWN = 'dropdown',
+  TABS = 'tabs',
 }
 
 export default class AuthPage extends Component<Args> {
+  @service declare readonly auth: AuthService;
   @service('csp-event') declare readonly csp: CspEventService;
 
   @tracked canceledMfaAuth = '';
@@ -75,12 +121,117 @@ export default class AuthPage extends Component<Args> {
     return null;
   }
 
+  get visibleMountTypes(): string[] {
+    return Object.keys(this.visibleMountsByType || {});
+  }
+
   get cspError() {
     const isStandby = this.args.cluster.standby;
     const hasConnectionViolations = this.csp.connectionViolations.length;
     return isStandby && hasConnectionViolations ? CSP_ERROR : '';
   }
 
+  // FORM STATE GETTERS
+  get formViews() {
+    const { directLinkData, loginSettings } = this.args;
+
+    if (directLinkData) {
+      return this.directLinkViews;
+    }
+
+    if (loginSettings) {
+      return this.loginSettingsViews;
+    }
+
+    if (this.visibleMountsByType) {
+      return this.visibleMountViews;
+    }
+
+    // If none of the above, the UI renders the standard dropdown with no alternate views
+    return this.standardDropdownView;
+  }
+
+  get initialAuthType(): string {
+    // First, prioritize canceledMfaAuth since it's set by user interaction.
+    // Next, "type" from direct link since the URL query param overrides any login settings.
+    // Then, first tab which is either the first backup method or visible mount tab.
+    // Finally, fallback to the most recently used auth method in localStorage.
+    // Token is the default otherwise.
+    const directLinkType = this.args.directLinkData?.type;
+    const firstTab = Object.keys(this.formViews.defaultView?.tabData || {})[0];
+    return this.canceledMfaAuth || directLinkType || firstTab || this.auth.getAuthType() || 'token';
+  }
+
+  get directLinkViews() {
+    const { directLinkData } = this.args;
+
+    // If "path" key exists we know the "with" query param references a mount with listing_visibility="unauth"
+    // Treat it as a preferred method and hide all other tabs.
+    if (directLinkData?.path) {
+      const tabData = this.filterVisibleMountsByType([directLinkData.type]);
+      const defaultView = this.constructViews(FormView.TABS, tabData);
+      const alternateView = this.constructViews(FormView.DROPDOWN, null);
+
+      return { defaultView, alternateView };
+    }
+
+    // Otherwise, directLinkData just has a "type" key.
+    // Render either visibleMountViews or dropdown with that type preselected
+    return this.visibleMountsByType ? this.visibleMountViews : this.standardDropdownView;
+  }
+
+  get standardDropdownView() {
+    return {
+      defaultView: this.constructViews(FormView.DROPDOWN, null),
+      alternateView: null,
+    };
+  }
+
+  get loginSettingsViews() {
+    const { loginSettings } = this.args;
+    const defaultType = loginSettings?.defaultType;
+    const backupTypes = loginSettings?.backupTypes;
+
+    // If a default is not set, render backup methods as the initial view
+    const preferredTypes = defaultType ? [defaultType] : backupTypes;
+    let defaultView;
+    if (preferredTypes) {
+      const tabData = this.filterVisibleMountsByType(preferredTypes);
+      defaultView = this.constructViews(FormView.TABS, tabData);
+    }
+
+    // Both default and backups must be set for an alternate view to exist
+    let alternateView = null;
+    if (defaultType && backupTypes) {
+      const tabData = this.filterVisibleMountsByType(backupTypes);
+      alternateView = this.constructViews(FormView.TABS, tabData);
+    }
+
+    return { defaultView, alternateView };
+  }
+
+  get visibleMountViews() {
+    const defaultView = this.constructViews(FormView.TABS, this.visibleMountsByType);
+    const alternateView = this.constructViews(FormView.DROPDOWN, null);
+    return { defaultView, alternateView };
+  }
+
+  get initialFormState() {
+    const { defaultView, alternateView } = this.formViews;
+    const hasTab = (tabData: object) => Object.keys(tabData).includes(this.initialAuthType);
+    const authIsNotDefaultTab = !hasTab(defaultView?.tabData || {});
+    const hasAlternateView = !!alternateView;
+    const authIsAlternateTab = hasTab(alternateView?.tabData || {});
+
+    // In rare cases, pre-toggle the form to the fallback dropdown if the selected method is not in the alternate view.
+    // This could happen if tabs render for visible mounts and the "with" query param references a type that isn't a tab.
+    // Or auth type is preset from canceled MFA or local storage and is not in the default view.
+    const showAlternate = (authIsNotDefaultTab && hasAlternateView) || authIsAlternateTab;
+
+    return { initialAuthType: this.initialAuthType, showAlternate };
+  }
+
+  // ACTIONS
   @action
   onAuthResponse(authResponse: AuthResponse | AuthResponseWithMfa, { selectedAuth = '', path = '' }) {
     const mfa_requirement = 'mfa_requirement' in authResponse ? authResponse.mfa_requirement : undefined;
@@ -118,4 +269,18 @@ export default class AuthPage extends Component<Args> {
     this.mfaAuthData = null;
     this.mfaErrors = '';
   }
+
+  // HELPERS
+  private filterVisibleMountsByType(authTypes: string[]) {
+    const tabs: UnauthMountsByType = {};
+    for (const type of authTypes) {
+      // adds visible mounts for each type, if they exist
+      tabs[type] = this.visibleMountsByType?.[type] || null;
+    }
+    return tabs;
+  }
+
+  private constructViews(view: FormView, tabData: UnauthMountsByType | null) {
+    return { view, tabData };
+  }
 }
diff --git a/ui/app/components/auth/tabs.hbs b/ui/app/components/auth/tabs.hbs
index 1188522b0f2..cfb49cc3f95 100644
--- a/ui/app/components/auth/tabs.hbs
+++ b/ui/app/components/auth/tabs.hbs
@@ -11,7 +11,7 @@
         {{! Elements "behind" tabs always render on the DOM and are just superficially hidden/shown. 
 	        However, for accessibility, we only want to render form inputs relevant to the selected method. 
 	        By wrapping the elements in this conditional, it only renders them when the tab is selected. }}
-        {{#if (eq @selectedAuthMethod methodType)}}
+        {{#if (and mounts (eq @selectedAuthMethod methodType))}}
           {{#if (gt mounts.length 1)}}
             {{! DROPDOWN for mount paths }}
             <Hds::Form::Select::Field name="path" data-test-select="path" as |F|>
@@ -26,7 +26,12 @@
             {{! SINGLE mount path }}
             {{#let (get mounts "0") as |mount|}}
               {{#if mount.description}}
-                <Hds::Text::Body @tag="p" @color="faint" data-test-description>{{mount.description}}</Hds::Text::Body>
+                <Hds::Text::Body
+                  @tag="p"
+                  @color="faint"
+                  class="white-space-pre-line"
+                  data-test-description
+                >{{mount.description}}</Hds::Text::Body>
               {{/if}}
               {{! the token auth method does't support custom paths so no need to render an input }}
               {{#if (not-eq @selectedAuthMethod "token")}}
diff --git a/ui/app/routes/vault/cluster/auth.js b/ui/app/routes/vault/cluster/auth.js
index cb97fbbd254..596acf870c1 100644
--- a/ui/app/routes/vault/cluster/auth.js
+++ b/ui/app/routes/vault/cluster/auth.js
@@ -19,6 +19,8 @@ export default class AuthRoute extends ClusterRouteBase {
   @service api;
   @service auth;
   @service flashMessages;
+  @service namespace;
+  @service store;
   @service version;
 
   beforeModel() {
@@ -36,13 +38,15 @@ export default class AuthRoute extends ClusterRouteBase {
       return { clusterModel, unwrapResponse: authResponse };
     }
 
+    const loginSettings = this.version.isEnterprise ? await this.fetchLoginSettings() : null;
     const visibleAuthMounts = await this.fetchMounts();
     const authMount = params?.authMount;
 
     return {
       clusterModel,
       visibleAuthMounts,
-      directLinkData: authMount ? this.getMountOrTypeData(authMount, visibleAuthMounts) : null,
+      directLinkData: this.getDirectLinkData(authMount, visibleAuthMounts),
+      loginSettings,
     };
   }
 
@@ -84,6 +88,30 @@ export default class AuthRoute extends ClusterRouteBase {
     }
   }
 
+  async fetchLoginSettings() {
+    const adapter = this.store.adapterFor('application');
+    try {
+      // TODO update with api service when api-client is updated
+      const response = await adapter.ajax(
+        '/v1/sys/internal/ui/default-auth-methods',
+        'GET',
+        this.api.buildHeaders({ token: '' })
+      );
+
+      if (response?.data) {
+        const { default_auth_type, backup_auth_types } = response.data;
+        return {
+          defaultType: default_auth_type,
+          // TODO WIP backend PR consistently return empty array when no backup_auth_types
+          backupTypes: backup_auth_types?.length ? backup_auth_types : null,
+        };
+      }
+    } catch {
+      // swallow if there's an error and fallback to default login form configuration
+      return null;
+    }
+  }
+
   async fetchMounts() {
     try {
       const resp = await this.api.sys.internalUiListEnabledVisibleMounts(
@@ -101,19 +129,26 @@ export default class AuthRoute extends ClusterRouteBase {
     In older versions of Vault, the "with" query param could refer to either the auth mount path or the type
     (which may be the same, since the default mount path *is* the type). 
     For backward compatibility, we handle both scenarios.
-    → If `authMount` matches a visible auth mount, return its mount data (which includes the type).
-    → If it matches a supported auth type instead, return just the type to preselect it in the dropdown.
+    → If `authMount` matches a visible auth mount the method will assume that mount path to login and render as the default in the login form.
+    → If `authMount` matches a supported auth type (and the mount does not have `listing_visibility="unauth"`), that type is preselected in the login form.
   */
-  getMountOrTypeData(authMount, visibleAuthMounts) {
-    if (visibleAuthMounts?.[authMount]) {
-      return { path: authMount, ...visibleAuthMounts[authMount], isVisibleMount: true };
+  getDirectLinkData(authMount, visibleAuthMounts) {
+    if (!authMount) return null;
+
+    const sanitizedParam = sanitizePath(authMount); // strip leading/trailing slashes
+    // mount paths in visibleAuthMounts always end in a slash, so format for consistency
+    const formattedPath = `${sanitizedParam}/`;
+    const mountData = visibleAuthMounts?.[formattedPath];
+    if (mountData) {
+      return { path: formattedPath, type: mountData.type };
     }
+
     const types = supportedTypes(this.version.isEnterprise);
-    if (types.includes(sanitizePath(authMount))) {
-      return { type: authMount, isVisibleMount: false };
+    if (types.includes(sanitizedParam)) {
+      return { type: sanitizedParam };
     }
     // `type` is necessary because it determines which login fields to render.
-    // If we can't safely glean it from the query param, ignore it and return null
+    // If we can't safely glean it from the query param, ignore it and return null.
     return null;
   }
 }
diff --git a/ui/app/styles/helper-classes/general.scss b/ui/app/styles/helper-classes/general.scss
index 0cf5ddfa94e..1da0c93d527 100644
--- a/ui/app/styles/helper-classes/general.scss
+++ b/ui/app/styles/helper-classes/general.scss
@@ -64,6 +64,10 @@
   }
 }
 
+.white-space-pre-line {
+  white-space: pre-line;
+}
+
 // large grouped css blocks
 .is-hint {
   color: color_variables.$grey;
diff --git a/ui/app/templates/vault/cluster/auth.hbs b/ui/app/templates/vault/cluster/auth.hbs
index 80d91833102..4cec9869d3e 100644
--- a/ui/app/templates/vault/cluster/auth.hbs
+++ b/ui/app/templates/vault/cluster/auth.hbs
@@ -16,6 +16,7 @@
   <Auth::Page
     @cluster={{this.model.clusterModel}}
     @directLinkData={{this.model.directLinkData}}
+    @loginSettings={{this.model.loginSettings}}
     @namespaceQueryParam={{this.namespaceQueryParam}}
     @oidcProviderQueryParam={{this.oidcProvider}}
     @onAuthSuccess={{action "authSuccess"}}
diff --git a/ui/lib/config-ui/addon/components/login-settings/page/details.hbs b/ui/lib/config-ui/addon/components/login-settings/page/details.hbs
index 7467bbbaef9..9908ce53213 100644
--- a/ui/lib/config-ui/addon/components/login-settings/page/details.hbs
+++ b/ui/lib/config-ui/addon/components/login-settings/page/details.hbs
@@ -37,16 +37,9 @@
     {{/if}}
   </ToolbarActions>
 </Toolbar>
-{{#each-in @rule as |key value|}}
-  {{#if (eq key "defaultAuthType")}}
-    <InfoTableRow @alwaysRender={{true}} @label="Default method" @value={{value}} />
-  {{else if (eq key "backupAuthTypes")}}
-    <InfoTableRow @alwaysRender={{true}} @label="Backup methods" @value={{value}} />
-  {{else if (eq key "disableInheritance")}}
-    <InfoTableRow @alwaysRender={{true}} @label="Inheritance" @value={{stringify (not value)}} />
-  {{else}}
-    <InfoTableRow @alwaysRender={{true}} @label={{capitalize key}} @value={{value}} />
-  {{/if}}
+
+{{#each-in this.displayFields as |key label|}}
+  <InfoTableRow @alwaysRender={{true}} @label={{label}} @value={{this.displayValue key}} />
 {{/each-in}}
 
 {{#if this.showConfirmModal}}
diff --git a/ui/lib/config-ui/addon/components/login-settings/page/details.js b/ui/lib/config-ui/addon/components/login-settings/page/details.js
index d91d5d55e6c..3cba1e18caf 100644
--- a/ui/lib/config-ui/addon/components/login-settings/page/details.js
+++ b/ui/lib/config-ui/addon/components/login-settings/page/details.js
@@ -30,6 +30,19 @@ export default class LoginSettingsRuleDetails extends Component {
 
   @tracked showConfirmModal = false;
 
+  displayFields = {
+    defaultAuthType: 'Default method',
+    backupAuthTypes: 'Backup methods',
+    disableInheritance: 'Inheritance enabled',
+    namespace: 'Namespace',
+  };
+
+  displayValue = (key) => {
+    const value = this.args.rule[key];
+    // flip boolean for disable inheritance so template reads "Inheritance enabled: Yes/No"
+    return key === 'disableInheritance' ? !value : value;
+  };
+
   @action
   async onDelete() {
     const { rule } = this.args;
diff --git a/ui/lib/config-ui/addon/components/login-settings/page/list.hbs b/ui/lib/config-ui/addon/components/login-settings/page/list.hbs
index 949e7dcd833..d59075c75d5 100644
--- a/ui/lib/config-ui/addon/components/login-settings/page/list.hbs
+++ b/ui/lib/config-ui/addon/components/login-settings/page/list.hbs
@@ -15,35 +15,52 @@
 
 {{#if @loginRules}}
   {{#each @loginRules as |rule|}}
-    <div class="list-item-row linked-block-item is-no-underline">
-      <div>
-        <div class="is-grid align-items-center linked-block-title">
-          <Hds::Icon @name="user-check" @size="24" />
-          <div class="has-text-weight-semibold has-left-margin-xs" data-test-rule-name={{rule.Name}}>{{rule.Name}}</div>
+    <LinkedBlock
+      class="list-item-row"
+      @params={{array "login-settings.rule.details" rule.name}}
+      @linkPrefix="vault.cluster.config-ui"
+      data-test-rule={{rule.name}}
+    >
+      <div class="level is-mobile">
+        <div class="level-left">
+          <div>
+            <Hds::Text::Display @tag="h3">
+              <Hds::Icon @name="user-check" @size="24" @isInline={{true}} />
+              {{rule.name}}
+            </Hds::Text::Display>
+            <div class="has-top-margin-m">
+              {{rule.namespace}}
+              <Hds::Badge
+                @text="Inheritance {{if rule.disableInheritance 'disabled' 'enabled'}}"
+                class="has-left-margin-xxs"
+              />
+            </div>
+          </div>
+        </div>
+        <div class="level-right is-flex is-paddingless is-marginless">
+          <div class="level-item">
+            <Hds::Dropdown @isInline={{true}} as |dd|>
+              <dd.ToggleIcon
+                @icon="more-horizontal"
+                @hasChevron={{false}}
+                @text="login rules menu"
+                data-test-popup-menu-trigger
+              />
+              <dd.Interactive @route="login-settings.rule.details" @model={{rule.name}} data-test-popup-menu="view-rule">
+                View
+              </dd.Interactive>
+              {{#if (has-capability this.capabilities "delete" pathKey="customLogin" params=rule)}}
+                <dd.Interactive
+                  @color="critical"
+                  data-test-popup-menu="delete-rule"
+                  {{on "click" (fn (mut this.ruleToDelete) rule)}}
+                >Delete</dd.Interactive>
+              {{/if}}
+            </Hds::Dropdown>
+          </div>
         </div>
-        <div class="has-top-margin-m" data-test-rule-path={{rule.Namespace}}>{{rule.Namespace}}</div>
-      </div>
-      <div class="linked-block-popup-menu">
-        <Hds::Dropdown @isInline={{true}} as |dd|>
-          <dd.ToggleIcon
-            @icon="more-horizontal"
-            @hasChevron={{false}}
-            @text="login rules menu"
-            data-test-popup-menu-trigger
-          />
-          <dd.Interactive @route="login-settings.rule.details" @model={{rule.Name}} data-test-popup-menu="view-rule">
-            View
-          </dd.Interactive>
-          {{#if (has-capability this.capabilities "delete" pathKey="customLogin" params=rule)}}
-            <dd.Interactive
-              @color="critical"
-              data-test-popup-menu="delete-rule"
-              {{on "click" (fn (mut this.ruleToDelete) rule)}}
-            >Delete</dd.Interactive>
-          {{/if}}
-        </Hds::Dropdown>
       </div>
-    </div>
+    </LinkedBlock>
   {{/each}}
 {{else}}
   <EmptyState
diff --git a/ui/lib/config-ui/addon/components/login-settings/page/list.js b/ui/lib/config-ui/addon/components/login-settings/page/list.js
index 757e25fb018..bef39a0fca7 100644
--- a/ui/lib/config-ui/addon/components/login-settings/page/list.js
+++ b/ui/lib/config-ui/addon/components/login-settings/page/list.js
@@ -12,10 +12,10 @@ import errorMessage from 'vault/utils/error-message';
 /**
  * @module Page::LoginSettingsList
  * Page::LoginSettingsList components are used to display list of rules.
+ *
  * @example
- * ```js
  * <Page::LoginSettingsList @loginRules={{this.rules}}  />
- * ```
+ *
  * @param {array} loginRules - array of rule objects
  */
 
diff --git a/ui/lib/config-ui/addon/routes/login-settings/index.js b/ui/lib/config-ui/addon/routes/login-settings/index.js
index 73556e90c13..1c7d41e9261 100644
--- a/ui/lib/config-ui/addon/routes/login-settings/index.js
+++ b/ui/lib/config-ui/addon/routes/login-settings/index.js
@@ -10,9 +10,18 @@ export default class LoginSettingsRoute extends Route {
   @service api;
 
   async model() {
-    const res = await this.api.sys.uiLoginDefaultAuthList(true);
-    const loginRules = this.api.keyInfoToArray({ keyInfo: res.keyInfo, keys: res.keys });
-
-    return { loginRules };
+    try {
+      const res = await this.api.sys.uiLoginDefaultAuthList(true);
+      const loginRules = this.api.keyInfoToArray({ keyInfo: res.keyInfo, keys: res.keys });
+      return { loginRules };
+    } catch (e) {
+      const error = await this.api.parseError(e);
+      if (error.status === 404) {
+        // If no login settings exist, return an empty array to render the empty state
+        return { loginRules: [] };
+      }
+      // Otherwise fallback to the standard error template
+      throw error;
+    }
   }
 }
diff --git a/ui/mirage/handlers/custom-login.js b/ui/mirage/handlers/custom-login.js
index cb821410763..6f7b7602f29 100644
--- a/ui/mirage/handlers/custom-login.js
+++ b/ui/mirage/handlers/custom-login.js
@@ -45,7 +45,7 @@ export default function (server) {
   });
 
   // UNAUTHENTICATED READ ONLY for login form display logic
-  server.get('sys/internal/ui/default-login-methods', (schema, req) => {
+  server.get('sys/internal/ui/default-auth-methods', (schema, req) => {
     const nsHeader = req.requestHeaders['X-Vault-Namespace'];
     // if no namespace is passed, assume root
     const namespace = !nsHeader ? '' : nsHeader;
diff --git a/ui/mirage/scenarios/custom-login.js b/ui/mirage/scenarios/custom-login.js
index 5fe4bf312e6..cfeb372f993 100644
--- a/ui/mirage/scenarios/custom-login.js
+++ b/ui/mirage/scenarios/custom-login.js
@@ -12,11 +12,11 @@ export default function (server) {
     disable_inheritance: true,
   });
   server.create('login-rule', {
-    namespace: 'admin/',
+    namespace: 'admin',
     default_auth_type: 'oidc',
     backup_auth_types: ['token'],
   });
-  server.create('login-rule', { default_auth_type: 'jwt', backup_auth_types: null }); // namespace-2
+  server.create('login-rule', { default_auth_type: 'jwt', backup_auth_types: [] }); // namespace-2
   server.create('login-rule', { default_auth_type: '', backup_auth_types: ['oidc', 'jwt'] }); // namespace-3
   server.create('login-rule', { default_auth_type: '', backup_auth_types: ['token'] }); // namespace-4
 }
diff --git a/ui/tests/acceptance/auth/auth-test.js b/ui/tests/acceptance/auth/auth-test.js
index 44148885774..ef4ae7fff8f 100644
--- a/ui/tests/acceptance/auth/auth-test.js
+++ b/ui/tests/acceptance/auth/auth-test.js
@@ -16,7 +16,13 @@ import {
   mountEngineCmd,
   runCmd,
 } from 'vault/tests/helpers/commands';
-import { login, loginMethod, loginNs, logout, VISIBLE_MOUNTS } from 'vault/tests/helpers/auth/auth-helpers';
+import {
+  login,
+  loginMethod,
+  loginNs,
+  logout,
+  SYS_INTERNAL_UI_MOUNTS,
+} from 'vault/tests/helpers/auth/auth-helpers';
 import { AUTH_FORM } from 'vault/tests/helpers/auth/auth-form-selectors';
 import { v4 as uuidv4 } from 'uuid';
 import { GENERAL } from 'vault/tests/helpers/general-selectors';
@@ -29,6 +35,17 @@ module('Acceptance | auth login form', function (hooks) {
   setupApplicationTest(hooks);
   setupMirage(hooks);
 
+  test('it does not request login settings for community versions', async function (assert) {
+    assert.expect(1); // should only be one assertion because the stubbed mirage request should NOT be hit
+    this.owner.lookup('service:version').type = 'community';
+    this.server.get('/sys/internal/ui/default-auth-methods', () => {
+      // cannot throw error here because request errors are swallowed
+      assert.false(true, 'request made for login settings and it should not have been');
+    });
+    await visit('/vault/auth');
+    assert.strictEqual(currentURL(), '/vault/auth');
+  });
+
   test('it selects auth method if "with" query param is a supported auth method', async function (assert) {
     const backends = supportedAuthBackends();
     assert.expect(backends.length);
@@ -38,6 +55,16 @@ module('Acceptance | auth login form', function (hooks) {
     }
   });
 
+  test('it selects auth method if "with" query param ends in an unencoded a slash', async function (assert) {
+    await visit('/vault/auth?with=userpass/');
+    assert.dom(AUTH_FORM.selectMethod).hasValue('userpass');
+  });
+
+  test('it selects auth method if "with" query param ends in an encoded slash and matches an auth type', async function (assert) {
+    await visit('/vault/auth?with=userpass%2F');
+    assert.dom(AUTH_FORM.selectMethod).hasValue('userpass');
+  });
+
   test('it redirects if "with" query param is not a supported auth method', async function (assert) {
     await visit('/vault/auth?with=fake');
     assert.strictEqual(currentURL(), '/vault/auth', 'invalid query param is cleared');
@@ -74,7 +101,7 @@ module('Acceptance | auth login form', function (hooks) {
   module('listing visibility', function (hooks) {
     hooks.beforeEach(async function () {
       this.server.get('/sys/internal/ui/mounts', () => {
-        return { data: { auth: VISIBLE_MOUNTS } };
+        return { data: { auth: SYS_INTERNAL_UI_MOUNTS } };
       });
       await logout(); // clear local storage
     });
@@ -84,7 +111,7 @@ module('Acceptance | auth login form', function (hooks) {
       const expectedTabs = [
         { type: 'userpass', display: 'Userpass' },
         { type: 'oidc', display: 'OIDC' },
-        { type: 'token', display: 'Token' },
+        { type: 'ldap', display: 'LDAP' },
       ];
       await visit('/vault/auth');
       await waitFor(AUTH_FORM.tabs);
@@ -129,9 +156,9 @@ module('Acceptance | auth login form', function (hooks) {
     });
 
     test('it selects type from dropdown if query param is NOT a visible mount, but is a supported method', async function (assert) {
-      await visit('/vault/auth?with=ldap');
+      await visit('/vault/auth?with=token');
       await waitFor(GENERAL.selectByAttr('auth type'));
-      assert.dom(GENERAL.selectByAttr('auth type')).hasValue('ldap');
+      assert.dom(GENERAL.selectByAttr('auth type')).hasValue('token');
       assert.dom(GENERAL.backButton).exists('it renders "Back" button because tabs do exist');
       assert
         .dom(AUTH_FORM.otherMethodsBtn)
@@ -357,9 +384,7 @@ module('Acceptance | auth login form', function (hooks) {
       await visit('/vault/auth');
 
       this.server.get('/sys/internal/ui/mounts', (_, req) => {
-        // sometimes the namespace header is "X-Vault-Namespace" and other times "x-vault-namespace"...haven't figured out why
-        const key = Object.keys(req.requestHeaders).find((k) => k.toLowerCase().includes('namespace'));
-        assert.strictEqual(req.requestHeaders[key], 'admin', `${key}: header contains namespace`);
+        assert.strictEqual(req.requestHeaders['x-vault-namespace'], 'admin', 'header contains namespace');
         req.passthrough();
       });
       await typeIn(GENERAL.inputByAttr('namespace'), 'admin');
diff --git a/ui/tests/acceptance/auth/login-settings-test.js b/ui/tests/acceptance/auth/login-settings-test.js
new file mode 100644
index 00000000000..5d1db9eaa2e
--- /dev/null
+++ b/ui/tests/acceptance/auth/login-settings-test.js
@@ -0,0 +1,91 @@
+/**
+ * Copyright (c) HashiCorp, Inc.
+ * SPDX-License-Identifier: BUSL-1.1
+ */
+
+import { module, test } from 'qunit';
+import { setupApplicationTest } from 'ember-qunit';
+import { click, fillIn, typeIn, visit, waitFor } from '@ember/test-helpers';
+import { runCmd } from 'vault/tests/helpers/commands';
+import { login, logout, rootToken } from 'vault/tests/helpers/auth/auth-helpers';
+import { AUTH_FORM } from 'vault/tests/helpers/auth/auth-form-selectors';
+import { GENERAL } from 'vault/tests/helpers/general-selectors';
+
+// Auth form login settings
+// This feature has thorough integration test coverage so only testing a few scenarios and direct link functionality
+// Tests for read/list views are in ui/tests/acceptance/config-ui/login-settings-test.js
+module('Acceptance | Enterprise | auth form custom login settings', function (hooks) {
+  setupApplicationTest(hooks);
+  hooks.beforeEach(async function () {
+    await login();
+    await runCmd([
+      `write sys/namespaces/test-ns -force`,
+      `write test-ns/sys/namespaces/child -force`,
+      `write sys/config/ui/login/default-auth/root-rule backup_auth_types=token default_auth_type=okta disable_inheritance=false namespace=""`,
+      `write sys/config/ui/login/default-auth/ns-rule default_auth_type=ldap disable_inheritance=true namespace=test-ns`,
+      `write sys/auth/my-oidc type=oidc`,
+      `write sys/auth/my-oidc/tune listing_visibility="unauth"`,
+    ]);
+    return await logout();
+  });
+
+  hooks.afterEach(async function () {
+    // cleanup login rules
+    await visit('/vault/auth?with=token');
+    await fillIn(GENERAL.inputByAttr('token'), rootToken);
+    await click(AUTH_FORM.login);
+    await runCmd([
+      'delete sys/config/ui/login/default-auth/root-rule',
+      'delete sys/config/ui/login/default-auth/ns-rule',
+      'delete sys/auth/my-oidc',
+      'delete test-ns/sys/namespaces/child',
+      'delete sys/namespaces/test-ns',
+    ]);
+  });
+
+  test('it renders login settings for root namespace', async function (assert) {
+    await visit('/vault/auth');
+    await waitFor(AUTH_FORM.tabBtn('okta'));
+    assert.dom(AUTH_FORM.tabBtn('okta')).hasAttribute('aria-selected', 'true');
+    assert.dom(AUTH_FORM.authForm('okta')).exists('it renders default method');
+    assert.dom(AUTH_FORM.advancedSettings).exists();
+
+    await click(AUTH_FORM.otherMethodsBtn);
+    assert.dom(AUTH_FORM.authForm('token')).exists('it renders backup method');
+  });
+
+  test('it renders login settings for namespaces', async function (assert) {
+    await visit('/vault/auth');
+    await fillIn(GENERAL.inputByAttr('namespace'), 'test-ns');
+    await waitFor(AUTH_FORM.authForm('ldap'));
+    assert.dom(AUTH_FORM.authForm('ldap')).exists('it renders default method');
+    assert.dom(AUTH_FORM.advancedSettings).exists();
+    assert.dom(AUTH_FORM.otherMethodsBtn).doesNotExist('it does not render alternate view');
+
+    // type in so that the namespace is "test-ns/child"
+    await typeIn(GENERAL.inputByAttr('namespace'), '/child');
+    await waitFor(AUTH_FORM.authForm('okta'));
+    assert
+      .dom(AUTH_FORM.authForm('okta'))
+      .exists('it inherits view from root namespace because "test-ns" settings are not inheritable');
+  });
+
+  test('it ignores login settings if query param references a visible mount path', async function (assert) {
+    await visit('/vault/auth?with=my-oidc%2F');
+    await waitFor(AUTH_FORM.tabBtn('oidc'));
+    assert
+      .dom(AUTH_FORM.tabBtn('oidc'))
+      .hasAttribute('aria-selected', 'true', 'it selects tab matching query param');
+    assert.dom(AUTH_FORM.authForm('oidc')).exists();
+    assert.dom(AUTH_FORM.advancedSettings).doesNotExist();
+    await click(AUTH_FORM.otherMethodsBtn);
+    assert.dom(GENERAL.selectByAttr('auth type')).exists('dropdown renders as fallback view');
+  });
+
+  test('it ignores login settings if query param references a valid type', async function (assert) {
+    await visit('/vault/auth?with=userpass');
+    assert.dom(GENERAL.selectByAttr('auth type')).hasValue('userpass', 'dropdown selects userpass');
+    await click(GENERAL.backButton);
+    assert.dom(AUTH_FORM.tabBtn('oidc')).exists('it renders tabs on "Back" because visible mounts exist');
+  });
+});
diff --git a/ui/tests/acceptance/config-ui/login-settings-test.js b/ui/tests/acceptance/config-ui/login-settings-test.js
index 802a5d4e33e..402634029d8 100644
--- a/ui/tests/acceptance/config-ui/login-settings-test.js
+++ b/ui/tests/acceptance/config-ui/login-settings-test.js
@@ -9,74 +9,121 @@ import { click, visit, currentRouteName } from '@ember/test-helpers';
 import { login } from 'vault/tests/helpers/auth/auth-helpers';
 import { GENERAL } from 'vault/tests/helpers/general-selectors';
 import { runCmd } from 'vault/tests/helpers/commands';
-
+import { setupMirage } from 'ember-cli-mirage/test-support';
+import { overrideResponse } from 'vault/tests/helpers/stubs';
+
+const SELECTORS = {
+  rule: (name) => (name ? `[data-test-rule="${name}"]` : '[data-test-rule]'),
+  popupMenu: (name) => `[data-test-rule="${name}"] ${GENERAL.menuTrigger}`,
+};
+// read view for custom login settings
 module('Acceptance | Enterprise | config-ui/login-settings', function (hooks) {
   setupApplicationTest(hooks);
+  setupMirage(hooks);
 
   hooks.beforeEach(async function () {
-    await login();
-
-    // create login rules
-    await runCmd([
-      `write sys/config/ui/login/default-auth/testRule backup_auth_types=userpass default_auth_type=okta disable_inheritance=false namespace=ns1`,
-      'write sys/config/ui/login/default-auth/testRule2 backup_auth_types=oidc default_auth_type=ldap disable_inheritance=true namespace=ns2',
-    ]);
-  });
-
-  hooks.afterEach(async function () {
-    await login();
-
-    // cleanup login rules
-    await runCmd([
-      'delete sys/config/ui/login/default-auth/testRule',
-      'delete sys/config/ui/login/default-auth/testRule2',
-    ]);
+    return await login();
   });
 
-  test('fetched login rule list renders', async function (assert) {
-    // Visit the login settings list index page
+  test('it renders empty state if no login settings exist', async function (assert) {
     await visit('vault/config-ui/login-settings');
 
-    // verify fetched rules are rendered in list
-    assert.dom('.linked-block-item').exists({ count: 2 });
-    // verify rule data namespaces render
-    assert.dom('[data-test-rule-path="ns1/"]').exists();
-    assert.dom('[data-test-rule-path="ns2/"]').exists();
+    assert.dom(GENERAL.emptyStateTitle).hasText('No UI login rules yet');
+    assert
+      .dom(GENERAL.emptyStateMessage)
+      .hasText(
+        'Login rules can be used to select default and back up login methods and customize which methods display in the web UI login form. Available to be created via the CLI or HTTP API.'
+      );
   });
 
-  test('delete rule from list view', async function (assert) {
-    // Visit the login settings list index page
+  test('it falls back error template if no permission', async function (assert) {
+    this.server.get('/sys/config/ui/login/default-auth', () => overrideResponse(403));
     await visit('vault/config-ui/login-settings');
-
-    await click(GENERAL.menuTrigger);
-    await click(GENERAL.menuItem('delete-rule'));
-
-    assert.dom(GENERAL.confirmationModal).exists();
-    await click(GENERAL.confirmButton);
-
-    // verify success message from deletion
-    assert.dom(GENERAL.latestFlashContent).includesText('Successfully deleted rule testRule.');
-    assert.dom('[data-test-rule-name="testRule"]').doesNotExist();
+    assert.dom(GENERAL.pageError.error).hasText('Error permission denied');
   });
 
-  test('navigate to rule details page and renders rule data', async function (assert) {
-    // visit individual rule page
-    await visit('vault/config-ui/login-settings');
-
-    await click(GENERAL.menuTrigger);
-    await click(GENERAL.menuItem('view-rule'));
-
-    // verify that user is redirected to the rule details page
-    assert.strictEqual(
-      currentRouteName(),
-      'vault.cluster.config-ui.login-settings.rule.details',
-      'goes to rule details page'
-    );
-
-    // verify fetched rule data is rendered
-    assert.dom(GENERAL.infoRowValue('Name')).hasText('testRule');
-    assert.dom(GENERAL.infoRowValue('Namespace')).hasText('ns1/');
-    assert.dom(GENERAL.infoRowValue('Backup methods')).hasText('userpass');
-    assert.dom(GENERAL.infoRowValue('Inheritance')).hasText('true');
+  module('list, read and delete', function (hooks) {
+    hooks.beforeEach(async function () {
+      await login();
+
+      // create login rules
+      await runCmd([
+        `write sys/config/ui/login/default-auth/testRule backup_auth_types=userpass default_auth_type=okta disable_inheritance=false namespace=ns1`,
+        'write sys/config/ui/login/default-auth/testRule2 backup_auth_types=oidc default_auth_type=ldap disable_inheritance=true namespace=ns2',
+      ]);
+    });
+
+    hooks.afterEach(async function () {
+      await login();
+
+      // cleanup login rules
+      await runCmd([
+        'delete sys/config/ui/login/default-auth/testRule',
+        'delete sys/config/ui/login/default-auth/testRule2',
+      ]);
+    });
+
+    test('fetched login rule list renders', async function (assert) {
+      // Visit the login settings list index page
+      await visit('vault/config-ui/login-settings');
+
+      // verify fetched rules are rendered in list
+      assert.dom(SELECTORS.rule()).exists({ count: 2 });
+      assert.dom(SELECTORS.rule('testRule')).hasText('testRule ns1/ Inheritance enabled');
+      assert.dom(SELECTORS.rule('testRule2')).hasText('testRule2 ns2/ Inheritance disabled');
+    });
+
+    test('delete rule from list view', async function (assert) {
+      // Visit the login settings list index page
+      await visit('vault/config-ui/login-settings');
+
+      assert.dom(SELECTORS.rule()).exists({ count: 2 });
+      await click(SELECTORS.popupMenu('testRule'));
+      await click(GENERAL.menuItem('delete-rule'));
+
+      assert.dom(GENERAL.confirmationModal).exists();
+      await click(GENERAL.confirmButton);
+
+      // verify success message from deletion
+      assert.dom(GENERAL.latestFlashContent).includesText('Successfully deleted rule testRule.');
+      assert.dom(SELECTORS.rule('testRule')).doesNotExist();
+      assert.dom(SELECTORS.rule()).exists({ count: 1 });
+    });
+
+    test('navigate to rule details page and renders rule data', async function (assert) {
+      // visit individual rule page
+      await visit('vault/config-ui/login-settings');
+
+      await click(SELECTORS.popupMenu('testRule'));
+      await click(GENERAL.menuItem('view-rule'));
+
+      // verify that user is redirected to the rule details page
+      assert.strictEqual(
+        currentRouteName(),
+        'vault.cluster.config-ui.login-settings.rule.details',
+        'goes to rule details page'
+      );
+
+      // verify fetched rule data is rendered
+      assert.dom(GENERAL.infoRowValue('Default method')).hasText('okta');
+      assert.dom(GENERAL.infoRowValue('Namespace')).hasText('ns1/');
+      assert.dom(GENERAL.infoRowValue('Backup methods')).hasText('userpass');
+      assert.dom(GENERAL.infoRowValue('Inheritance enabled')).hasText('Yes');
+    });
+
+    test('it navigates to rule details from linked block', async function (assert) {
+      await visit('vault/config-ui/login-settings');
+      await click(SELECTORS.rule('testRule2'));
+      assert.strictEqual(
+        currentRouteName(),
+        'vault.cluster.config-ui.login-settings.rule.details',
+        'goes to rule details page'
+      );
+
+      assert.dom(GENERAL.infoRowValue('Default method')).hasText('ldap');
+      assert.dom(GENERAL.infoRowValue('Namespace')).hasText('ns2/');
+      assert.dom(GENERAL.infoRowValue('Backup methods')).hasText('oidc');
+      assert.dom(GENERAL.infoRowValue('Inheritance enabled')).hasText('No');
+    });
   });
 });
diff --git a/ui/tests/helpers/auth/auth-helpers.ts b/ui/tests/helpers/auth/auth-helpers.ts
index d61c68fd101..f9a46147157 100644
--- a/ui/tests/helpers/auth/auth-helpers.ts
+++ b/ui/tests/helpers/auth/auth-helpers.ts
@@ -128,7 +128,8 @@ export const AUTH_METHOD_MAP = [
   { authType: 'saml', options: LOGIN_DATA.saml },
 ];
 
-export const VISIBLE_MOUNTS = {
+// Mock response for `sys/internal/ui/mounts`
+export const SYS_INTERNAL_UI_MOUNTS = {
   'userpass/': {
     description: '',
     options: {},
@@ -144,9 +145,9 @@ export const VISIBLE_MOUNTS = {
     options: {},
     type: 'oidc',
   },
-  'token/': {
-    description: 'token based credentials',
+  'ldap/': {
+    description: '',
     options: null,
-    type: 'token',
+    type: 'ldap',
   },
 };
diff --git a/ui/tests/integration/components/auth/form-template-test.js b/ui/tests/integration/components/auth/form-template-test.js
index ab1db62d7c3..8e16bcc6e45 100644
--- a/ui/tests/integration/components/auth/form-template-test.js
+++ b/ui/tests/integration/components/auth/form-template-test.js
@@ -27,26 +27,29 @@ module('Integration | Component | auth | form template', function (hooks) {
   hooks.beforeEach(function () {
     window.localStorage.clear();
     this.version = this.owner.lookup('service:version');
-    this.visibleMountsByType = null;
     this.cluster = { id: '1' };
-    this.directLinkData = null;
+
+    this.alternateView = null;
+    this.defaultView = { view: 'dropdown', tabData: null };
     this.handleNamespaceUpdate = sinon.spy();
+    this.initialFormState = { initialAuthType: 'token', showAlternate: false };
     this.namespaceQueryParam = '';
     this.oidcProviderQueryParam = '';
     this.onSuccess = sinon.spy();
-    this.canceledMfaAuth = '';
+    this.visibleMountTypes = null;
 
     this.renderComponent = () => {
       return render(hbs`
          <Auth::FormTemplate
-          @visibleMountsByType={{this.visibleMountsByType}}
+          @alternateView={{this.alternateView}}
           @cluster={{this.cluster}}
-          @directLinkData={{this.directLinkData}}
+          @defaultView={{this.defaultView}}
           @handleNamespaceUpdate={{this.handleNamespaceUpdate}}
+          @initialFormState={{this.initialFormState}}
           @namespaceQueryParam={{this.namespaceQueryParam}}
           @oidcProviderQueryParam={{this.oidcProviderQueryParam}}
           @onSuccess={{this.onSuccess}}
-          @canceledMfaAuth={{this.canceledMfaAuth}}
+          @visibleMountTypes={{this.visibleMountTypes}}
         />`);
     };
   });
@@ -57,29 +60,16 @@ module('Integration | Component | auth | form template', function (hooks) {
     assert.dom(GENERAL.selectByAttr('auth type')).hasValue('token');
   });
 
-  test('it selects @canceledMfaAuth by default', async function (assert) {
-    this.canceledMfaAuth = 'ldap';
+  test('it does not show toggle buttons if @alternateView does not exist', async function (assert) {
     await this.renderComponent();
-    assert.dom(GENERAL.selectByAttr('auth type')).hasValue('ldap');
-    assert.dom(GENERAL.inputByAttr('username')).exists();
-    assert.dom(GENERAL.inputByAttr('password')).exists();
-  });
-
-  test('it selects type in the dropdown if @directLinkData data just contains type', async function (assert) {
-    this.directLinkData = { type: 'oidc', isVisibleMount: false };
-    await this.renderComponent();
-    assert.dom(GENERAL.selectByAttr('auth type')).hasValue('oidc');
-    assert.dom(GENERAL.inputByAttr('role')).exists();
-    await click(AUTH_FORM.advancedSettings);
-    assert.dom(GENERAL.inputByAttr('path')).exists();
-    assert.dom(GENERAL.backButton).doesNotExist();
+    assert.dom(GENERAL.backButton).doesNotExist('"Back" button does not render');
     assert.dom(AUTH_FORM.otherMethodsBtn).doesNotExist('"Sign in with other methods" does not render');
   });
 
-  test('it does not show toggle buttons when listing visibility is not set', async function (assert) {
+  test('it initializes with preset auth type', async function (assert) {
+    this.initialFormState = { initialAuthType: 'userpass' };
     await this.renderComponent();
-    assert.dom(GENERAL.backButton).doesNotExist('"Back" button does not render');
-    assert.dom(AUTH_FORM.otherMethodsBtn).doesNotExist('"Sign in with other methods" does not render');
+    assert.dom(GENERAL.selectByAttr('auth type')).hasValue('userpass');
   });
 
   test('it displays errors', async function (assert) {
@@ -95,7 +85,7 @@ module('Integration | Component | auth | form template', function (hooks) {
 
   module('listing visibility', function (hooks) {
     hooks.beforeEach(function () {
-      this.visibleMountsByType = {
+      const defaultTabs = {
         userpass: [
           {
             path: 'userpass/',
@@ -127,27 +117,12 @@ module('Integration | Component | auth | form template', function (hooks) {
           },
         ],
       };
-    });
-
-    test('it renders mounts configured with listing_visibility="unuath"', async function (assert) {
-      const expectedTabs = [
-        { type: 'userpass', display: 'Userpass' },
-        { type: 'oidc', display: 'OIDC' },
-        { type: 'token', display: 'Token' },
-      ];
-
-      await this.renderComponent();
-      assert.dom(GENERAL.selectByAttr('auth type')).doesNotExist('dropdown does not render');
-      // there are 4 mount paths returned in the stubbed sys/internal/ui/mounts response above,
-      // but two are of the same type so only expect 3 tabs
-      assert.dom(AUTH_FORM.tabs).exists({ count: 3 }, 'it groups mount paths by type and renders 3 tabs');
-      expectedTabs.forEach((m) => {
-        assert.dom(AUTH_FORM.tabBtn(m.type)).exists(`${m.type} renders as a tab`);
-        assert.dom(AUTH_FORM.tabBtn(m.type)).hasText(m.display, `${m.type} renders expected display name`);
-      });
-      assert
-        .dom(AUTH_FORM.tabBtn('userpass'))
-        .hasAttribute('aria-selected', 'true', 'it selects the first type by default');
+      // all computed by the parent, in this case the initial tabs are the same as visible mount types
+      // but that isn't always the case
+      this.visibleMountTypes = Object.keys(defaultTabs);
+      this.defaultView = { type: 'tabs', tabData: defaultTabs };
+      this.alternateView = { type: 'dropdown', tabData: null };
+      this.initialFormState = { initialAuthType: 'userpass', showAlternate: false };
     });
 
     test('it selects each auth tab and renders form for that type', async function (assert) {
@@ -180,37 +155,14 @@ module('Integration | Component | auth | form template', function (hooks) {
       assert.dom(AUTH_FORM.advancedSettings).doesNotExist();
     });
 
-    test('it renders the mount description', async function (assert) {
-      await this.renderComponent();
-      await click(AUTH_FORM.tabBtn('token'));
-      assert.dom(AUTH_FORM.description).hasText('token based credentials');
-    });
-
-    test('it renders a dropdown if multiple mount paths are returned', async function (assert) {
-      await this.renderComponent();
-      await click(AUTH_FORM.tabBtn('userpass'));
-      const dropdownOptions = findAll(`${GENERAL.selectByAttr('path')} option`).map((o) => o.value);
-      const expectedPaths = ['userpass/', 'userpass2/'];
-      expectedPaths.forEach((p) => {
-        assert.true(dropdownOptions.includes(p), `dropdown includes path: ${p}`);
-      });
-    });
-
-    test('it renders hidden input if only one mount path is returned', async function (assert) {
-      await this.renderComponent();
-      await click(AUTH_FORM.tabBtn('oidc'));
-      assert.dom(GENERAL.inputByAttr('path')).hasAttribute('type', 'hidden');
-      assert.dom(GENERAL.inputByAttr('path')).hasValue('my-oidc/');
-    });
-
-    test('it clicks "Sign in with other methods"', async function (assert) {
+    test('it clicks "Sign in with other methods" and toggles to other view', async function (assert) {
       await this.renderComponent();
       assert.dom(AUTH_FORM.tabs).exists({ count: 3 }, 'tabs render by default');
       assert.dom(GENERAL.backButton).doesNotExist();
       await click(AUTH_FORM.otherMethodsBtn);
       assert
         .dom(AUTH_FORM.otherMethodsBtn)
-        .doesNotExist('"Sign in with other methods" does not renderafter it is clicked');
+        .doesNotExist('"Sign in with other methods" does not render after it is clicked');
       assert
         .dom(GENERAL.selectByAttr('auth type'))
         .exists('clicking "Sign in with other methods" renders dropdown instead of tabs');
@@ -239,15 +191,15 @@ module('Integration | Component | auth | form template', function (hooks) {
       assert.dom(AUTH_FORM.tabBtn('token')).hasAttribute('aria-selected', 'false');
     });
 
-    test('it preselects tab if @canceledMfaAuth is a tab', async function (assert) {
-      this.canceledMfaAuth = 'oidc';
+    test('it preselects tab from initialFormState', async function (assert) {
+      this.initialFormState = { initialAuthType: 'oidc', showAlternate: false };
       await this.renderComponent();
       assert.dom(AUTH_FORM.authForm('oidc')).exists('oidc form renders');
       assert.dom(AUTH_FORM.tabBtn('oidc')).hasAttribute('aria-selected', 'true');
     });
 
-    test('if @canceledMfaAuth is NOT a tab, dropdown renders with type selected instead of tabs', async function (assert) {
-      this.canceledMfaAuth = 'ldap';
+    test('it renders dropdown and preselects type if initialFormState is not a tab', async function (assert) {
+      this.initialFormState = { initialAuthType: 'ldap', showAlternate: true };
       await this.renderComponent();
       assert.dom(GENERAL.selectByAttr('auth type')).hasValue('ldap');
       assert.dom(GENERAL.inputByAttr('username')).exists();
@@ -256,41 +208,6 @@ module('Integration | Component | auth | form template', function (hooks) {
       assert.dom(GENERAL.backButton).exists('"Back" button renders');
       assert.dom(AUTH_FORM.otherMethodsBtn).doesNotExist('"Sign in with other methods" does not render');
     });
-
-    // if mount data exists, the mount has listing_visibility="unauth"
-    test('it renders single mount view instead of tabs if @directLinkData data exists and includes mount data', async function (assert) {
-      this.directLinkData = { path: 'my-oidc/', type: 'oidc', isVisibleMount: true };
-      await this.renderComponent();
-      assert.dom(AUTH_FORM.authForm('oidc')).exists;
-      assert.dom(AUTH_FORM.tabBtn('oidc')).hasText('OIDC', 'it renders auth type tab');
-      assert.dom(AUTH_FORM.tabs).exists({ count: 1 }, 'only one tab renders');
-      assert.dom(GENERAL.inputByAttr('role')).exists();
-      assert.dom(GENERAL.inputByAttr('path')).hasAttribute('type', 'hidden');
-      assert.dom(GENERAL.inputByAttr('path')).hasValue('my-oidc/');
-      assert.dom(AUTH_FORM.otherMethodsBtn).exists('"Sign in with other methods" renders');
-
-      assert.dom(GENERAL.selectByAttr('auth type')).doesNotExist('dropdown does not render');
-      assert.dom(AUTH_FORM.advancedSettings).doesNotExist();
-      assert.dom(GENERAL.backButton).doesNotExist();
-    });
-
-    test('it does not render tabs if @directLinkData data exists and just includes type', async function (assert) {
-      // set a type that is NOT in a visible mount because mount data exists otherwise
-      this.directLinkData = { type: 'ldap', isVisibleMount: false };
-      await this.renderComponent();
-
-      assert.dom(GENERAL.selectByAttr('auth type')).hasValue('ldap', 'dropdown has type selected');
-      assert.dom(AUTH_FORM.authForm('ldap')).exists();
-      assert.dom(GENERAL.inputByAttr('username')).exists();
-      assert.dom(GENERAL.inputByAttr('password')).exists();
-      await click(AUTH_FORM.advancedSettings);
-      assert.dom(GENERAL.inputByAttr('path')).exists();
-      assert.dom(AUTH_FORM.tabBtn('ldap')).doesNotExist('tab does not render');
-      assert
-        .dom(GENERAL.backButton)
-        .exists('back button renders because listing_visibility="unauth" for other mounts');
-      assert.dom(AUTH_FORM.otherMethodsBtn).doesNotExist('"Sign in with other methods" does not render');
-    });
   });
 
   module('community', function (hooks) {
diff --git a/ui/tests/integration/components/auth/page-test.js b/ui/tests/integration/components/auth/page-test.js
index d855041ab5e..106f9029020 100644
--- a/ui/tests/integration/components/auth/page-test.js
+++ b/ui/tests/integration/components/auth/page-test.js
@@ -10,9 +10,10 @@ import hbs from 'htmlbars-inline-precompile';
 import sinon from 'sinon';
 import { setupMirage } from 'ember-cli-mirage/test-support';
 import { AUTH_FORM } from 'vault/tests/helpers/auth/auth-form-selectors';
-import { fillInLoginFields, VISIBLE_MOUNTS } from 'vault/tests/helpers/auth/auth-helpers';
+import { fillInLoginFields, SYS_INTERNAL_UI_MOUNTS } from 'vault/tests/helpers/auth/auth-helpers';
 import { GENERAL } from 'vault/tests/helpers/general-selectors';
 import { CSP_ERROR } from 'vault/components/auth/page';
+import { setupTotpMfaResponse } from 'vault/tests/helpers/mfa/mfa-helpers';
 
 module('Integration | Component | auth | page', function (hooks) {
   setupRenderingTest(hooks);
@@ -21,16 +22,18 @@ module('Integration | Component | auth | page', function (hooks) {
   hooks.beforeEach(function () {
     this.version = this.owner.lookup('service:version');
     this.cluster = { id: '1' };
+    this.directLinkData = null;
+    this.loginSettings = null;
     this.onAuthSuccess = sinon.spy();
     this.onNamespaceUpdate = sinon.spy();
     this.visibleAuthMounts = false;
-    this.directLinkData = null;
 
     this.renderComponent = () => {
       return render(hbs`
         <Auth::Page
           @cluster={{this.cluster}}
           @directLinkData={{this.directLinkData}}
+          @loginSettings={{this.loginSettings}}
           @namespaceQueryParam={{this.nsQp}}
           @oidcProviderQueryParam={{this.providerQp}}
           @onAuthSuccess={{this.onAuthSuccess}}
@@ -96,18 +99,32 @@ module('Integration | Component | auth | page', function (hooks) {
     assert.strictEqual(actual, 'mynamespace', `onNamespaceUpdate called with: ${actual}`);
   });
 
+  // DIRECT LINK tests (without any listing visibility)
+  test('it selects type in the dropdown if direct link is just type', async function (assert) {
+    this.directLinkData = { type: 'oidc' };
+    await this.renderComponent();
+    assert.dom(AUTH_FORM.tabBtn('oidc')).doesNotExist('tab does not render');
+    assert.dom(GENERAL.selectByAttr('auth type')).hasValue('oidc', 'dropdown has type selected');
+    assert.dom(AUTH_FORM.authForm('oidc')).exists();
+    assert.dom(GENERAL.inputByAttr('role')).exists();
+    await click(AUTH_FORM.advancedSettings);
+    assert.dom(GENERAL.inputByAttr('path')).exists({ count: 1 });
+    assert.dom(GENERAL.backButton).doesNotExist();
+    assert.dom(AUTH_FORM.otherMethodsBtn).doesNotExist('"Sign in with other methods" does not render');
+  });
+
   module('listing visibility', function (hooks) {
     hooks.beforeEach(function () {
-      this.visibleAuthMounts = VISIBLE_MOUNTS;
+      this.visibleAuthMounts = SYS_INTERNAL_UI_MOUNTS;
       window.localStorage.clear();
     });
 
-    test('it formats tab data if visible auth mounts exist', async function (assert) {
+    test('it formats and renders tabs if visible auth mounts exist', async function (assert) {
       await this.renderComponent();
       const expectedTabs = [
         { type: 'userpass', display: 'Userpass' },
         { type: 'oidc', display: 'OIDC' },
-        { type: 'token', display: 'Token' },
+        { type: 'ldap', display: 'LDAP' },
       ];
 
       assert.dom(GENERAL.selectByAttr('auth type')).doesNotExist('dropdown does not render');
@@ -123,33 +140,101 @@ module('Integration | Component | auth | page', function (hooks) {
         .hasAttribute('aria-selected', 'true', 'it selects the first type by default');
     });
 
-    test('it selects type in the dropdown if @directLinkData references NON visible type', async function (assert) {
-      this.directLinkData = { type: 'ldap', isVisibleMount: false };
+    test('it renders dropdown as alternate view', async function (assert) {
       await this.renderComponent();
-      assert.dom(GENERAL.selectByAttr('auth type')).hasValue('ldap', 'dropdown has type selected');
-      assert.dom(AUTH_FORM.authForm('ldap')).exists();
-      assert.dom(GENERAL.inputByAttr('username')).exists();
-      assert.dom(GENERAL.inputByAttr('password')).exists();
+      assert.dom(AUTH_FORM.tabs).exists({ count: 3 }, 'tabs render by default');
+      assert.dom(GENERAL.backButton).doesNotExist();
+      await click(AUTH_FORM.otherMethodsBtn);
+      assert.dom(AUTH_FORM.otherMethodsBtn).doesNotExist('button disappears after it is clicked');
+      assert.dom(GENERAL.selectByAttr('auth type')).hasValue('userpass', 'dropdown has userpass selected');
+      assert.dom(AUTH_FORM.advancedSettings).exists('toggle renders even though userpass has visible mounts');
       await click(AUTH_FORM.advancedSettings);
-      assert.dom(GENERAL.inputByAttr('path')).exists();
-      assert.dom(AUTH_FORM.tabBtn('ldap')).doesNotExist('tab does not render');
-      assert
-        .dom(GENERAL.backButton)
-        .exists('back button renders because listing_visibility="unauth" for other mounts');
-      assert.dom(AUTH_FORM.otherMethodsBtn).doesNotExist('"Sign in with other methods" does not render');
+      assert.dom(GENERAL.inputByAttr('path')).exists({ count: 1 });
+      assert.dom(GENERAL.inputByAttr('path')).hasValue('', 'it renders empty custom path input');
+
+      await fillIn(GENERAL.selectByAttr('auth type'), 'oidc');
+      assert.dom(AUTH_FORM.advancedSettings).exists('toggle renders even though oidc has a visible mount');
+      await click(AUTH_FORM.advancedSettings);
+      assert.dom(GENERAL.inputByAttr('path')).exists({ count: 1 });
+      assert.dom(GENERAL.inputByAttr('path')).hasValue('', 'it renders empty custom path input');
+      await click(GENERAL.backButton);
+      assert.dom(GENERAL.backButton).doesNotExist('"Back" button does not render after it is clicked');
+      assert.dom(AUTH_FORM.tabs).exists({ count: 3 }, 'clicking "Back" renders tabs again');
+      assert.dom(AUTH_FORM.otherMethodsBtn).exists('"Sign in with other methods" renders again');
     });
 
-    test('it renders single mount view instead of tabs if @directLinkData data references a visible type', async function (assert) {
-      this.directLinkData = { path: 'my-oidc/', type: 'oidc', isVisibleMount: true };
-      await this.renderComponent();
-      assert.dom(AUTH_FORM.tabBtn('oidc')).hasText('OIDC', 'it renders tab for type');
-      assert.dom(GENERAL.inputByAttr('role')).exists();
-      assert.dom(GENERAL.inputByAttr('path')).hasAttribute('type', 'hidden');
-      assert.dom(GENERAL.inputByAttr('path')).hasValue('my-oidc/');
-      assert.dom(AUTH_FORM.otherMethodsBtn).exists('"Sign in with other methods" renders');
-      assert.dom(GENERAL.selectByAttr('auth type')).doesNotExist();
-      assert.dom(AUTH_FORM.advancedSettings).doesNotExist();
-      assert.dom(GENERAL.backButton).doesNotExist();
+    module('with a direct link', function (hooks) {
+      hooks.beforeEach(function () {
+        // if path exists, the mount has listing_visibility="unauth"
+        this.directLinkIsVisibleMount = { path: 'my-oidc/', type: 'oidc' };
+        this.directLinkIsJustType = { type: 'okta' };
+      });
+
+      test('it selects type in the dropdown if direct link is just type', async function (assert) {
+        this.directLinkData = this.directLinkIsJustType;
+        await this.renderComponent();
+        assert.dom(AUTH_FORM.tabBtn('okta')).doesNotExist('tab does not render');
+        assert.dom(GENERAL.selectByAttr('auth type')).hasValue('okta', 'dropdown has type selected');
+        assert.dom(AUTH_FORM.authForm('okta')).exists();
+        assert.dom(GENERAL.inputByAttr('username')).exists();
+        assert.dom(GENERAL.inputByAttr('password')).exists();
+        await click(AUTH_FORM.advancedSettings);
+        assert.dom(GENERAL.inputByAttr('path')).exists({ count: 1 });
+        assert.dom(AUTH_FORM.otherMethodsBtn).doesNotExist('"Sign in with other methods" does not render');
+        assert.dom(GENERAL.backButton).exists('back button renders because tabs exist for other methods');
+        await click(GENERAL.backButton);
+        assert
+          .dom(AUTH_FORM.tabBtn('userpass'))
+          .hasAttribute('aria-selected', 'true', 'first tab is selected on back');
+      });
+
+      test('it renders single method view instead of tabs if direct link includes path', async function (assert) {
+        this.directLinkData = this.directLinkIsVisibleMount;
+        await this.renderComponent();
+        assert.dom(AUTH_FORM.authForm('oidc')).exists;
+        assert.dom(AUTH_FORM.tabBtn('oidc')).hasText('OIDC', 'it renders tab for type');
+        assert.dom(AUTH_FORM.tabs).exists({ count: 1 }, 'only one tab renders');
+        assert.dom(GENERAL.inputByAttr('role')).exists();
+        assert.dom(GENERAL.inputByAttr('path')).hasAttribute('type', 'hidden');
+        assert.dom(GENERAL.inputByAttr('path')).hasValue('my-oidc/');
+        assert.dom(AUTH_FORM.otherMethodsBtn).exists('"Sign in with other methods" renders');
+        assert.dom(GENERAL.selectByAttr('auth type')).doesNotExist();
+        assert.dom(AUTH_FORM.advancedSettings).doesNotExist();
+        assert.dom(GENERAL.backButton).doesNotExist();
+      });
+
+      test('it prioritizes auth type from canceled mfa instead of direct link for path', async function (assert) {
+        assert.expect(1);
+        this.directLinkData = this.directLinkIsVisibleMount;
+        const authType = 'okta';
+        const { loginData, url } = REQUEST_DATA.username;
+        const requestUrl = url({ path: authType, username: loginData?.username });
+        this.server.post(requestUrl, () => setupTotpMfaResponse(authType));
+
+        await this.renderComponent();
+        await click(AUTH_FORM.otherMethodsBtn);
+        await fillIn(AUTH_FORM.selectMethod, authType);
+        await fillInLoginFields(loginData);
+        await click(AUTH_FORM.login);
+        await waitFor('[data-test-mfa-description]'); // wait until MFA validation renders
+        await click(GENERAL.backButton);
+        assert.dom(AUTH_FORM.selectMethod).hasValue(authType, 'Okta is selected in dropdown');
+      });
+
+      test('it prioritizes auth type from canceled mfa instead of direct link with type', async function (assert) {
+        assert.expect(1);
+        this.directLinkData = this.directLinkIsJustType;
+        const authType = 'userpass';
+        const { loginData, url } = REQUEST_DATA.username;
+        const requestUrl = url({ path: authType, username: loginData?.username });
+        this.server.post(requestUrl, () => setupTotpMfaResponse(authType));
+        await this.renderComponent();
+        await fillIn(AUTH_FORM.selectMethod, authType);
+        await fillInLoginFields(loginData);
+        await click(AUTH_FORM.login);
+        await click(GENERAL.backButton);
+        assert.dom(AUTH_FORM.tabBtn('userpass')).hasAttribute('aria-selected', 'true');
+      });
     });
   });
 
@@ -182,7 +267,6 @@ module('Integration | Component | auth | page', function (hooks) {
 
       await this.renderComponent();
       await fillIn(AUTH_FORM.selectMethod, authType);
-      // await fillIn(AUTH_FORM.selectMethod, authType);
       await fillInLoginFields(loginData);
       await click(AUTH_FORM.login);
       const [actual] = this.onAuthSuccess.lastCall.args;
@@ -218,6 +302,20 @@ module('Integration | Component | auth | page', function (hooks) {
       };
       assert.propEqual(actual, expected, `onAuthSuccess called with: ${JSON.stringify(actual)}`);
     });
+
+    test('it preselects auth type from canceled mfa', async function (assert) {
+      assert.expect(1);
+      const { loginData, url } = options;
+      const requestUrl = url({ path: authType, username: loginData?.username });
+      this.server.post(requestUrl, () => setupTotpMfaResponse(authType));
+
+      await this.renderComponent();
+      await fillIn(AUTH_FORM.selectMethod, authType);
+      await fillInLoginFields(loginData);
+      await click(AUTH_FORM.login);
+      await click(GENERAL.backButton);
+      assert.dom(AUTH_FORM.selectMethod).hasValue(authType, `${authType} is selected in dropdown`);
+    });
   }
 
   // token makes a GET request so test separately
@@ -240,4 +338,340 @@ module('Integration | Component | auth | page', function (hooks) {
     };
     assert.propEqual(actual, expected, `onAuthSuccess called with: ${JSON.stringify(actual)}`);
   });
+
+  /* 
+  Login settings are an enterprise only feature but the component is version agnostic (and subsequently so are these tests) 
+  because fetching login settings happens in the route only for enterprise versions.
+  Each combination must be tested with and without visible mounts (i.e. tuned with listing_visibility="unauth")
+  1. default+backups: default type set, backup types set 
+  2. default only: no backup types
+  3. backup only: backup types set without a default
+   */
+  module('ent login settings', function (hooks) {
+    hooks.beforeEach(function () {
+      this.loginSettings = {
+        defaultType: 'oidc',
+        backupTypes: ['userpass', 'ldap'],
+      };
+
+      this.assertPathInput = async (assert, { isHidden = false, value = '' } = {}) => {
+        // the path input can render behind the "Advanced settings" toggle or as a hidden input.
+        // Assert it only renders once and is the expected input
+        if (!isHidden) {
+          await click(AUTH_FORM.advancedSettings);
+          assert.dom(GENERAL.inputByAttr('path')).exists('it renders mount path input');
+        }
+        if (isHidden) {
+          assert.dom(AUTH_FORM.advancedSettings).doesNotExist();
+          assert.dom(GENERAL.inputByAttr('path')).hasAttribute('type', 'hidden');
+          assert.dom(GENERAL.inputByAttr('path')).hasValue(value);
+        }
+        assert.dom(GENERAL.inputByAttr('path')).exists({ count: 1 });
+      };
+    });
+
+    test('(default+backups): it initially renders default type and toggles to view backup methods', async function (assert) {
+      await this.renderComponent();
+      assert.dom(AUTH_FORM.tabBtn('oidc')).hasText('OIDC', 'it renders default method');
+      assert.dom(AUTH_FORM.tabs).exists({ count: 1 }, 'only one tab renders');
+      assert.dom(AUTH_FORM.authForm('oidc')).exists();
+      assert.dom(GENERAL.backButton).doesNotExist();
+      await this.assertPathInput(assert);
+      await click(AUTH_FORM.otherMethodsBtn);
+      assert.dom(GENERAL.backButton).exists();
+      assert.dom(AUTH_FORM.tabs).exists({ count: 2 }, 'it renders 2 backup type tabs');
+      assert
+        .dom(AUTH_FORM.tabBtn('userpass'))
+        .hasAttribute('aria-selected', 'true', 'it selects the first backup type');
+      await this.assertPathInput(assert);
+      await click(AUTH_FORM.tabBtn('ldap'));
+      assert.dom(AUTH_FORM.tabBtn('ldap')).hasAttribute('aria-selected', 'true', 'it selects ldap tab');
+      await this.assertPathInput(assert);
+    });
+
+    test('(default only): it renders default type without backup methods', async function (assert) {
+      this.loginSettings.backupTypes = null;
+      await this.renderComponent();
+      assert.dom(AUTH_FORM.tabBtn('oidc')).hasText('OIDC', 'it renders default method');
+      assert.dom(AUTH_FORM.tabs).exists({ count: 1 }, 'only one tab renders');
+      assert.dom(GENERAL.backButton).doesNotExist();
+      assert.dom(AUTH_FORM.otherMethodsBtn).doesNotExist();
+    });
+
+    test('(backups only): it initially renders backup types if no default is set', async function (assert) {
+      this.loginSettings.defaultType = '';
+      await this.renderComponent();
+      assert.dom(AUTH_FORM.tabBtn('oidc')).doesNotExist();
+      assert.dom(AUTH_FORM.tabs).exists({ count: 2 }, 'it renders 2 backup type tabs');
+      assert
+        .dom(AUTH_FORM.tabBtn('userpass'))
+        .hasAttribute('aria-selected', 'true', 'it selects the first backup type');
+      await this.assertPathInput(assert);
+      assert.dom(GENERAL.backButton).doesNotExist();
+      assert.dom(AUTH_FORM.otherMethodsBtn).doesNotExist();
+    });
+
+    module('all methods have visible mounts', function (hooks) {
+      hooks.beforeEach(function () {
+        this.loginSettings = {
+          defaultType: 'oidc',
+          backupTypes: ['userpass', 'ldap'],
+        };
+        this.visibleAuthMounts = SYS_INTERNAL_UI_MOUNTS;
+      });
+
+      test('(default+backups): it hides advanced settings for both views', async function (assert) {
+        await this.renderComponent();
+        assert.dom(AUTH_FORM.tabBtn('oidc')).hasText('OIDC', 'it renders default method');
+        assert.dom(AUTH_FORM.tabs).exists({ count: 1 }, 'only one tab renders');
+        this.assertPathInput(assert, { isHidden: true, value: 'my-oidc/' });
+        await click(AUTH_FORM.otherMethodsBtn);
+        assert.dom(AUTH_FORM.tabs).exists({ count: 2 }, 'it renders 2 backup type tabs');
+        assert
+          .dom(AUTH_FORM.tabBtn('userpass'))
+          .hasAttribute('aria-selected', 'true', 'it selects the first backup type');
+        assert.dom(AUTH_FORM.advancedSettings).doesNotExist();
+        assert.dom(GENERAL.inputByAttr('path')).doesNotExist();
+        assert.dom(GENERAL.selectByAttr('path')).exists(); // dropdown renders because userpass has 2 mount paths
+        await click(AUTH_FORM.tabBtn('ldap'));
+        this.assertPathInput(assert, { isHidden: true, value: 'ldap/' });
+      });
+
+      test('(default only): it hides advanced settings and renders hidden input', async function (assert) {
+        this.loginSettings.backupTypes = null;
+        await this.renderComponent();
+        assert.dom(AUTH_FORM.tabBtn('oidc')).hasText('OIDC', 'it renders default method');
+        assert.dom(AUTH_FORM.tabs).exists({ count: 1 }, 'only one tab renders');
+        assert.dom(AUTH_FORM.authForm('oidc')).exists();
+        this.assertPathInput(assert, { isHidden: true, value: 'my-oidc/' });
+        assert.dom(GENERAL.backButton).doesNotExist();
+        assert.dom(AUTH_FORM.otherMethodsBtn).doesNotExist();
+      });
+
+      test('(backups only): it hides advanced settings and renders hidden input', async function (assert) {
+        this.loginSettings.defaultType = '';
+        await this.renderComponent();
+        assert.dom(AUTH_FORM.tabs).exists({ count: 2 }, 'it renders 2 backup type tabs');
+        assert
+          .dom(AUTH_FORM.tabBtn('userpass'))
+          .hasAttribute('aria-selected', 'true', 'it selects the first backup type');
+        assert.dom(AUTH_FORM.advancedSettings).doesNotExist();
+        assert.dom(GENERAL.backButton).doesNotExist();
+        assert.dom(AUTH_FORM.otherMethodsBtn).doesNotExist();
+      });
+    });
+
+    module('only some methods have visible mounts', function (hooks) {
+      hooks.beforeEach(function () {
+        this.loginSettings = {
+          defaultType: 'oidc',
+          backupTypes: ['userpass', 'ldap'],
+        };
+        this.mountData = (path) => ({ [path]: SYS_INTERNAL_UI_MOUNTS[path] });
+      });
+
+      test('(default+backups): it hides advanced settings for default with visible mount but it renders for backups', async function (assert) {
+        this.visibleAuthMounts = { ...this.mountData('my-oidc/') };
+        await this.renderComponent();
+        this.assertPathInput(assert, { isHidden: true, value: 'my-oidc/' });
+        await click(AUTH_FORM.otherMethodsBtn);
+        assert.dom(AUTH_FORM.tabBtn('userpass')).hasAttribute('aria-selected', 'true');
+        await this.assertPathInput(assert);
+        await click(AUTH_FORM.tabBtn('ldap'));
+        await this.assertPathInput(assert);
+      });
+
+      test('(default+backups): it only renders advanced settings for method without mounts', async function (assert) {
+        // default and only one backup method have visible mounts
+        this.visibleAuthMounts = {
+          ...this.mountData('my-oidc/'),
+          ...this.mountData('userpass/'),
+          ...this.mountData('userpass2/'),
+        };
+        await this.renderComponent();
+        assert.dom(AUTH_FORM.advancedSettings).doesNotExist();
+        await click(AUTH_FORM.otherMethodsBtn);
+        assert.dom(AUTH_FORM.tabBtn('userpass')).hasAttribute('aria-selected', 'true');
+        assert.dom(GENERAL.selectByAttr('path')).exists();
+        assert.dom(AUTH_FORM.advancedSettings).doesNotExist();
+        await click(AUTH_FORM.tabBtn('ldap'));
+        assert.dom(AUTH_FORM.advancedSettings).exists();
+      });
+
+      test('(default+backups): it hides advanced settings for single backup method with mounts', async function (assert) {
+        this.visibleAuthMounts = { ...this.mountData('ldap/') };
+        await this.renderComponent();
+        assert.dom(AUTH_FORM.authForm('oidc')).exists();
+        assert.dom(AUTH_FORM.advancedSettings).exists();
+        await click(AUTH_FORM.otherMethodsBtn);
+        assert.dom(AUTH_FORM.tabBtn('userpass')).hasAttribute('aria-selected', 'true');
+        assert.dom(AUTH_FORM.advancedSettings).exists();
+        await click(AUTH_FORM.tabBtn('ldap'));
+        this.assertPathInput(assert, { isHidden: true, value: 'ldap/' });
+      });
+
+      test('(backups only): it hides advanced settings for single method with mounts', async function (assert) {
+        this.loginSettings.defaultType = '';
+        this.visibleAuthMounts = { ...this.mountData('ldap/') };
+        await this.renderComponent();
+        assert.dom(AUTH_FORM.tabBtn('userpass')).hasAttribute('aria-selected', 'true');
+        assert.dom(AUTH_FORM.advancedSettings).exists();
+        await click(AUTH_FORM.tabBtn('ldap'));
+        this.assertPathInput(assert, { isHidden: true, value: 'ldap/' });
+      });
+    });
+
+    module('@directLinkData overrides login settings', function (hooks) {
+      hooks.beforeEach(function () {
+        this.mountData = SYS_INTERNAL_UI_MOUNTS;
+      });
+
+      module('when there are no visible mounts at all', function (hooks) {
+        hooks.beforeEach(function () {
+          this.visibleAuthMounts = null;
+          this.directLinkData = { type: 'okta' };
+        });
+
+        const testHelper = (assert) => {
+          assert.dom(AUTH_FORM.selectMethod).hasValue('okta');
+          assert.dom(AUTH_FORM.authForm('okta')).exists();
+          assert.dom(AUTH_FORM.otherMethodsBtn).doesNotExist();
+          assert.dom(GENERAL.backButton).doesNotExist();
+        };
+
+        test('(default+backups): it renders standard view and selects @directLinkData type from dropdown', async function (assert) {
+          await this.renderComponent();
+          testHelper(assert);
+        });
+
+        test('(default only): it renders standard view and selects @directLinkData type from dropdown', async function (assert) {
+          this.loginSettings.backupTypes = null;
+          await this.renderComponent();
+          testHelper(assert);
+        });
+
+        test('(backups only): it renders standard view and selects @directLinkData type from dropdown', async function (assert) {
+          this.loginSettings.defaultType = '';
+          await this.renderComponent();
+          testHelper(assert);
+        });
+      });
+
+      module('when param matches a visible mount path and other visible mounts exist', function (hooks) {
+        hooks.beforeEach(function () {
+          this.visibleAuthMounts = {
+            ...this.mountData,
+            'my-okta/': {
+              description: '',
+              options: null,
+              type: 'okta',
+            },
+          };
+          this.directLinkData = { path: 'my-okta/', type: 'okta' };
+        });
+
+        const testHelper = async (assert) => {
+          assert.dom(AUTH_FORM.tabBtn('okta')).hasText('Okta', 'it renders preferred method');
+          assert.dom(AUTH_FORM.tabs).exists({ count: 1 }, 'only one tab renders');
+          assert.dom(AUTH_FORM.authForm('okta'));
+          assert.dom(AUTH_FORM.advancedSettings).doesNotExist();
+          assert.dom(GENERAL.inputByAttr('path')).hasAttribute('type', 'hidden');
+          assert.dom(GENERAL.inputByAttr('path')).hasValue('my-okta/');
+          assert.dom(GENERAL.inputByAttr('path')).exists({ count: 1 });
+          await click(AUTH_FORM.otherMethodsBtn);
+          assert
+            .dom(GENERAL.selectByAttr('auth type'))
+            .exists('it renders dropdown after clicking "Sign in with other"');
+        };
+
+        test('(default+backups): it renders single mount view for @directLinkData', async function (assert) {
+          await this.renderComponent();
+          await testHelper(assert);
+        });
+
+        test('(default only): it renders single mount view for @directLinkData', async function (assert) {
+          this.loginSettings.backupTypes = null;
+          await this.renderComponent();
+          await testHelper(assert);
+        });
+
+        test('(backups only): it renders single mount view for @directLinkData', async function (assert) {
+          this.loginSettings.defaultType = '';
+          await this.renderComponent();
+          await testHelper(assert);
+        });
+      });
+
+      module('when param matches a type and other visible mounts exist', function (hooks) {
+        hooks.beforeEach(function () {
+          // only type is present in directLinkData because the query param does not match a path with listing_visibility="unauth"
+          this.directLinkData = { type: 'okta' };
+          this.visibleAuthMounts = this.mountData;
+        });
+
+        const testHelper = async (assert) => {
+          assert.dom(GENERAL.backButton).exists('back button renders because other methods have tabs');
+          assert.dom(AUTH_FORM.selectMethod).hasValue('okta');
+          assert.dom(AUTH_FORM.authForm('okta')).exists();
+          assert.dom(AUTH_FORM.otherMethodsBtn).doesNotExist();
+          await click(GENERAL.backButton);
+          assert.dom(AUTH_FORM.tabBtn('userpass')).hasAttribute('aria-selected', 'true');
+          await click(AUTH_FORM.otherMethodsBtn);
+          assert.dom(AUTH_FORM.selectMethod).exists('it toggles back to dropdown');
+        };
+
+        test('(default+backups): it selects @directLinkData type from dropdown and toggles to tab view', async function (assert) {
+          await this.renderComponent();
+          await testHelper(assert);
+        });
+
+        test('(default only): it selects @directLinkData type from dropdown and toggles to tab view', async function (assert) {
+          this.loginSettings.backupTypes = null;
+          await this.renderComponent();
+          await testHelper(assert);
+        });
+
+        test('(backups only): it selects @directLinkData type from dropdown and toggles to tab view', async function (assert) {
+          this.loginSettings.defaultType = '';
+          await this.renderComponent();
+          await testHelper(assert);
+        });
+      });
+
+      module('when param matches a type that matches other visible mounts', function (hooks) {
+        hooks.beforeEach(function () {
+          // only type exists because the query param does not match a path with listing_visibility="unauth"
+          this.directLinkData = { type: 'oidc' };
+          this.visibleAuthMounts = this.mountData;
+        });
+
+        const testHelper = async (assert) => {
+          assert.dom(AUTH_FORM.tabBtn('oidc')).hasAttribute('aria-selected', 'true');
+          assert.dom(AUTH_FORM.authForm('oidc')).exists();
+          assert.dom(GENERAL.backButton).doesNotExist();
+          await click(AUTH_FORM.otherMethodsBtn);
+          assert.dom(AUTH_FORM.selectMethod).exists('it toggles to view dropdown');
+          await click(GENERAL.backButton);
+          assert.dom(AUTH_FORM.tabs).exists('it toggles back to tabs');
+        };
+
+        test('(default+backups): it selects @directLinkData type tab and toggles to dropdown view', async function (assert) {
+          await this.renderComponent();
+          await testHelper(assert);
+        });
+
+        test('(default only): it selects @directLinkData type tab and toggles to dropdown view', async function (assert) {
+          this.loginSettings.backupTypes = null;
+          await this.renderComponent();
+          await testHelper(assert);
+        });
+
+        test('(backups only): it selects @directLinkData type tab and toggles to dropdown view', async function (assert) {
+          this.loginSettings.defaultType = '';
+          await this.renderComponent();
+          await testHelper(assert);
+        });
+      });
+    });
+  });
 });
diff --git a/ui/tests/integration/components/auth/tabs-test.js b/ui/tests/integration/components/auth/tabs-test.js
new file mode 100644
index 00000000000..40fc14c0a16
--- /dev/null
+++ b/ui/tests/integration/components/auth/tabs-test.js
@@ -0,0 +1,110 @@
+/**
+ * Copyright (c) HashiCorp, Inc.
+ * SPDX-License-Identifier: BUSL-1.1
+ */
+
+import { module, test } from 'qunit';
+import { setupRenderingTest } from 'ember-qunit';
+import { click, findAll, render } from '@ember/test-helpers';
+import hbs from 'htmlbars-inline-precompile';
+import sinon from 'sinon';
+import { AUTH_FORM } from 'vault/tests/helpers/auth/auth-form-selectors';
+import { GENERAL } from 'vault/tests/helpers/general-selectors';
+
+module('Integration | Component | auth | tabs', function (hooks) {
+  setupRenderingTest(hooks);
+
+  hooks.beforeEach(function () {
+    this.tabData = {
+      userpass: [
+        {
+          path: 'userpass/',
+          description: '',
+          options: {},
+          type: 'userpass',
+        },
+        {
+          path: 'userpass2/',
+          description: '',
+          options: {},
+          type: 'userpass',
+        },
+      ],
+      oidc: [
+        {
+          path: 'my-oidc/',
+          description: '',
+          options: {},
+          type: 'oidc',
+        },
+      ],
+      token: [
+        {
+          path: 'token/',
+          description: 'token based credentials',
+          options: null,
+          type: 'token',
+        },
+      ],
+    };
+    this.selectedAuthMethod = '';
+    this.handleTabClick = sinon.spy();
+    this.renderComponent = () => {
+      return render(hbs`
+      <Auth::Tabs
+        @authTabData={{this.tabData}}
+        @handleTabClick={{this.handleTabClick}}
+        @selectedAuthMethod={{this.selectedAuthMethod}}
+      />`);
+    };
+  });
+
+  test('it renders tabs', async function (assert) {
+    const expectedTabs = [
+      { type: 'userpass', display: 'Userpass' },
+      { type: 'oidc', display: 'OIDC' },
+      { type: 'token', display: 'Token' },
+    ];
+
+    await this.renderComponent();
+    expectedTabs.forEach((m) => {
+      assert.dom(AUTH_FORM.tabBtn(m.type)).exists(`${m.type} renders as a tab`);
+      assert.dom(AUTH_FORM.tabBtn(m.type)).hasText(m.display, `${m.type} renders expected display name`);
+    });
+  });
+
+  test('it selects first tab if no @selectedAuthMethod exists', async function (assert) {
+    await this.renderComponent();
+    assert.dom(AUTH_FORM.tabBtn('userpass')).hasAttribute('aria-selected', 'true');
+  });
+
+  test('it renders the mount description', async function (assert) {
+    this.selectedAuthMethod = 'token';
+    await this.renderComponent();
+    assert.dom(AUTH_FORM.description).hasText('token based credentials');
+  });
+
+  test('it renders a dropdown if multiple mount paths are returned', async function (assert) {
+    this.selectedAuthMethod = 'userpass';
+    await this.renderComponent();
+    const dropdownOptions = findAll(`${GENERAL.selectByAttr('path')} option`).map((o) => o.value);
+    const expectedPaths = ['userpass/', 'userpass2/'];
+    expectedPaths.forEach((p) => {
+      assert.true(dropdownOptions.includes(p), `dropdown includes path: ${p}`);
+    });
+  });
+
+  test('it renders hidden input if only one mount path is returned', async function (assert) {
+    this.selectedAuthMethod = 'oidc';
+    await this.renderComponent();
+    assert.dom(GENERAL.inputByAttr('path')).hasAttribute('type', 'hidden');
+    assert.dom(GENERAL.inputByAttr('path')).hasValue('my-oidc/');
+  });
+
+  test('it calls handleTabClick with tab method type', async function (assert) {
+    await this.renderComponent();
+    await click(AUTH_FORM.tabBtn('oidc'));
+    const [actual] = this.handleTabClick.lastCall.args;
+    assert.strictEqual(actual, 'oidc');
+  });
+});
diff --git a/ui/types/vault/auth/form.d.ts b/ui/types/vault/auth/form.d.ts
index cf32073d7fb..ec3dd441ed7 100644
--- a/ui/types/vault/auth/form.d.ts
+++ b/ui/types/vault/auth/form.d.ts
@@ -5,14 +5,15 @@
 
 export interface UnauthMountsByType {
   // key is the auth method type
-  [key: string]: AuthTabMountData[];
+  // if the value is "null" there is no mount data for that type
+  [key: string]: AuthTabMountData[] | null;
 }
 export interface UnauthMountsResponse {
   // key is the mount path
   [key: string]: { type: string; description?: string; config?: object | null };
 }
 
-export interface AuthTabMountData {
+interface AuthTabMountData {
   path: string;
   type: string;
   description?: string;


Reviews:
user: login: hellobontempo
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T01:01:56Z
user: login: hellobontempo
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T01:10:57Z
user: login: hellobontempo
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T01:12:42Z
user: login: hellobontempo
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T01:13:06Z
user: login: hellobontempo
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T01:13:28Z
user: login: lane-wetmore
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T16:37:03Z
user: login: hellobontempo
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T17:17:48Z
user: login: hellobontempo
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T17:18:18Z
user: login: lane-wetmore
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T17:36:54Z
user: login: lane-wetmore
state: DISMISSED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T15:22:34Z
user: login: Monkeychip
state: DISMISSED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T19:37:22Z
user: login: drivera258
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T21:18:30Z
user: login: hellobontempo
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T21:24:27Z

Review comments:
diff_hunk: @@ -3,105 +3,103 @@
   SPDX-License-Identifier: BUSL-1.1
 }}
 
-<div {{did-insert this.initializeState}}>
path: ui/app/components/auth/form-template.hbs
user: login: hellobontempo
body: moved initialization to the parent, so no more need for `did-insert` 🎉 . I recommend reviewing with ✅ Hide whitespace
created_at: 2025-05-21T01:01:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -118,4 +268,18 @@ export default class AuthPage extends Component<Args> {
     this.mfaAuthData = null;
     this.mfaErrors = '';
   }
+
+  // HELPERS
+  private filterVisibleMountsByType(authTypes: string[]) {
+    const tabs: UnauthMountsByType = {};
+    for (const type of authTypes) {
+      // adds visible mounts for each type, if they exist
+      tabs[type] = this.visibleMountsByType?.[type] || null;
+    }
+    return tabs;
+  }
+
+  private constructViews(view: FormView, tabData: UnauthMountsByType | null) {
+    return { view, tabData };
path: ui/app/components/auth/page.ts
user: login: hellobontempo
body: `view` is actually never used anywhere and only included it to make the form view logic easier to navigate. Without it, the `dropdown` view has to be deduced whether or not `tabData` exists. So for state that has an alternate view, but no tabs it would be `alternateView = { tabData: null }`. It felt strange to rely on an object of that shape for a truthy value. Alternatively, I could add a key like `hasAlternateView` but at that point it just seemed like I might as well say what said alternate view is to ease navigating these chaotic logic combinations 😅 
created_at: 2025-05-21T01:10:57Z
author_association: CONTRIBUTOR
diff_hunk: @@ -101,19 +126,24 @@ export default class AuthRoute extends ClusterRouteBase {
     In older versions of Vault, the "with" query param could refer to either the auth mount path or the type
     (which may be the same, since the default mount path *is* the type). 
     For backward compatibility, we handle both scenarios.
-    → If `authMount` matches a visible auth mount, return its mount data (which includes the type).
-    → If it matches a supported auth type instead, return just the type to preselect it in the dropdown.
+    → If `authMount` matches a visible auth mount the method will assume that mount path to login and render as the default in the login form.
+    → If `authMount` matches a supported auth type (and the mount does not have `listing_visibility="unauth"`), that is preselected in the login form.
   */
   getMountOrTypeData(authMount, visibleAuthMounts) {
-    if (visibleAuthMounts?.[authMount]) {
-      return { path: authMount, ...visibleAuthMounts[authMount], isVisibleMount: true };
+    const sanitizedParam = sanitizePath(authMount); // strip leading/trailing slashes
path: ui/app/routes/vault/cluster/auth.js
user: login: hellobontempo
body: added test [here](https://github.com/hashicorp/vault/pull/30700/files?diff=split&w=1#diff-7c072458c21367a973345b1a8a712fa34739b8a59657dc78cac51ed55e552e3fR58-R66) for this update
created_at: 2025-05-21T01:12:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -180,37 +155,14 @@ module('Integration | Component | auth | form template', function (hooks) {
       assert.dom(AUTH_FORM.advancedSettings).doesNotExist();
     });
 
-    test('it renders the mount description', async function (assert) {
path: ui/tests/integration/components/auth/form-template-test.js
user: login: hellobontempo
body: moved these tests to `auth/tabs-test` component
created_at: 2025-05-21T01:13:05Z
author_association: CONTRIBUTOR
path: ui/tests/integration/components/auth/form-template-test.js
user: login: hellobontempo
body: unless otherwise noted, tests deleted here migrated to `auth/page-test.js`
created_at: 2025-05-21T01:13:28Z
author_association: CONTRIBUTOR
diff_hunk: @@ -3,105 +3,103 @@
   SPDX-License-Identifier: BUSL-1.1
 }}
 
-<div {{did-insert this.initializeState}}>
-  {{#if this.formComponent}}
-    {{#let (component this.formComponent) as |AuthFormComponent|}}
-      {{! renders Auth::Form::Base or Auth::Form::<Type>}}
-      <AuthFormComponent
-        @authType={{this.selectedAuthMethod}}
-        @cluster={{@cluster}}
-        @onError={{this.handleError}}
-        @onSuccess={{@onSuccess}}
-      >
-        <:namespace>
-          {{#if (has-feature "Namespaces")}}
-            <Auth::NamespaceInput
-              @disabled={{@oidcProviderQueryParam}}
-              @hvdManagedNamespace={{this.flags.hvdManagedNamespaceRoot}}
-              @namespaceValue={{this.namespaceInput}}
-              @updateNamespace={{this.handleNamespaceUpdate}}
-            />
-          {{/if}}
-        </:namespace>
+{{#if this.formComponent}}
+  {{#let (component this.formComponent) as |AuthFormComponent|}}
+    {{! renders Auth::Form::Base or Auth::Form::<Type>}}
+    <AuthFormComponent
+      @authType={{this.selectedAuthMethod}}
+      @cluster={{@cluster}}
+      @onError={{this.handleError}}
+      @onSuccess={{@onSuccess}}
+    >
+      <:namespace>
+        {{#if (has-feature "Namespaces")}}
+          <Auth::NamespaceInput
+            @disabled={{@oidcProviderQueryParam}}
+            @hvdManagedNamespace={{this.flags.hvdManagedNamespaceRoot}}
+            @namespaceValue={{this.namespaceInput}}
+            @updateNamespace={{this.handleNamespaceUpdate}}
+          />
+        {{/if}}
+      </:namespace>
 
-        <:back>
-          {{#if this.showOtherMethods}}
-            <Hds::Button
-              @text="Back"
-              {{on "click" this.toggleView}}
-              @color="tertiary"
-              @icon="arrow-left"
-              data-test-back-button
+      <:back>
+        {{#if this.showOtherMethods}}
+          <Hds::Button
+            @text="Back"
+            {{on "click" this.toggleView}}
+            @color="tertiary"
+            @icon="arrow-left"
+            data-test-back-button
+          />
+        {{/if}}
+      </:back>
+
+      {{! DIRECT LINK, TABS OR DROPDOWN }}
+      <:authSelectOptions>
+        <div class="has-bottom-margin-m">
+          {{#if this.tabData}}
+            <Auth::Tabs
+              @authTabData={{this.tabData}}
+              @handleTabClick={{this.setAuthType}}
+              @selectedAuthMethod={{this.selectedAuthMethod}}
             />
+          {{else}}
+            {{! Fallback is dropdown with all auth methods }}
+            <Hds::Form::Select::Field
+              name="selectedAuthMethod"
+              {{on "input" this.setTypeFromDropdown}}
+              data-test-select="auth type"
+              as |F|
+            >
+              <F.Label class="has-top-margin-m">Method</F.Label>
+              <F.Options>
+                {{#each this.supportedAuthTypes as |type|}}
+                  <option selected={{eq this.selectedAuthMethod type}} value={{type}}>
+                    {{auth-display-name type}}
+                  </option>
+                {{/each}}
+              </F.Options>
+            </Hds::Form::Select::Field>
           {{/if}}
-        </:back>
-
-        {{! DIRECT LINK, TABS OR DROPDOWN }}
-        <:authSelectOptions>
-          <div class="has-bottom-margin-m">
-            {{#if this.showCustomAuthOptions}}
-              <Auth::Tabs
-                @authTabData={{this.tabData}}
-                @handleTabClick={{this.setAuthType}}
-                @selectedAuthMethod={{this.selectedAuthMethod}}
-              />
-            {{else}}
-              {{! fallback view is the dropdown with all auth methods }}
-              <Hds::Form::Select::Field
-                name="selectedAuthMethod"
-                {{on "input" this.setTypeFromDropdown}}
-                data-test-select="auth type"
-                as |F|
-              >
-                <F.Label class="has-top-margin-m">Auth method</F.Label>
-                <F.Options>
-                  {{#each this.availableMethodTypes as |type|}}
-                    <option selected={{eq this.selectedAuthMethod type}} value={{type}}>
-                      {{auth-display-name type}}
-                    </option>
-                  {{/each}}
-                </F.Options>
-              </Hds::Form::Select::Field>
-            {{/if}}
-          </div>
-        </:authSelectOptions>
+        </div>
+      </:authSelectOptions>
 
-        <:error>
-          {{#if this.errorMessage}}
-            <MessageError @errorMessage={{this.errorMessage}} />
-          {{/if}}
-        </:error>
+      <:error>
+        {{#if this.errorMessage}}
+          <MessageError @errorMessage={{this.errorMessage}} />
+        {{/if}}
+      </:error>
 
-        <:advancedSettings>
-          {{! custom auth options render their own mount path inputs and token does not support custom paths }}
-          {{#if (and (not this.showCustomAuthOptions) (not-eq this.selectedAuthMethod "token"))}}
-            <Hds::Reveal @text="Advanced settings" data-test-auth-form-options-toggle class="is-fullwidth">
-              <Hds::Form::TextInput::Field name="path" data-test-input="path" as |F|>
-                <F.Label class="has-top-margin-m">Mount path</F.Label>
-                <F.HelperText>
-                  If this authentication method was mounted using a non-default path, input it below. Otherwise Vault will
-                  assume the default path
-                  <Hds::Text::Code class="code-in-text">{{this.selectedAuthMethod}}</Hds::Text::Code>
-                  .</F.HelperText>
-              </Hds::Form::TextInput::Field>
-            </Hds::Reveal>
-          {{/if}}
-        </:advancedSettings>
+      <:advancedSettings>
+        {{! custom auth options render their own mount path inputs and token does not support custom paths }}
+        {{#unless this.hideAdvancedSettings}}
path: ui/app/components/auth/form-template.hbs
user: login: lane-wetmore
body: Good job simplifying this conditional! It's so much easier to read now 😄 
created_at: 2025-05-21T15:58:12Z
author_association: CONTRIBUTOR
diff_hunk: @@ -29,71 +29,83 @@ import type { HTMLElementEvent } from 'vault/forms';
  * dynamically renders the corresponding form.
  *
  *
+ * @param {object | null} alternateView - if an alternate view is supported, form view ("tabs" or "dropdown") and tab data for that view
path: ui/app/components/auth/form-template.ts
user: login: lane-wetmore
body: nit: I think the wording could be made a little clearer here. Does this mean if the alternate view is supported, then the form view will be the tabs or dropdown? 
created_at: 2025-05-21T16:01:59Z
author_association: CONTRIBUTOR
diff_hunk: @@ -29,71 +29,83 @@ import type { HTMLElementEvent } from 'vault/forms';
  * dynamically renders the corresponding form.
  *
  *
+ * @param {object | null} alternateView - if an alternate view is supported, form view ("tabs" or "dropdown") and tab data for that view
  * @param {string} canceledMfaAuth - saved auth type from a cancelled mfa verification
  * @param {object} cluster - The route model which is the ember data cluster model. contains information such as cluster id, name and boolean for if the cluster is in standby
- * @param {object} directLinkData - mount data built from the "with" query param. If param is a mount path and maps to a visible mount, the login form defaults to this mount. Otherwise the form preselects the passed auth type.
+ * @param {object} defaultView - form view (default is "dropdown") and tab data if view is not the standard dropdown
path: ui/app/components/auth/form-template.ts
user: login: lane-wetmore
body: nit: same as the wording above, it's a bit unclear to me at first read what exactly this means
created_at: 2025-05-21T16:02:38Z
author_association: CONTRIBUTOR
diff_hunk: @@ -101,19 +126,24 @@ export default class AuthRoute extends ClusterRouteBase {
     In older versions of Vault, the "with" query param could refer to either the auth mount path or the type
     (which may be the same, since the default mount path *is* the type). 
     For backward compatibility, we handle both scenarios.
-    → If `authMount` matches a visible auth mount, return its mount data (which includes the type).
-    → If it matches a supported auth type instead, return just the type to preselect it in the dropdown.
+    → If `authMount` matches a visible auth mount the method will assume that mount path to login and render as the default in the login form.
+    → If `authMount` matches a supported auth type (and the mount does not have `listing_visibility="unauth"`), that is preselected in the login form.
path: ui/app/routes/vault/cluster/auth.js
user: login: lane-wetmore
body: nit: I think the wording could be the tiniest bit clearer here.
```suggestion
    → If `authMount` matches a supported auth type (and the mount does not have `listing_visibility="unauth"`), that type is preselected in the login form.
```
created_at: 2025-05-21T16:12:45Z
author_association: CONTRIBUTOR
diff_hunk: @@ -29,71 +29,83 @@ import type { HTMLElementEvent } from 'vault/forms';
  * dynamically renders the corresponding form.
  *
  *
+ * @param {object | null} alternateView - if an alternate view is supported, form view ("tabs" or "dropdown") and tab data for that view
path: ui/app/components/auth/form-template.ts
user: login: hellobontempo
body: Both `alternateView` and `defaultView` are `FormState` objects (see typescript args below). So if an alternate view is supported, this objects defines the `view` and if the view is `tabs` it specifies tab data. I'll push some changes, let me know if it's clearer!
created_at: 2025-05-21T17:17:48Z
author_association: CONTRIBUTOR
diff_hunk: @@ -29,71 +27,81 @@ import type { HTMLElementEvent } from 'vault/forms';
  * dynamically renders the corresponding form.
  *
  *
+ * @param {object | null} alternateView - if an alternate view exists, this is the `FormView` (see interface below) data to render that view.
path: ui/app/components/auth/form-template.ts
user: login: hellobontempo
body: @lane-wetmore - let me know if these docs are clearer 😄 
created_at: 2025-05-21T17:18:17Z
author_association: CONTRIBUTOR
diff_hunk: @@ -29,71 +27,81 @@ import type { HTMLElementEvent } from 'vault/forms';
  * dynamically renders the corresponding form.
  *
  *
+ * @param {object | null} alternateView - if an alternate view exists, this is the `FormView` (see interface below) data to render that view.
path: ui/app/components/auth/form-template.ts
user: login: lane-wetmore
body: Yep!
created_at: 2025-05-21T17:36:21Z
author_association: CONTRIBUTOR
diff_hunk: @@ -15,35 +15,52 @@
 
 {{#if @loginRules}}
   {{#each @loginRules as |rule|}}
-    <div class="list-item-row linked-block-item is-no-underline">
path: ui/lib/config-ui/addon/components/login-settings/page/list.hbs
user: login: hellobontempo
body: Makes the list item clickable
created_at: 2025-05-22T21:24:27Z
author_association: CONTRIBUTOR

PR comments:
user: login: github-actions[bot]
created_at: 2025-05-20T23:18:46Z
body: CI Results:
All Go tests succeeded! :white_check_mark:
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-05-20T23:19:53Z
body: Build Results:
All builds succeeded! :white_check_mark:
author_association: NONE
user: login: hellobontempo
created_at: 2025-05-21T01:47:20Z
body: <img width="557" alt="Screenshot 2025-05-20 at 6 46 56 PM" src="https://github.com/user-attachments/assets/a95f337f-f5ce-40a6-924f-d3727e68adc8" />

author_association: CONTRIBUTOR
user: login: hellobontempo
created_at: 2025-05-22T15:16:28Z
body: Enterprise tests ✅
author_association: CONTRIBUTOR
user: login: hellobontempo
created_at: 2025-05-22T22:13:06Z
body: <img width="432" alt="Screenshot 2025-05-22 at 3 12 38 PM" src="https://github.com/user-attachments/assets/1a0fe349-8a0c-4469-af01-199a80540517" />

author_association: CONTRIBUTOR