fix: harden OAuth login flow — open redirect, path traversal, info leak

- Validate authorize_url is HTTPS before redirect (open redirect prevention)
- encodeURIComponent on provider name in API URL paths (path traversal prevention)
- encodeURIComponent on referralCode in t.me deep link
- Gate console.warn behind import.meta.env.DEV (no info leak in prod)
This commit is contained in:
Fringg
2026-02-11 02:58:04 +03:00
parent f74e316161
commit a744b41910
2 changed files with 17 additions and 5 deletions

View File

@@ -138,7 +138,7 @@ export const authApi = {
provider: string,
): Promise<{ authorize_url: string; state: string }> => {
const response = await apiClient.get<{ authorize_url: string; state: string }>(
`/cabinet/auth/oauth/${provider}/authorize`,
`/cabinet/auth/oauth/${encodeURIComponent(provider)}/authorize`,
);
return response.data;
},
@@ -146,7 +146,7 @@ export const authApi = {
// OAuth: callback (exchange code for tokens)
oauthCallback: async (provider: string, code: string, state: string): Promise<AuthResponse> => {
const response = await apiClient.post<AuthResponse>(
`/cabinet/auth/oauth/${provider}/callback`,
`/cabinet/auth/oauth/${encodeURIComponent(provider)}/callback`,
{ code, state },
);
return response.data;

View File

@@ -109,6 +109,17 @@ export default function Login() {
setOauthLoading(provider);
try {
const { authorize_url, state } = await authApi.getOAuthAuthorizeUrl(provider);
// Validate redirect URL — only allow HTTPS to prevent open redirect
try {
const parsed = new URL(authorize_url);
if (parsed.protocol !== 'https:') {
throw new Error('Invalid OAuth redirect URL');
}
} catch {
throw new Error('Invalid OAuth redirect URL');
}
saveOAuthState(state, provider);
window.location.href = authorize_url;
} catch {
@@ -122,7 +133,7 @@ export default function Login() {
// If email auth is disabled but user came with ref param, redirect to bot
useEffect(() => {
if (referralCode && emailAuthConfig?.enabled === false && botUsername) {
window.location.href = `https://t.me/${botUsername}?start=${referralCode}`;
window.location.href = `https://t.me/${botUsername}?start=${encodeURIComponent(referralCode)}`;
}
}, [referralCode, emailAuthConfig, botUsername]);
@@ -165,7 +176,8 @@ export default function Login() {
const error = err as { response?: { status?: number; data?: { detail?: string } } };
const status = error.response?.status;
const detail = error.response?.data?.detail;
console.warn(`Telegram auth attempt ${attempt + 1} failed:`, status, detail);
if (import.meta.env.DEV)
console.warn(`Telegram auth attempt ${attempt + 1} failed:`, status, detail);
if (status === 401 && attempt < MAX_RETRIES) {
await new Promise((r) => setTimeout(r, 1500));
@@ -200,7 +212,7 @@ export default function Login() {
const error = err as { response?: { status?: number; data?: { detail?: string } } };
const status = error.response?.status;
const detail = error.response?.data?.detail;
console.warn('Telegram auth retry failed:', status, detail);
if (import.meta.env.DEV) console.warn('Telegram auth retry failed:', status, detail);
setError(
detail ||
t('auth.telegramRetryFailed', 'Authorization failed. Close the app and try again.'),