Phase 1C: @bytelyst/auth-client + @bytelyst/react-auth Google Sign-In
- loginWithGoogle/Microsoft/Apple(idToken) → POST /auth/oauth/:provider
- getProviders/linkProvider/unlinkProvider → provider management
- React context: loginWithGoogle, providers state, refreshProviders
Phase 2D: MFA + Social Login SDK + Auth UI
- verifyMfa/setupTotp/verifyTotpSetup/disableMfa/getMfaStatus
- regenerateRecoveryCodes → recovery code management
- React context: mfaRequired/mfaChallenge/mfaMethods state, verifyMfa action
- login() handles MfaLoginResult (returns false, sets MFA state)
- NEW @bytelyst/auth-ui: LoginForm, MfaChallenge, SocialButtons components
Phase 3: Passkeys + Device SDK
- getPasskeyRegisterOptions/verifyPasskeyRegistration
- getPasskeyAuthOptions/verifyPasskeyAuth/listPasskeys/deletePasskey
- listDevices/trustDevice/revokeDevice/revokeAllDevices
Phase 4C: @bytelyst/auth RS256 support
- createJwtUtils({ algorithm: 'RS256', rsaPrivateKey, rsaPublicKey })
- Dual verification: RS256 first, HS256 fallback (migration-safe)
- Remote JWKS support via jwksUrl option
- Backward-compatible: HS256 remains default
Phase 5B: Admin security endpoints
- getSecurityOverview/unlockUser/exportAuthData/cancelDeletion
Tests: 101 total (36 auth-client + 21 react-auth + 13 auth-ui + 31 auth)
Builds: all 4 packages pass tsc
49 lines
1.3 KiB
TypeScript
49 lines
1.3 KiB
TypeScript
import type { SocialButtonsProps, SocialProvider } from './types.js';
|
|
|
|
const PROVIDER_LABELS: Record<SocialProvider, string> = {
|
|
google: 'Google',
|
|
microsoft: 'Microsoft',
|
|
apple: 'Apple',
|
|
};
|
|
|
|
/**
|
|
* Renders social login buttons for the configured providers.
|
|
* Styled via CSS custom properties (inherits --bl-* from host app).
|
|
*/
|
|
export function SocialButtons({
|
|
providers,
|
|
onSelect,
|
|
disabled = false,
|
|
className,
|
|
}: SocialButtonsProps) {
|
|
return (
|
|
<div
|
|
className={className}
|
|
style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}
|
|
data-testid="bl-social-buttons"
|
|
>
|
|
{providers.map(provider => (
|
|
<button
|
|
key={provider}
|
|
type="button"
|
|
onClick={() => onSelect(provider)}
|
|
disabled={disabled}
|
|
data-testid={`bl-social-${provider}`}
|
|
style={{
|
|
padding: '10px 16px',
|
|
border: '1px solid var(--bl-border, #ccc)',
|
|
borderRadius: 'var(--bl-radius, 6px)',
|
|
background: 'var(--bl-surface, #fff)',
|
|
color: 'var(--bl-text, #333)',
|
|
cursor: disabled ? 'not-allowed' : 'pointer',
|
|
fontSize: '14px',
|
|
opacity: disabled ? 0.6 : 1,
|
|
}}
|
|
>
|
|
Continue with {PROVIDER_LABELS[provider]}
|
|
</button>
|
|
))}
|
|
</div>
|
|
);
|
|
}
|