Files
cinny/src/app/pages/auth/login/Login.tsx
T
Lotus CIandClaude Opus 5.5 3c3bb2e41c
CI / Build & Quality Checks (push) Skipped
CI / Secret scan (gitleaks) (push) Skipped
CI / Playwright smoke (e2e) (push) Skipped
CI / Build & Quality Checks (pull_request) Successful in 3m41s
CI / Trigger Desktop Build (pull_request) Skipped
CI / Docker image build & smoke test (pull_request) Skipped
CI / Secret scan (gitleaks) (pull_request) Successful in 10s
CI / Playwright smoke (e2e) (pull_request) Successful in 16m45s
feat(auth): offer native OAuth sign-in on servers like matrix.org
The login page looked for an OAuth issuer in .well-known
(m.authentication), which matrix.org no longer publishes, so its users
got the password / SSO compatibility forms. Detect native OAuth the way
the SDK finds the auth server for refresh: load /auth_metadata next to
the login flows. When present:
- Login shows "Continue with <server>" only (as Element does); a legacy
  SSO loginToken coming back from a redirect still completes.
- Register opens the auth server's sign-up page (prompt=create, where
  prompt_values_supported says so) instead of the legacy forms.
Servers without it (Lotus) are unchanged.

Checked against the live servers: matrix.org login and register show
"Continue with matrix.org" and no password form; matrix.lotusguild.org
keeps its password form.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017PCb59uY3UKqDqt7B6hzd5
2026-10-24 16:15:24 -04:00

124 lines
4.6 KiB
TypeScript

import React, { useMemo } from 'react';
import { Box, Text, color } from 'folds';
import { Link, useSearchParams } from 'react-router-dom';
import { SSOAction } from 'matrix-js-sdk';
import { useAuthFlows } from '../../../hooks/useAuthFlows';
import { useAuthServer } from '../../../hooks/useAuthServer';
import { useParsedLoginFlows } from '../../../hooks/useParsedLoginFlows';
import { PasswordLoginForm } from './PasswordLoginForm';
import { SSOLogin } from '../SSOLogin';
import { TokenLogin } from './TokenLogin';
import { OidcLogin } from './OidcLogin';
import { useAutoDiscoveryInfo } from '../../../hooks/useAutoDiscoveryInfo';
import { OrDivider } from '../OrDivider';
import { getLoginPath, getRegisterPath, withSearchParam } from '../../pathUtils';
import { usePathWithOrigin } from '../../../hooks/usePathWithOrigin';
import { LoginPathSearchParams } from '../../paths';
import { useClientConfig } from '../../../hooks/useClientConfig';
const getLoginTokenSearchParam = () => {
// when using hasRouter query params in existing route
// gets ignored by react-router, so we need to read it ourself
// we only need to read loginToken as it's the only param that
// is provided by external entity. example: SSO login
const parmas = new URLSearchParams(window.location.search);
const loginToken = parmas.get('loginToken');
return loginToken ?? undefined;
};
const useLoginSearchParams = (searchParams: URLSearchParams): LoginPathSearchParams =>
useMemo(
() => ({
username: searchParams.get('username') ?? undefined,
email: searchParams.get('email') ?? undefined,
loginToken: searchParams.get('loginToken') ?? undefined,
}),
[searchParams],
);
export function Login() {
const server = useAuthServer();
const discovery = useAutoDiscoveryInfo();
const { hashRouter } = useClientConfig();
const { loginFlows, authMetadata } = useAuthFlows();
const [searchParams] = useSearchParams();
const loginSearchParams = useLoginSearchParams(searchParams);
const ssoRedirectUrl = usePathWithOrigin(getLoginPath(server));
const loginTokenForHashRouter = getLoginTokenSearchParam();
const absoluteLoginPath = usePathWithOrigin(getLoginPath(server));
if (hashRouter?.enabled && loginTokenForHashRouter) {
window.location.replace(
withSearchParam(absoluteLoginPath, {
loginToken: loginTokenForHashRouter,
}),
);
}
const parsedFlows = useParsedLoginFlows(loginFlows.flows);
// Next-gen auth (MSC3861): when the homeserver serves OAuth metadata
// (`/auth_metadata`, e.g. matrix.org via MAS), use the native OAuth flow and
// suppress the password / legacy-SSO forms, as Element does. Detected the way
// the SDK finds the auth server for refresh; the old `.well-known`
// `m.authentication` key is gone from matrix.org. A legacy SSO `loginToken`
// coming back from a redirect still completes below.
if (authMetadata && !loginSearchParams.loginToken) {
return (
<Box direction="Column" gap="500">
<Text as="h1" size="H2" priority="400">
Login
</Text>
<OidcLogin homeserverBaseUrl={discovery['m.homeserver'].base_url} serverName={server} />
<span data-spacing-node />
<Text align="Center">
Do not have an account? <Link to={getRegisterPath(server)}>Register</Link>
</Text>
</Box>
);
}
return (
<Box direction="Column" gap="500">
<Text as="h1" size="H2" priority="400">
Login
</Text>
{parsedFlows.token && loginSearchParams.loginToken && (
<TokenLogin token={loginSearchParams.loginToken} />
)}
{parsedFlows.password && (
<>
<PasswordLoginForm
defaultUsername={loginSearchParams.username}
defaultEmail={loginSearchParams.email}
/>
<span data-spacing-node />
{parsedFlows.sso && <OrDivider />}
</>
)}
{parsedFlows.sso && (
<>
<SSOLogin
providers={parsedFlows.sso.identity_providers}
redirectUrl={ssoRedirectUrl}
action={SSOAction.LOGIN}
saveScreenSpace={parsedFlows.password !== undefined}
/>
<span data-spacing-node />
</>
)}
{!parsedFlows.password && !parsedFlows.sso && (
<>
<Text style={{ color: color.Critical.Main }}>
{`This client does not support login on "${server}" homeserver. Password and SSO based login method not found.`}
</Text>
<span data-spacing-node />
</>
)}
<Text align="Center">
Do not have an account? <Link to={getRegisterPath(server)}>Register</Link>
</Text>
</Box>
);
}