👽 Update the OIDC login
This commit is contained in:
@ -9,6 +9,7 @@ import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
import 'package:island/pods/config.dart';
|
||||
import 'package:island/pods/network.dart';
|
||||
import 'package:island/services/udid.dart';
|
||||
import 'package:island/widgets/app_scaffold.dart';
|
||||
import 'package:styled_widget/styled_widget.dart';
|
||||
|
||||
@ -27,6 +28,13 @@ class _OidcScreenState extends ConsumerState<OidcScreen> {
|
||||
String? currentUrl;
|
||||
final TextEditingController _urlController = TextEditingController();
|
||||
bool _isLoading = true;
|
||||
late Future<String> _deviceIdFuture;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_deviceIdFuture = getUdid();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
@ -43,155 +51,174 @@ class _OidcScreenState extends ConsumerState<OidcScreen> {
|
||||
appBar: AppBar(
|
||||
title: widget.title != null ? Text(widget.title!) : Text('login').tr(),
|
||||
),
|
||||
body: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: InAppWebView(
|
||||
initialSettings: InAppWebViewSettings(
|
||||
userAgent:
|
||||
kIsWeb
|
||||
? null
|
||||
: Platform.isIOS
|
||||
? 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1'
|
||||
: Platform.isAndroid
|
||||
? 'Mozilla/5.0 (Linux; Android 13) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Mobile Safari/537.36'
|
||||
: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36',
|
||||
),
|
||||
initialUrlRequest: URLRequest(
|
||||
url: WebUri('$serverUrl/auth/login/${widget.provider}'),
|
||||
headers: {
|
||||
if (token?.token.isNotEmpty ?? false)
|
||||
'Authorization': 'AtField ${token!.token}',
|
||||
},
|
||||
),
|
||||
onWebViewCreated: (controller) {
|
||||
// Register a handler to receive the token from JavaScript
|
||||
controller.addJavaScriptHandler(
|
||||
handlerName: 'tokenHandler',
|
||||
callback: (args) {
|
||||
// args[0] will be the token string
|
||||
if (args.isNotEmpty && args[0] is String) {
|
||||
body: FutureBuilder<String>(
|
||||
future: _deviceIdFuture,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
|
||||
if (snapshot.hasError) {
|
||||
return Center(child: Text('somethingWentWrong').tr());
|
||||
}
|
||||
|
||||
final deviceId = snapshot.data!;
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: InAppWebView(
|
||||
initialSettings: InAppWebViewSettings(
|
||||
userAgent:
|
||||
kIsWeb
|
||||
? null
|
||||
: Platform.isIOS
|
||||
? 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1'
|
||||
: Platform.isAndroid
|
||||
? 'Mozilla/5.0 (Linux; Android 13) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Mobile Safari/537.36'
|
||||
: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36',
|
||||
),
|
||||
initialUrlRequest: URLRequest(
|
||||
url: WebUri('$serverUrl/auth/login/${widget.provider}'),
|
||||
headers: {
|
||||
if (token?.token.isNotEmpty ?? false)
|
||||
'Authorization': 'AtField ${token!.token}',
|
||||
'X-Device-Id': deviceId,
|
||||
},
|
||||
),
|
||||
onWebViewCreated: (controller) {
|
||||
// Register a handler to receive the token from JavaScript
|
||||
controller.addJavaScriptHandler(
|
||||
handlerName: 'tokenHandler',
|
||||
callback: (args) {
|
||||
// args[0] will be the token string
|
||||
if (args.isNotEmpty && args[0] is String) {
|
||||
setState(() {
|
||||
authToken = args[0];
|
||||
});
|
||||
|
||||
// Return the token and close the webview
|
||||
Navigator.of(context).pop(authToken);
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
shouldOverrideUrlLoading: (
|
||||
controller,
|
||||
navigationAction,
|
||||
) async {
|
||||
final url = navigationAction.request.url;
|
||||
if (url != null) {
|
||||
setState(() {
|
||||
authToken = args[0];
|
||||
currentUrl = url.toString();
|
||||
_urlController.text = currentUrl ?? '';
|
||||
_isLoading = true;
|
||||
});
|
||||
|
||||
// Return the token and close the webview
|
||||
Navigator.of(context).pop(authToken);
|
||||
final path = url.path;
|
||||
final queryParams = url.queryParameters;
|
||||
|
||||
// Check if we're on the token page
|
||||
if (path.endsWith('/auth/callback')) {
|
||||
// Extract token from URL
|
||||
final challenge = queryParams['challenge'];
|
||||
// Return the token and close the webview
|
||||
Navigator.of(context).pop(challenge);
|
||||
return NavigationActionPolicy.CANCEL;
|
||||
}
|
||||
}
|
||||
return NavigationActionPolicy.ALLOW;
|
||||
},
|
||||
onUpdateVisitedHistory: (controller, url, androidIsReload) {
|
||||
if (url != null) {
|
||||
setState(() {
|
||||
currentUrl = url.toString();
|
||||
_urlController.text = currentUrl ?? '';
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
shouldOverrideUrlLoading: (controller, navigationAction) async {
|
||||
final url = navigationAction.request.url;
|
||||
if (url != null) {
|
||||
setState(() {
|
||||
currentUrl = url.toString();
|
||||
_urlController.text = currentUrl ?? '';
|
||||
_isLoading = true;
|
||||
});
|
||||
|
||||
final path = url.path;
|
||||
final queryParams = url.queryParameters;
|
||||
|
||||
// Check if we're on the token page
|
||||
if (path.contains('/auth/callback')) {
|
||||
// Extract token from URL
|
||||
final token = queryParams['token'] ?? true;
|
||||
// Return the token and close the webview
|
||||
Navigator.of(context).pop(token);
|
||||
return NavigationActionPolicy.CANCEL;
|
||||
}
|
||||
}
|
||||
return NavigationActionPolicy.ALLOW;
|
||||
},
|
||||
onUpdateVisitedHistory: (controller, url, androidIsReload) {
|
||||
if (url != null) {
|
||||
setState(() {
|
||||
currentUrl = url.toString();
|
||||
_urlController.text = currentUrl ?? '';
|
||||
});
|
||||
}
|
||||
},
|
||||
onLoadStop: (controller, url) {
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
},
|
||||
onLoadStart: (controller, url) {
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
});
|
||||
},
|
||||
onLoadError: (controller, url, code, message) {
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
// Loading progress indicator
|
||||
if (_isLoading)
|
||||
LinearProgressIndicator(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
backgroundColor: Theme.of(context).colorScheme.surfaceVariant,
|
||||
borderRadius: BorderRadius.zero,
|
||||
stopIndicatorRadius: 0,
|
||||
minHeight: 2,
|
||||
)
|
||||
else
|
||||
ColoredBox(
|
||||
color: Theme.of(context).colorScheme.surfaceVariant,
|
||||
).height(2),
|
||||
// Debug location bar (only visible in debug mode)
|
||||
Container(
|
||||
padding: EdgeInsets.only(
|
||||
left: 16,
|
||||
right: 0,
|
||||
bottom: MediaQuery.of(context).padding.bottom + 8,
|
||||
top: 8,
|
||||
),
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: TextField(
|
||||
controller: _urlController,
|
||||
decoration: InputDecoration(
|
||||
isDense: true,
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 8,
|
||||
vertical: 8,
|
||||
),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
),
|
||||
hintText: 'URL',
|
||||
),
|
||||
style: const TextStyle(fontSize: 12),
|
||||
readOnly: true,
|
||||
),
|
||||
onLoadStop: (controller, url) {
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
},
|
||||
onLoadStart: (controller, url) {
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
});
|
||||
},
|
||||
onLoadError: (controller, url, code, message) {
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
},
|
||||
),
|
||||
const Gap(4),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.copy, size: 20),
|
||||
padding: const EdgeInsets.all(4),
|
||||
constraints: const BoxConstraints(),
|
||||
onPressed: () {
|
||||
if (currentUrl != null) {
|
||||
Clipboard.setData(ClipboardData(text: currentUrl!));
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text('copyToClipboard').tr(),
|
||||
duration: const Duration(seconds: 1),
|
||||
),
|
||||
// Loading progress indicator
|
||||
if (_isLoading)
|
||||
LinearProgressIndicator(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
backgroundColor: Theme.of(context).colorScheme.surfaceVariant,
|
||||
borderRadius: BorderRadius.zero,
|
||||
stopIndicatorRadius: 0,
|
||||
minHeight: 2,
|
||||
)
|
||||
else
|
||||
ColoredBox(
|
||||
color: Theme.of(context).colorScheme.surfaceVariant,
|
||||
).height(2),
|
||||
// Debug location bar (only visible in debug mode)
|
||||
Container(
|
||||
padding: EdgeInsets.only(
|
||||
left: 16,
|
||||
right: 0,
|
||||
bottom: MediaQuery.of(context).padding.bottom + 8,
|
||||
top: 8,
|
||||
),
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: TextField(
|
||||
controller: _urlController,
|
||||
decoration: InputDecoration(
|
||||
isDense: true,
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 8,
|
||||
vertical: 8,
|
||||
),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
),
|
||||
hintText: 'URL',
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
style: const TextStyle(fontSize: 12),
|
||||
readOnly: true,
|
||||
),
|
||||
),
|
||||
const Gap(4),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.copy, size: 20),
|
||||
padding: const EdgeInsets.all(4),
|
||||
constraints: const BoxConstraints(),
|
||||
onPressed: () {
|
||||
if (currentUrl != null) {
|
||||
Clipboard.setData(ClipboardData(text: currentUrl!));
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text('copyToClipboard').tr(),
|
||||
duration: const Duration(seconds: 1),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
Reference in New Issue
Block a user