Skip to content

CompositeAuth does not proxy signIn from credentials sub-providers #17705

@cheyenne404

Description

@cheyenne404

Description

CompositeAuth proxies SSO (getLoginUrl, handleCallback), Session (createSession, validateSession), and User (getCurrentUser, getUser) interfaces from sub-providers, but does not proxy the credentials signIn interface. When using new CompositeAuth([apiKeyProvider, credentialsProvider]), the resulting auth object has no signIn method, causing:

  1. implementsInterface(auth, "signIn") returns false
  2. buildCapabilities() returns login: null
  3. Mastra Studio shows "Authentication Required — no login method is configured"
  4. POST /api/auth/credentials/sign-in returns 404

Steps to Reproduce

const auth = new CompositeAuth([simpleApiAuth, studioAuthProvider]);
// typeof auth.signIn === "undefined"  ← should be a function

const capabilities = await buildCapabilities(auth, request);
// capabilities.login === null  ← should be { type: "credentials" }

Root Cause

packages/core/src/server/composite-auth.tsCompositeAuth constructor checks for three provider interfaces but is missing isCredentialsProvider:

isSSOProvider        → getLoginUrl / handleCallback / getLoginButtonConfig
isSessionProvider    → createSession / validateSession / getSessionIdFromRequest
isUserProvider       → getCurrentUser / getUser
isCredentialsProvider → (missing)      ← signIn / isSignUpEnabled not proxied

PR #16664 fixed the duck-typing detection for the three existing interfaces but did not add credentials support.

Expected Behavior

const auth = new CompositeAuth([simpleApiAuth, studioAuthProvider]);
typeof auth.signIn === "function";  // true — delegated from studioAuthProvider

Proposed Fix

Three additions to packages/core/src/server/composite-auth.ts:

  1. Guard function:
function isCredentialsProvider(p: unknown): boolean {
  return p !== null && typeof p === "object" && typeof (p as any).signIn === "function";
}
  1. In CompositeAuth constructor, after the isUserProvider block:
if (!providers.some(isCredentialsProvider)) {
  (this as any).signIn = void 0;
  (this as any).isSignUpEnabled = void 0;
} else {
  const credProvider = providers.find(isCredentialsProvider)!;
  (this as any).signIn = credProvider.signIn.bind(credProvider);
  (this as any).isSignUpEnabled = () =>
    typeof credProvider.isSignUpEnabled === "function"
      ? credProvider.isSignUpEnabled()
      : false;
}

Workaround

Manually patch the CompositeAuth instance after construction:

const auth = new CompositeAuth([simpleApiAuth, studioAuthProvider]);
(auth as any).signIn = studioAuthProvider.signIn.bind(studioAuthProvider);
(auth as any).isSignUpEnabled = () => studioAuthProvider.isSignUpEnabled();

Related

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions