feat: add OAuth redirect handling and update Google OAuth integration
This commit is contained in:
@@ -22,7 +22,9 @@
|
||||
"Bash(source:*)",
|
||||
"Bash(npm install:*)",
|
||||
"Bash(npx tsc:*)",
|
||||
"Bash(npx expo:*)"
|
||||
"Bash(npx expo:*)",
|
||||
"Bash(find /var/home/felipecn/DEV/MeowSpool/mobile -name .env* -o -name *.env*)",
|
||||
"Bash(grep -r \"useURL\\\\|useDeepLinking\\\\|Linking.parse\\\\|useLinkTo\" /var/home/felipecn/DEV/MeowSpool/mobile/app --include=*.tsx)"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { z } from 'zod';
|
||||
import { Ionicons } from '@expo/vector-icons';
|
||||
import * as Google from 'expo-auth-session/providers/google';
|
||||
import * as WebBrowser from 'expo-web-browser';
|
||||
import { Screen } from '@presentation/components/layout/Screen';
|
||||
import { Input } from '@presentation/components/ui/Input';
|
||||
import { Button } from '@presentation/components/ui/Button';
|
||||
@@ -16,9 +15,6 @@ import { loginUseCase, googleLoginUseCase } from '@infrastructure/container';
|
||||
import { useAuthStore } from '@store/authStore';
|
||||
import { CatIcon } from '@presentation/components/icons/CatIcon';
|
||||
|
||||
// Necessário para fechar o browser após redirect OAuth no Android/iOS
|
||||
WebBrowser.maybeCompleteAuthSession();
|
||||
|
||||
const schema = z.object({
|
||||
email: z.string().email('E-mail inválido'),
|
||||
password: z.string().min(1, 'Senha obrigatória'),
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
import * as WebBrowser from 'expo-web-browser';
|
||||
|
||||
// Esta tela é o destino do redirect OAuth (meowspool://oauthredirect?code=...).
|
||||
// maybeCompleteAuthSession() detecta os parâmetros da URL, sinaliza o
|
||||
// expo-auth-session para fechar o browser e entregar o resultado ao hook
|
||||
// useAuthRequest em login.tsx.
|
||||
WebBrowser.maybeCompleteAuthSession();
|
||||
|
||||
export default function OAuthRedirect(): null {
|
||||
return null;
|
||||
}
|
||||
@@ -2,16 +2,15 @@
|
||||
* Plugin local: injeta o intent filter do Google OAuth no AndroidManifest.xml
|
||||
* durante o `expo prebuild`.
|
||||
*
|
||||
* Sem esse intent filter, o Android não sabe que o MeowSpool deve interceptar
|
||||
* o redirect do Google OAuth (scheme com.googleusercontent.apps.*), fazendo o
|
||||
* Chrome tratar a URL como pesquisa.
|
||||
*
|
||||
* O scheme é o reverse do Android OAuth 2.0 client ID registrado no Google Cloud Console.
|
||||
* expo-auth-session v5+ usa ${Application.applicationId}:/oauthredirect como
|
||||
* redirect URI nativo (ex: com.meowspool.app:/oauthredirect).
|
||||
* Sem esse intent filter, o Android não intercepta o redirect e o Chrome
|
||||
* trata a URL como pesquisa, redirecionando para o google.com.
|
||||
*/
|
||||
const { withAndroidManifest } = require('@expo/config-plugins');
|
||||
const { withAndroidManifest } = require("@expo/config-plugins");
|
||||
|
||||
const GOOGLE_REVERSE_CLIENT_ID =
|
||||
'com.googleusercontent.apps.724520558909-bg5a7e4u24jmis8lgg41ucs0kv0nfp1v';
|
||||
// Scheme gerado por expo-auth-session v7: applicationId do app
|
||||
const APP_SCHEME = "com.meowspool.app";
|
||||
|
||||
module.exports = function withGoogleOAuth(config) {
|
||||
return withAndroidManifest(config, (config) => {
|
||||
@@ -19,29 +18,29 @@ module.exports = function withGoogleOAuth(config) {
|
||||
const activities = manifest.manifest.application?.[0]?.activity ?? [];
|
||||
|
||||
const mainActivity = activities.find(
|
||||
(a) => a.$?.['android:name'] === '.MainActivity',
|
||||
(a) => a.$?.["android:name"] === ".MainActivity",
|
||||
);
|
||||
|
||||
if (!mainActivity) return config;
|
||||
|
||||
// Evita duplicar o intent filter em rebuilds consecutivos
|
||||
const alreadyAdded = (mainActivity['intent-filter'] ?? []).some((filter) =>
|
||||
const alreadyAdded = (mainActivity["intent-filter"] ?? []).some((filter) =>
|
||||
(filter.data ?? []).some(
|
||||
(d) => d.$?.['android:scheme'] === GOOGLE_REVERSE_CLIENT_ID,
|
||||
(d) => d.$?.["android:scheme"] === APP_SCHEME,
|
||||
),
|
||||
);
|
||||
|
||||
if (alreadyAdded) return config;
|
||||
|
||||
mainActivity['intent-filter'] = [
|
||||
...(mainActivity['intent-filter'] ?? []),
|
||||
mainActivity["intent-filter"] = [
|
||||
...(mainActivity["intent-filter"] ?? []),
|
||||
{
|
||||
action: [{ $: { 'android:name': 'android.intent.action.VIEW' } }],
|
||||
action: [{ $: { "android:name": "android.intent.action.VIEW" } }],
|
||||
category: [
|
||||
{ $: { 'android:name': 'android.intent.category.DEFAULT' } },
|
||||
{ $: { 'android:name': 'android.intent.category.BROWSABLE' } },
|
||||
{ $: { "android:name": "android.intent.category.DEFAULT" } },
|
||||
{ $: { "android:name": "android.intent.category.BROWSABLE" } },
|
||||
],
|
||||
data: [{ $: { 'android:scheme': GOOGLE_REVERSE_CLIENT_ID } }],
|
||||
data: [{ $: { "android:scheme": APP_SCHEME } }],
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
SafeAreaView,
|
||||
ScrollView,
|
||||
KeyboardAvoidingView,
|
||||
View,
|
||||
@@ -8,6 +7,7 @@ import {
|
||||
StyleSheet,
|
||||
type ViewStyle,
|
||||
} from 'react-native';
|
||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||
import { colors, spacing } from '@shared/theme';
|
||||
|
||||
interface ScreenProps {
|
||||
|
||||
Reference in New Issue
Block a user