add client ID fetching support, rip out old logins
This commit is contained in:
parent
7992f0e815
commit
fe59f7679c
9 changed files with 97 additions and 114 deletions
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
|
|
@ -10,6 +10,7 @@
|
|||
"msgtype",
|
||||
"muks",
|
||||
"prefs",
|
||||
"unban"
|
||||
"unban",
|
||||
"unredact"
|
||||
]
|
||||
}
|
||||
|
|
|
|||
2
gomuks
2
gomuks
|
|
@ -1 +1 @@
|
|||
Subproject commit 23638a8d2b5ad7ed9f72a0ec39f56cac119c45fb
|
||||
Subproject commit d7506029a62299515356f72061d36ae35e9c9590
|
||||
|
|
@ -20,8 +20,8 @@ import "package:nexus/models/requests/get_event.dart";
|
|||
import "package:nexus/models/requests/get_related_events.dart";
|
||||
import "package:nexus/models/requests/get_room_state.dart";
|
||||
import "package:nexus/models/requests/join_room.dart";
|
||||
import "package:nexus/models/requests/login.dart";
|
||||
import "package:nexus/models/profile.dart";
|
||||
import "package:nexus/models/requests/oauth/register_client.dart";
|
||||
import "package:nexus/models/requests/paginate.dart";
|
||||
import "package:nexus/models/requests/redact_event.dart";
|
||||
import "package:nexus/models/requests/report.dart";
|
||||
|
|
@ -275,14 +275,11 @@ class ClientController extends AsyncNotifier<int> {
|
|||
});
|
||||
}
|
||||
|
||||
Future<String?> login(LoginRequest login) async {
|
||||
try {
|
||||
await _sendCommand("login", login.toJson());
|
||||
return null;
|
||||
} catch (error) {
|
||||
return error.toString();
|
||||
}
|
||||
}
|
||||
Future<String> registerClient(OAuthRegisterClientRequest request) async =>
|
||||
(await _sendCommand(
|
||||
"oauth_register_client",
|
||||
request.toJson(),
|
||||
))["client_id"];
|
||||
|
||||
Future<Uri?> discoverHomeserver(Uri homeserver) async {
|
||||
try {
|
||||
|
|
|
|||
33
lib/controllers/client_id_controller.dart
Normal file
33
lib/controllers/client_id_controller.dart
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
import "package:flutter_riverpod/flutter_riverpod.dart";
|
||||
import "package:nexus/controllers/client.dart";
|
||||
import "package:nexus/controllers/shared_prefs.dart";
|
||||
|
||||
class ClientIdController extends AsyncNotifier<String> {
|
||||
static const prefString = "client_id";
|
||||
|
||||
@override
|
||||
Future<String> build() async {
|
||||
final prefs = await ref.watch(SharedPrefsController.provider.future);
|
||||
final fromPref = prefs.getString(prefString);
|
||||
|
||||
if (fromPref != null) return fromPref;
|
||||
|
||||
final fetched = await ref
|
||||
.watch(ClientController.provider.notifier)
|
||||
.registerClient(
|
||||
.new(
|
||||
homeserverUrl: Uri.https("matrix.federated.nexus"),
|
||||
clientUri: Uri.https("nexus.federated.nexus"),
|
||||
),
|
||||
);
|
||||
|
||||
await prefs.setString(prefString, fetched);
|
||||
|
||||
return fetched;
|
||||
}
|
||||
|
||||
static final provider =
|
||||
AsyncNotifierProvider.autoDispose<ClientIdController, String>(
|
||||
ClientIdController.new,
|
||||
);
|
||||
}
|
||||
|
|
@ -2,9 +2,8 @@ import "package:freezed_annotation/freezed_annotation.dart";
|
|||
part "get_event.freezed.dart";
|
||||
part "get_event.g.dart";
|
||||
|
||||
@Freezed()
|
||||
@freezed
|
||||
abstract class GetEventRequest with _$GetEventRequest {
|
||||
const GetEventRequest._();
|
||||
const factory GetEventRequest({
|
||||
required String roomId,
|
||||
required String eventId,
|
||||
|
|
|
|||
|
|
@ -1,15 +0,0 @@
|
|||
import "package:freezed_annotation/freezed_annotation.dart";
|
||||
part "login.freezed.dart";
|
||||
part "login.g.dart";
|
||||
|
||||
@freezed
|
||||
abstract class LoginRequest with _$LoginRequest {
|
||||
const factory LoginRequest({
|
||||
required String username,
|
||||
required String password,
|
||||
required String homeserverUrl,
|
||||
}) = _LoginRequest;
|
||||
|
||||
factory LoginRequest.fromJson(Map<String, Object?> json) =>
|
||||
_$LoginRequestFromJson(json);
|
||||
}
|
||||
0
lib/models/requests/oauth/get_auth_url.dart
Normal file
0
lib/models/requests/oauth/get_auth_url.dart
Normal file
47
lib/models/requests/oauth/register_client.dart
Normal file
47
lib/models/requests/oauth/register_client.dart
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
import "package:fast_immutable_collections/fast_immutable_collections.dart";
|
||||
import "package:freezed_annotation/freezed_annotation.dart";
|
||||
part "register_client.freezed.dart";
|
||||
part "register_client.g.dart";
|
||||
|
||||
@freezed
|
||||
abstract class OAuthRegisterClientRequest with _$OAuthRegisterClientRequest {
|
||||
const factory OAuthRegisterClientRequest({
|
||||
required Uri homeserverUrl,
|
||||
ApplicationType? applicationType,
|
||||
String? clientName,
|
||||
required Uri clientUri,
|
||||
Uri? logoUri,
|
||||
Uri? policyUri,
|
||||
Uri? tosUri,
|
||||
IList<GrantType>? grantTypes,
|
||||
IList<Uri>? redirectUris,
|
||||
IList<ResponseType>? responseTypes,
|
||||
AuthMethod? authMethod,
|
||||
}) = _OAuthRegisterClientRequest;
|
||||
|
||||
factory OAuthRegisterClientRequest.fromJson(Map<String, Object?> json) =>
|
||||
_$OAuthRegisterClientRequestFromJson(json);
|
||||
}
|
||||
|
||||
enum ApplicationType { native, web }
|
||||
|
||||
@JsonEnum(fieldRename: .snake)
|
||||
enum ResponseType { code, idToken }
|
||||
|
||||
@JsonEnum(fieldRename: .snake)
|
||||
enum AuthMethod {
|
||||
clientSecretPost,
|
||||
clientSecretBasic,
|
||||
clientSecretJwt,
|
||||
privateKeyJwt,
|
||||
none,
|
||||
}
|
||||
|
||||
@JsonEnum(fieldRename: .snake)
|
||||
enum GrantType {
|
||||
authorizationCode,
|
||||
refreshToken,
|
||||
clientCredentials,
|
||||
@JsonValue("urn:ietf:params:oauth:grant-type:device_code")
|
||||
deviceCode,
|
||||
}
|
||||
|
|
@ -2,6 +2,8 @@ import "package:flutter/material.dart";
|
|||
import "package:flutter_hooks/flutter_hooks.dart";
|
||||
import "package:hooks_riverpod/hooks_riverpod.dart";
|
||||
import "package:nexus/controllers/client.dart";
|
||||
import "package:nexus/controllers/client_id_controller.dart";
|
||||
import "package:nexus/helpers/extensions/better_when.dart";
|
||||
import "package:nexus/widgets/appbar.dart";
|
||||
import "package:nexus/helpers/required_validator_helper.dart";
|
||||
|
||||
|
|
@ -10,90 +12,9 @@ class LoginPage extends HookConsumerWidget {
|
|||
const LoginPage({super.key, required this.homeserver});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final client = ref.watch(ClientController.provider.notifier);
|
||||
|
||||
final isLoading = useState(false);
|
||||
final username = useTextEditingController();
|
||||
final password = useTextEditingController();
|
||||
|
||||
final inputError = useState<String?>(null);
|
||||
final formKey = useRef(GlobalKey<FormState>());
|
||||
|
||||
Future<void> tryLogin() async {
|
||||
isLoading.value = true;
|
||||
|
||||
try {
|
||||
if (formKey.value.currentState?.validate() != true) return;
|
||||
|
||||
final error = await client.login(
|
||||
.new(
|
||||
username: username.text,
|
||||
password: password.text,
|
||||
homeserverUrl: homeserver.origin,
|
||||
),
|
||||
Widget build(BuildContext context, WidgetRef ref) => Scaffold(
|
||||
body: ref
|
||||
.watch(ClientIdController.provider)
|
||||
.betterWhen(data: (value) => Text(value)),
|
||||
);
|
||||
|
||||
if (error != null) {
|
||||
inputError.value = error;
|
||||
isLoading.value = false;
|
||||
} else {
|
||||
if (context.mounted) Navigator.of(context).pop();
|
||||
}
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
return Scaffold(
|
||||
appBar: Appbar(
|
||||
leading: IconButton(
|
||||
icon: Icon(Icons.arrow_back),
|
||||
onPressed: Navigator.of(context).pop,
|
||||
),
|
||||
),
|
||||
body: AlertDialog(
|
||||
title: Text("Login to ${homeserver.host}"),
|
||||
content: Form(
|
||||
key: formKey.value,
|
||||
child: Column(
|
||||
mainAxisSize: .min,
|
||||
crossAxisAlignment: .start,
|
||||
children: [
|
||||
TextFormField(
|
||||
autofocus: true,
|
||||
textInputAction: .next,
|
||||
autovalidateMode: .onUserInteraction,
|
||||
validator: requiredValidator,
|
||||
decoration: .new(label: Text("Username")),
|
||||
controller: username,
|
||||
),
|
||||
SizedBox(height: 12),
|
||||
TextFormField(
|
||||
textInputAction: .done,
|
||||
decoration: .new(
|
||||
label: Text("Password"),
|
||||
errorText: inputError.value,
|
||||
errorMaxLines: 5,
|
||||
),
|
||||
autovalidateMode: .onUserInteraction,
|
||||
validator: requiredValidator,
|
||||
controller: password,
|
||||
obscureText: true,
|
||||
onFieldSubmitted: (_) => tryLogin(),
|
||||
// Don't defocus on submit
|
||||
onEditingComplete: () {},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: isLoading.value ? null : tryLogin,
|
||||
child: Text("Sign In"),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue