🐛 Fixes some bugs in OIDC

This commit is contained in:
2025-06-17 00:18:41 +08:00
parent 4dbee27718
commit 9b67d58ee4
15 changed files with 457 additions and 186 deletions

View File

@ -11,8 +11,6 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_otp_text_field/flutter_otp_text_field.dart';
import 'package:flutter_svg/svg.dart';
import 'package:google_sign_in/google_sign_in.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:gap/gap.dart';
import 'package:island/models/auth.dart';
@ -20,6 +18,7 @@ import 'package:island/pods/config.dart';
import 'package:island/pods/network.dart';
import 'package:island/pods/userinfo.dart';
import 'package:island/pods/websocket.dart';
import 'package:island/screens/account/me/settings_connections.dart';
import 'package:island/services/notify.dart';
import 'package:island/services/udid.dart';
import 'package:island/widgets/alert.dart';
@ -271,7 +270,6 @@ class _LoginCheckScreen extends HookConsumerWidget {
],
decoration: InputDecoration(
isDense: true,
border: const OutlineInputBorder(),
labelText: 'password'.tr(),
),
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
@ -292,14 +290,12 @@ class _LoginCheckScreen extends HookConsumerWidget {
textStyle: Theme.of(context).textTheme.titleLarge!,
),
const Gap(12),
Card(
child: ListTile(
leading: Icon(
kFactorTypes[factor!.type]?.$3 ?? Symbols.question_mark,
),
title: Text(kFactorTypes[factor!.type]?.$1 ?? 'unknown').tr(),
subtitle: Text(kFactorTypes[factor!.type]?.$2 ?? 'unknown').tr(),
ListTile(
leading: Icon(
kFactorTypes[factor!.type]?.$3 ?? Symbols.question_mark,
),
title: Text(kFactorTypes[factor!.type]?.$1 ?? 'unknown').tr(),
subtitle: Text(kFactorTypes[factor!.type]?.$2 ?? 'unknown').tr(),
),
const Gap(12),
Row(
@ -604,25 +600,6 @@ class _LoginLookupScreen extends HookConsumerWidget {
}
}
Future<void> withGoogle() async {
// TODO This crashes for no reason
GoogleSignIn gsi = GoogleSignIn(
clientId:
kIsWeb
? '961776991058-963m1qin2vtp8fv693b5fdrab5hmpl89.apps.googleusercontent.com'
: (Platform.isIOS || Platform.isMacOS)
? '961776991058-stt7et4qvn3cpscl4r61gl1hnlatqkig.apps.googleusercontent.com'
: '961776991058-r4iv9qoio57ul7utbfpgfrda2etvtch8.apps.googleusercontent.com',
scopes: ['openid', 'https://www.googleapis.com/auth/userinfo.email'],
);
try {
var ga = await gsi.signIn();
} catch (err) {
showErrorAlert(err);
}
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@ -658,28 +635,13 @@ class _LoginLookupScreen extends HookConsumerWidget {
Text("loginOr").tr().fontSize(11).opacity(0.85),
const Gap(8),
Spacer(),
// IconButton.filledTonal(
// // onPressed: withGoogle,
// padding: EdgeInsets.zero,
// icon: SvgPicture.asset(
// 'assets/images/oidc/google.svg',
// width: 16,
// height: 16,
// ),
// tooltip: 'Google',
// ),
IconButton.filledTonal(
onPressed: withApple,
padding: EdgeInsets.zero,
icon: SvgPicture.asset(
'assets/images/oidc/apple.svg',
width: 16,
height: 16,
color:
MediaQuery.of(context).platformBrightness ==
Brightness.light
? Colors.black54
: Colors.white,
icon: getProviderIcon(
"apple",
size: 16,
color: Theme.of(context).colorScheme.onPrimaryContainer,
),
tooltip: 'Apple Account',
),

View File

@ -1,36 +1,55 @@
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:island/pods/config.dart';
import 'package:island/pods/network.dart';
import 'package:island/widgets/app_scaffold.dart';
class OidcScreen extends ConsumerStatefulWidget {
final String provider;
final String? title;
const OidcScreen({super.key, required this.provider});
const OidcScreen({super.key, required this.provider, this.title});
@override
ConsumerState<OidcScreen> createState() => _OIDCScreenState();
ConsumerState<OidcScreen> createState() => _OidcScreenState();
}
class _OIDCScreenState extends ConsumerState<OidcScreen> {
InAppWebViewController? _webViewController;
class _OidcScreenState extends ConsumerState<OidcScreen> {
String? authToken;
@override
Widget build(BuildContext context) {
final serverUrl = ref.watch(serverUrlProvider);
final token = ref.watch(tokenProvider);
return AppScaffold(
appBar: AppBar(title: Text('login').tr()),
appBar: AppBar(
title: widget.title != null ? Text(widget.title!) : Text('login').tr(),
),
body: 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}'),
url: WebUri(
(token?.token.isNotEmpty ?? false)
? '$serverUrl/auth/login/${widget.provider}?tk=${token!.token}'
: '$serverUrl/auth/login/${widget.provider}',
),
),
onWebViewCreated: (controller) {
_webViewController = controller;
// Register a handler to receive the token from JavaScript
controller.addJavaScriptHandler(
handlerName: 'tokenHandler',

View File

@ -4,20 +4,22 @@ import 'dart:ui_web' as ui;
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:island/pods/config.dart';
import 'package:island/pods/network.dart';
import 'package:island/widgets/app_scaffold.dart';
import 'package:web/web.dart' as web;
import 'package:flutter/material.dart';
class OidcScreen extends ConsumerStatefulWidget {
final String provider;
final String? title;
const OidcScreen({super.key, required this.provider});
const OidcScreen({super.key, required this.provider, this.title});
@override
ConsumerState<OidcScreen> createState() => _OIDCScreenState();
ConsumerState<OidcScreen> createState() => _OidcScreenState();
}
class _OIDCScreenState extends ConsumerState<OidcScreen> {
class _OidcScreenState extends ConsumerState<OidcScreen> {
bool _isInitialized = false;
final String _viewType = 'oidc-iframe';
@ -29,15 +31,19 @@ class _OIDCScreenState extends ConsumerState<OidcScreen> {
if (message.startsWith("token=")) {
String token = message.replaceFirst("token=", "");
// Return the token and close the screen
if (context.mounted) Navigator.pop(context, token);
if (mounted) Navigator.pop(context, token);
}
}
});
// Create the iframe for the OIDC login
final token = ref.watch(tokenProvider);
final iframe =
web.HTMLIFrameElement()
..src = '$serverUrl/auth/login/${widget.provider}'
..src =
(token?.token.isNotEmpty ?? false)
? '$serverUrl/auth/login/${widget.provider}?tk=${token!.token}'
: '$serverUrl/auth/login/${widget.provider}'
..style.border = 'none'
..width = '100%'
..height = '100%';
@ -68,7 +74,9 @@ class _OIDCScreenState extends ConsumerState<OidcScreen> {
@override
Widget build(BuildContext context) {
return AppScaffold(
appBar: AppBar(title: Text('login').tr()),
appBar: AppBar(
title: widget.title != null ? Text(widget.title!) : Text('login').tr(),
),
body:
_isInitialized
? HtmlElementView(viewType: _viewType)