auth: replace password login with OIDC PKCE flow (Phase 5)
- Install expo-auth-session + expo-web-browser - Add 'bincio-rec' URL scheme to app.json for deep-link redirect - auth.ts: generate PKCE verifier/challenge, open bincio.org/oauth2/authorize in browser, exchange auth code for RS256 id_token, store in AsyncStorage - SettingsScreen: remove handle/password fields, single 'Sign in with bincio' button that opens the browser flow
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import {
|
||||
View, Text, TextInput, StyleSheet, Switch,
|
||||
View, Text, StyleSheet, Switch,
|
||||
TouchableOpacity, Alert, ScrollView, ActivityIndicator,
|
||||
} from 'react-native';
|
||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||
@@ -131,34 +131,28 @@ function AppTab() {
|
||||
|
||||
function SyncTab() {
|
||||
const { accent } = useTheme();
|
||||
const [instanceUrl, setInstanceUrl] = useState('');
|
||||
const [handle, setHandle] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
const [connectedAs, setConnectedAs] = useState<string | null>(null);
|
||||
const [connecting, setConnecting] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
loadAuthState().then((auth) => {
|
||||
if (auth) { setInstanceUrl(auth.instanceUrl); setHandle(auth.handle); setConnectedAs(auth.handle); }
|
||||
if (auth) setConnectedAs(auth.handle);
|
||||
});
|
||||
}, []);
|
||||
|
||||
async function handleConnect() {
|
||||
if (!instanceUrl.trim()) { Alert.alert('Required', 'Enter the instance URL.'); return; }
|
||||
if (!handle.trim()) { Alert.alert('Required', 'Enter your handle.'); return; }
|
||||
if (!password) { Alert.alert('Required', 'Enter your password.'); return; }
|
||||
setConnecting(true);
|
||||
const result = await login(instanceUrl.trim(), handle.trim(), password);
|
||||
const result = await login();
|
||||
setConnecting(false);
|
||||
if (result.ok) { setConnectedAs(result.displayName || handle.trim()); setPassword(''); }
|
||||
else Alert.alert('Login failed', result.error ?? 'Unknown error');
|
||||
if (result.ok) setConnectedAs(result.displayName ?? '');
|
||||
else if (result.error !== 'Cancelled') Alert.alert('Sign in failed', result.error ?? 'Unknown error');
|
||||
}
|
||||
|
||||
async function handleDisconnect() {
|
||||
Alert.alert('Disconnect', 'Remove saved credentials?', [
|
||||
{ text: 'Cancel', style: 'cancel' },
|
||||
{ text: 'Disconnect', style: 'destructive', onPress: async () => {
|
||||
await logout(); setConnectedAs(null); setHandle(''); setPassword('');
|
||||
await logout(); setConnectedAs(null);
|
||||
}},
|
||||
]);
|
||||
}
|
||||
@@ -167,18 +161,6 @@ function SyncTab() {
|
||||
<ScrollView contentContainerStyle={styles.content}>
|
||||
<Text style={styles.sectionTitle}>bincio instance</Text>
|
||||
|
||||
<Text style={styles.label}>Instance URL</Text>
|
||||
<TextInput
|
||||
style={styles.input}
|
||||
value={instanceUrl}
|
||||
onChangeText={setInstanceUrl}
|
||||
placeholder="https://bincio.example.com"
|
||||
placeholderTextColor={colors.placeholder}
|
||||
autoCapitalize="none"
|
||||
keyboardType="url"
|
||||
editable={!connectedAs}
|
||||
/>
|
||||
|
||||
{connectedAs ? (
|
||||
<View style={styles.connectedBox}>
|
||||
<View>
|
||||
@@ -191,16 +173,9 @@ function SyncTab() {
|
||||
</View>
|
||||
) : (
|
||||
<>
|
||||
<Text style={styles.label}>Handle</Text>
|
||||
<TextInput style={styles.input} value={handle} onChangeText={setHandle} placeholder="your-handle" placeholderTextColor={colors.placeholder} autoCapitalize="none" autoCorrect={false} />
|
||||
|
||||
<Text style={styles.label}>Password</Text>
|
||||
<TextInput style={styles.input} value={password} onChangeText={setPassword} placeholder="••••••••" placeholderTextColor={colors.placeholder} secureTextEntry />
|
||||
|
||||
<Text style={styles.hint}>Your password is used once to obtain a session token, then forgotten.</Text>
|
||||
|
||||
<Text style={styles.hint}>Sign in with your bincio account to sync recordings.</Text>
|
||||
<TouchableOpacity style={[styles.connectBtn, connecting && styles.connectBtnDisabled]} onPress={handleConnect} disabled={connecting}>
|
||||
{connecting ? <ActivityIndicator color={colors.text} /> : <Text style={styles.connectBtnText}>Connect</Text>}
|
||||
{connecting ? <ActivityIndicator color={colors.text} /> : <Text style={styles.connectBtnText}>Sign in with bincio</Text>}
|
||||
</TouchableOpacity>
|
||||
</>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user