Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions packages/auth/src/LoginForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ export interface LoginFormLabels {
signUpText?: string;
/** Divider label between social sign-in and email/password (defaults to "or") */
orText?: string;
/** Label for the SSO sign-in button (defaults to "Sign in with SSO") */
ssoButton?: string;
}

export interface LoginFormProps {
Expand Down Expand Up @@ -130,6 +132,7 @@ export function LoginForm({
noAccountText: labels.noAccountText ?? "Don't have an account?",
signUpText: labels.signUpText ?? 'Sign up',
orText: labels.orText ?? 'or',
ssoButton: labels.ssoButton ?? 'Sign in with SSO',
};

const handleSubmit = async (e: React.FormEvent) => {
Expand All @@ -147,6 +150,33 @@ export function LoginForm({
}
};

/**
* Federated SSO sign-in: routes the entered email to its configured IdP
* (better-auth `@better-auth/sso`, by email domain) and redirects the
* browser to the provider's authorization endpoint. Falls back to an inline
* error when no provider matches the domain.
*/
const handleSso = async () => {
setError(null);
try {
const base = window.location.pathname.replace(/\/login(?:\/.*)?$/, '');
const res = await fetch('/api/v1/auth/sign-in/sso', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ email, callbackURL: base + '/home' }),
credentials: 'include',
});
const data = (await res.json().catch(() => ({}))) as { url?: string; message?: string };
if (res.ok && typeof data.url === 'string') {
window.location.href = data.url;
return;
}
setError(data.message || 'No SSO provider is configured for this email domain.');
} catch (err) {
setError(err instanceof Error ? err.message : String(err));
}
};

return (
<div className="mx-auto flex w-full flex-col justify-center space-y-7 sm:w-[400px]">
<AuthFormHeader
Expand Down Expand Up @@ -215,6 +245,15 @@ export function LoginForm({
{isLoading && <AuthSpinner />}
{isLoading ? l.submittingButton : l.submitButton}
</button>

<button
type="button"
onClick={handleSso}
disabled={isLoading}
className="flex w-full items-center justify-center rounded-md border border-input bg-background px-4 py-2 text-sm font-medium transition-colors hover:bg-accent disabled:opacity-50"
>
{l.ssoButton}
</button>
</form>
</div>

Expand Down
Loading