feat: expand Gmail OAuth scopes, unify Google callback URL, store Gmail creds on login
- Add gmail.readonly to GMAIL_SCOPES (gmail_service) and GMAIL_API_SCOPES (providers) so users().getProfile() no longer returns 403 insufficientPermissions - Consolidate Gmail scope list: GMAIL_SCOPES in gmail_service.py is the single source of truth; auth.py and providers.py now import and spread it - Add include_granted_scopes=true to both Gmail and login authorize URLs so scope additions take effect for previously-connected users - Add state=gmail_connect to the Gmail authorize URL; the shared /auth/callback page routes to gmailApi.saveCallback() when this state is present, otherwise falls through to the normal login flow - Google Sign-In authorize URL now requests all six scopes (openid, email, profile + 3 Gmail scopes) with access_type=offline, prompt=consent, and include_granted_scopes=true - POST /auth/google now stores Gmail credentials automatically after sign-in (non-fatal: login succeeds even if credential storage fails) - Settings Connect Gmail button now uses /auth/callback instead of /auth/gmail-callback - only ONE redirect URI needed in Google Cloud Console - URL-encode scope parameter in both authorize URL builders - Update auth_service._register_google scope to include all Gmail scopes - Update CHANGELOG.md and docs/TODO.md Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com> Agent-Logs-Url: https://github.com/christianlouis/pop_puller_to_gmail/sessions/d6e2de8b-088d-46f3-8127-137245c3ecdd
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
import { Suspense, useEffect, useState } from 'react';
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import { authApi, userApi } from '@/lib/api';
|
||||
import { authApi, gmailApi, userApi } from '@/lib/api';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
import { Loader2, CheckCircle, XCircle } from 'lucide-react';
|
||||
|
||||
@@ -17,6 +17,7 @@ function AuthCallbackContent() {
|
||||
const handleCallback = async () => {
|
||||
const code = searchParams.get('code');
|
||||
const error = searchParams.get('error');
|
||||
const state = searchParams.get('state');
|
||||
|
||||
if (error) {
|
||||
setStatus('error');
|
||||
@@ -32,8 +33,31 @@ function AuthCallbackContent() {
|
||||
return;
|
||||
}
|
||||
|
||||
const redirectUri = `${window.location.origin}/auth/callback`;
|
||||
|
||||
// Gmail reconnect flow: user was already logged in and clicked
|
||||
// "Connect Gmail" in Settings. The authorize URL includes state=gmail_connect.
|
||||
if (state === 'gmail_connect') {
|
||||
try {
|
||||
await gmailApi.saveCallback(code, redirectUri);
|
||||
setStatus('success');
|
||||
setMessage('Gmail connected successfully! Redirecting to settings…');
|
||||
setTimeout(() => router.push('/settings'), 1500);
|
||||
} catch (err: unknown) {
|
||||
const detail =
|
||||
err instanceof Error && 'response' in err
|
||||
? (err as { response?: { data?: { detail?: string } } }).response?.data
|
||||
?.detail
|
||||
: null;
|
||||
setStatus('error');
|
||||
setMessage(detail || 'Failed to connect Gmail. Please try again.');
|
||||
setTimeout(() => router.push('/settings'), 3000);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Normal Google Sign-In flow
|
||||
try {
|
||||
const redirectUri = `${window.location.origin}/auth/callback`;
|
||||
const response = await authApi.googleAuth(code, redirectUri);
|
||||
|
||||
localStorage.setItem('access_token', response.access_token);
|
||||
|
||||
@@ -175,7 +175,7 @@ function SettingsContent() {
|
||||
|
||||
const handleConnectGmail = async () => {
|
||||
try {
|
||||
const redirectUri = `${window.location.origin}/auth/gmail-callback`;
|
||||
const redirectUri = `${window.location.origin}/auth/callback`;
|
||||
const url = await gmailApi.getAuthorizeUrl(redirectUri);
|
||||
window.location.href = url;
|
||||
} catch (error) {
|
||||
|
||||
Reference in New Issue
Block a user