[client][login] Allow entering token manually on login token page

This commit is contained in:
Abhishek Shroff
2025-07-16 14:18:18 +05:30
parent ca1aaf2848
commit a82917ff25
4 changed files with 91 additions and 68 deletions

View File

@@ -120,8 +120,8 @@ class PhylumRouteInformationParser extends RouteInformationParser<PhylumRoute> {
} else if (segments.length == 2) {
if (segments[0] == 'login' && segments[1] == 'token') {
return SynchronousFuture(TokenLoginRoute(
instanceUrl: uri.queryParameters['instance'] ?? '',
loginToken: uri.queryParameters['token'] ?? '',
instanceUrl: uri.queryParameters['instance'],
loginToken: uri.queryParameters['token'],
));
}
if (segments[0] == 'folder') {

View File

@@ -29,7 +29,7 @@ class TokenLoginRoute extends PhylumRoute {
final String? instanceUrl;
final String? loginToken;
TokenLoginRoute({required this.instanceUrl, required this.loginToken});
TokenLoginRoute({this.instanceUrl, this.loginToken});
@override
Uri get uri => Uri(path: '/login/token');

View File

@@ -200,12 +200,8 @@ class _LoginFragmentState extends State<LoginFragment> {
if ((response ?? true) || !context.mounted) return;
final token = await showInputDialog(context, title: 'Login Token', barrierDismissable: true);
if (token == null || !context.mounted) return;
Navigator.of(context).pop(false);
context.read<PhylumRouterDelegate>().go(TokenLoginRoute(
instanceUrl: widget.instanceConfig.url.toString(),
loginToken: token,
));
}
}

View File

@@ -1,13 +1,16 @@
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:http/http.dart';
import 'package:offtheline/offtheline.dart';
import 'package:phylum/libphylum/phylum_account.dart';
import 'package:phylum/libphylum/responses/responses.dart';
import 'package:phylum/ui/app/router.dart';
import 'package:phylum/ui/app/routes.dart';
import 'package:phylum/ui/login/request.dart';
import 'package:phylum/util/dialogs.dart';
import 'package:phylum/util/logging.dart';
import 'package:provider/provider.dart';
import 'package:uri/uri.dart';
import 'package:phylum/ui/app/dialog_scaffold.dart';
@@ -27,87 +30,111 @@ class TokenLoginPage extends StatefulWidget {
}
class _TokenLoginPageState extends State<TokenLoginPage> {
String? error;
late final instanceUrlController = TextEditingController(text: widget.instanceUrl);
late final loginTokenController = TextEditingController(text: widget.loginToken);
@override
void initState() {
super.initState();
performLogin(context);
if (widget.instanceUrl != null && widget.loginToken != null) {
final instanceUrl = Uri.tryParse(widget.instanceUrl!);
if (instanceUrl != null) {
WidgetsBinding.instance.addPostFrameCallback((d) {
_performLogin(context, instanceUrl, widget.loginToken!);
});
}
}
}
@override
void dispose() {
instanceUrlController.dispose();
loginTokenController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return PhylumDialogScaffold(
child: error == null
? const Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
spacing: 12.0,
children: [
CircularProgressIndicator(),
Text('Logging In'),
],
)
: Column(
mainAxisSize: MainAxisSize.min,
spacing: 6.0,
children: [
Text('Error: $error'),
ElevatedButton(
onPressed: () => context.read<PhylumRouterDelegate>().go(ExplorerRouteHome()),
child: Text('OK'),
),
],
child: Column(
mainAxisSize: MainAxisSize.min,
spacing: 6.0,
children: [
TextField(
decoration: InputDecoration(
label: Text('Instance URL'),
hintText: 'https://phylum.example.com',
),
controller: instanceUrlController,
enabled: widget.instanceUrl == null,
keyboardType: TextInputType.url,
),
TextField(
decoration: InputDecoration(
label: Text('Login Token'),
hintText: 'AG...',
),
controller: loginTokenController,
keyboardType: TextInputType.url,
textInputAction: TextInputAction.go,
onSubmitted: (value) {
final instanceUrl = Uri.tryParse(instanceUrlController.value.text);
if (instanceUrl != null) {
_performLogin(context, instanceUrl, value);
}
},
),
ElevatedButton(
onPressed: () {
final instanceUrl = Uri.tryParse(instanceUrlController.value.text);
if (instanceUrl != null) {
_performLogin(context, instanceUrl, loginTokenController.value.text);
}
},
child: Text('Log In'),
),
],
),
);
}
void performLogin(BuildContext context) async {
if (widget.instanceUrl == null || widget.loginToken == null) {
Future.microtask(() => setState(() => error = 'Missing Parameters'));
}
final instanceUrl = Uri.parse(widget.instanceUrl!);
Future<void> _performLogin(BuildContext context, Uri instanceUrl, String token) async {
final builder = UriBuilder.fromUri(instanceUrl);
builder.path = '${builder.path}/api/v1/auth/token/login';
final request = MultipartRequest('post', builder.build());
request.fields['token'] = widget.loginToken!;
request.fields['token'] = token;
try {
final response = await Client().send(request);
final responseString = await response.stream.bytesToString();
final responseMap = (jsonDecode(responseString) as Map).cast<String, dynamic>();
if (response.statusCode >= 200 && response.statusCode < 300) {
final responseString = await sendRequest(context, 'Logging In', request);
if (responseString == null) return;
if (context.mounted) {
final routerDelegate = context.read<PhylumRouterDelegate>();
final accountManager = context.read<AccountManager<PhylumAccount>>();
final navigator = Navigator.of(context);
showProgressDialog(context, barrierDismissible: false, message: 'Logging In');
try {
final response =
BootstrapLoginResponse.fromResponse((jsonDecode(responseString) as Map).cast<String, dynamic>());
final account = await PhylumAccount.create(
serverUrl: instanceUrl,
apiToken: response.apiToken!,
user: response.user,
);
await response.process(account);
await accountManager.addAccount(account);
navigator.pop();
routerDelegate.go(const ExplorerRouteHome());
} catch (e, stack) {
navigator.pop();
if (context.mounted) {
final routerDelegate = context.read<PhylumRouterDelegate>();
final accountManager = context.read<AccountManager<PhylumAccount>>();
final response = BootstrapLoginResponse.fromResponse(responseMap);
final account = await PhylumAccount.create(
serverUrl: instanceUrl,
apiToken: response.apiToken!,
user: response.user,
showAlertDialog(
context,
barrierDismissible: false,
title: 'Error',
message: e.toString(),
);
await response.process(account);
await accountManager.addAccount(account);
routerDelegate.go(ExplorerRouteHome());
}
} else {
if (context.mounted) {
setState(() => error = responseMap['msg']);
}
return null;
logger.w('Login Error', error: e, stackTrace: stack);
}
} on SocketException {
if (context.mounted) {
setState(() => error = 'unable to reach server');
}
return null;
} catch (e) {
if (context.mounted) {
setState(() => error = e.toString());
}
return null;
}
}
}