dotnet/aspnetcore · error · Error

Authentication refresh is only supported with HTTP-based con

Error message

Authentication refresh is only supported with HTTP-based connections.

What it means

Thrown at HubConnection.ts:625 when `refreshAuthentication()` is called on a connection whose `features.authenticationRefresh` is not set. The authentication refresh feature is only configured by HttpConnection after a successful negotiate (see _configureAuthenticationRefresh at HttpConnection.ts:402); a non-HTTP connection (custom IConnection, or skipNegotiate which skips negotiate) does not set it.

Source

Thrown at src/SignalR/clients/ts/signalr/src/HubConnection.ts:625

     */
    public onreconnected(callback: (connectionId?: string) => void): void {
        if (callback) {
            this._reconnectedCallbacks.push(callback);
        }
    }

    /** Refreshes the authentication state for this connection.
     *
     * @returns A Promise that resolves with the new server-reported token lifetime in seconds, or undefined when the server does not report one.
     */
    public async refreshAuthentication(): Promise<number | undefined> {
        if (this._connectionState !== HubConnectionState.Connected) {
            throw new Error("Cannot refresh authentication when the connection is not active.");
        }

        const authenticationRefreshFeature = this.connection.features.authenticationRefresh as IAuthenticationRefreshFeature | undefined;
        if (!authenticationRefreshFeature) {
            throw new Error("Authentication refresh is only supported with HTTP-based connections.");
        }

        let newTokenLifetimeInSeconds: number | undefined;
        try {
            newTokenLifetimeInSeconds = await authenticationRefreshFeature.refreshAuthentication();
        } catch (e) {
            await this._invokeAuthenticationRefreshFailed(e);
            throw e;
        }

        if (this._connectionState === HubConnectionState.Connected &&
            this.connection.features.authenticationRefresh === authenticationRefreshFeature &&
            this._isAutoAuthenticationRefreshEnabled() &&
            isValidAuthenticationTokenLifetime(newTokenLifetimeInSeconds)) {
            this._scheduleAuthenticationRefresh(newTokenLifetimeInSeconds);
        }

        await this._invokeAuthenticationRefreshed(newTokenLifetimeInSeconds);

View on GitHub (pinned to 294cab2f9b)

Solutions

  1. If you need refresh, use a standard HTTP-based HttpConnection without skipNegotiation so negotiate runs and sets the feature.
  2. For a custom IConnection, expose `features.authenticationRefresh = { initialTokenLifetimeInSeconds, refreshAuthentication }`.
  3. If you skipped negotiate, acquire app tokens via the accessTokenFactory instead of server refresh.

Example fix

// before
new HubConnectionBuilder().withUrl(url, { skipNegotiation: true, transport: HttpTransportType.WebSockets }).build();
await hub.refreshAuthentication(); // throws - feature not set

// after - use accessTokenFactory for app-side tokens
new HubConnectionBuilder().withUrl(url, { accessTokenFactory: () => getJwt() }).build();
Defensive patterns

Strategy: type-guard

Validate before calling

function hasRefreshFeature(conn) {
  return !!conn && !!conn.features && typeof conn.features.authenticationRefresh?.refreshAuthentication === 'function';
}
if (hasRefreshFeature(hub.connection)) await hub.refreshAuthentication();
else { await hub.stop(); await hub.start(); }

Type guard

function supportsAuthRefresh(hub: HubConnection): boolean {
  const f = (hub as any).connection?.features?.authenticationRefresh;
  return !!f && typeof f.refreshAuthentication === 'function';
}

Try / catch

try { await hub.refreshAuthentication(); }
catch (e) {
  if (/HTTP-based connections/.test(String(e))) {
    // no server-side refresh; do a full reconnect using accessTokenFactory
    await hub.stop(); await hub.start();
  } else throw e;
}

Prevention

When it happens

Trigger: Using a custom IConnection implementation that doesn't expose the authenticationRefresh feature; constructing HttpConnection with `skipNegotiation: true` (which bypasses _configureAuthenticationRefresh); calling refreshAuthentication on a mock connection in tests.

Common situations: Injecting a custom IConnection (e.g. a mock or a non-HTTP transport) but expecting refresh to work; using skipNegotiation with WebSockets and still wanting server-side token refresh; tests that build a HubConnection over a fake connection.

Understand the failure class

Related errors


AI-assisted analysis of dotnet/aspnetcore@294cab2f9b (2026-08-06). Data as JSON: /api/errors/29bf11ad972ce250. Report an issue: GitHub.