add auth code flow support
This commit is contained in:
parent
fe59f7679c
commit
790caf1d38
9 changed files with 112 additions and 31 deletions
2
gomuks
2
gomuks
|
|
@ -1 +1 @@
|
||||||
Subproject commit d7506029a62299515356f72061d36ae35e9c9590
|
Subproject commit 3c759ba03a5235deb82cb1559ebc42c36ee0e874
|
||||||
32
lib/controllers/auth_url.dart
Normal file
32
lib/controllers/auth_url.dart
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
import "package:flutter_riverpod/flutter_riverpod.dart";
|
||||||
|
import "package:nexus/controllers/client.dart";
|
||||||
|
import "package:nexus/controllers/client_id.dart";
|
||||||
|
|
||||||
|
class AuthUrlController extends AsyncNotifier<Uri> {
|
||||||
|
static const prefString = "auth_url";
|
||||||
|
|
||||||
|
final Uri homeserver;
|
||||||
|
AuthUrlController(this.homeserver);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Uri> build() async {
|
||||||
|
final fetched = await ref
|
||||||
|
.watch(ClientController.provider.notifier)
|
||||||
|
.getAuthUrl(
|
||||||
|
.new(
|
||||||
|
homeserverUrl: homeserver,
|
||||||
|
redirectUri: Uri.https("nexus.federated.nexus"),
|
||||||
|
responseMode: .query,
|
||||||
|
scopes: .new([.clientApi]),
|
||||||
|
clientId: await ref.watch(
|
||||||
|
ClientIdController.provider(homeserver).future,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return fetched.url;
|
||||||
|
}
|
||||||
|
|
||||||
|
static final provider = AsyncNotifierProvider.autoDispose
|
||||||
|
.family<AuthUrlController, Uri, Uri>(AuthUrlController.new);
|
||||||
|
}
|
||||||
|
|
@ -15,12 +15,14 @@ import "package:nexus/controllers/top_level_spaces.dart";
|
||||||
import "package:nexus/helpers/extensions/gomuks_buffer.dart";
|
import "package:nexus/helpers/extensions/gomuks_buffer.dart";
|
||||||
import "package:nexus/main.dart";
|
import "package:nexus/main.dart";
|
||||||
import "package:nexus/models/event.dart";
|
import "package:nexus/models/event.dart";
|
||||||
|
import "package:nexus/models/oauth_auth_code_response.dart";
|
||||||
import "package:nexus/models/paginate.dart";
|
import "package:nexus/models/paginate.dart";
|
||||||
import "package:nexus/models/requests/get_event.dart";
|
import "package:nexus/models/requests/get_event.dart";
|
||||||
import "package:nexus/models/requests/get_related_events.dart";
|
import "package:nexus/models/requests/get_related_events.dart";
|
||||||
import "package:nexus/models/requests/get_room_state.dart";
|
import "package:nexus/models/requests/get_room_state.dart";
|
||||||
import "package:nexus/models/requests/join_room.dart";
|
import "package:nexus/models/requests/join_room.dart";
|
||||||
import "package:nexus/models/profile.dart";
|
import "package:nexus/models/profile.dart";
|
||||||
|
import "package:nexus/models/requests/oauth/get_auth_url.dart";
|
||||||
import "package:nexus/models/requests/oauth/register_client.dart";
|
import "package:nexus/models/requests/oauth/register_client.dart";
|
||||||
import "package:nexus/models/requests/paginate.dart";
|
import "package:nexus/models/requests/paginate.dart";
|
||||||
import "package:nexus/models/requests/redact_event.dart";
|
import "package:nexus/models/requests/redact_event.dart";
|
||||||
|
|
@ -281,6 +283,11 @@ class ClientController extends AsyncNotifier<int> {
|
||||||
request.toJson(),
|
request.toJson(),
|
||||||
))["client_id"];
|
))["client_id"];
|
||||||
|
|
||||||
|
Future<OAuthAuthCodeResponse> getAuthUrl(OAuthGetAuthUrl request) async =>
|
||||||
|
.fromJson(
|
||||||
|
await _sendCommand("oauth_get_authorization_url", request.toJson()),
|
||||||
|
);
|
||||||
|
|
||||||
Future<Uri?> discoverHomeserver(Uri homeserver) async {
|
Future<Uri?> discoverHomeserver(Uri homeserver) async {
|
||||||
try {
|
try {
|
||||||
final response = await _sendCommand("discover_homeserver", {
|
final response = await _sendCommand("discover_homeserver", {
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,9 @@ import "package:nexus/controllers/shared_prefs.dart";
|
||||||
class ClientIdController extends AsyncNotifier<String> {
|
class ClientIdController extends AsyncNotifier<String> {
|
||||||
static const prefString = "client_id";
|
static const prefString = "client_id";
|
||||||
|
|
||||||
|
final Uri homeserver;
|
||||||
|
ClientIdController(this.homeserver);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<String> build() async {
|
Future<String> build() async {
|
||||||
final prefs = await ref.watch(SharedPrefsController.provider.future);
|
final prefs = await ref.watch(SharedPrefsController.provider.future);
|
||||||
|
|
@ -16,8 +19,17 @@ class ClientIdController extends AsyncNotifier<String> {
|
||||||
.watch(ClientController.provider.notifier)
|
.watch(ClientController.provider.notifier)
|
||||||
.registerClient(
|
.registerClient(
|
||||||
.new(
|
.new(
|
||||||
homeserverUrl: Uri.https("matrix.federated.nexus"),
|
clientName: "Nexus",
|
||||||
|
applicationType: .native,
|
||||||
|
grantTypes: .new([.authorizationCode, .refreshToken]),
|
||||||
|
responseTypes: .new([.code]),
|
||||||
|
logoUri: Uri.https(
|
||||||
|
"nexus.federated.nexus",
|
||||||
|
"raw/branch/main/assets/mobile.svg",
|
||||||
|
),
|
||||||
|
homeserverUrl: homeserver,
|
||||||
clientUri: Uri.https("nexus.federated.nexus"),
|
clientUri: Uri.https("nexus.federated.nexus"),
|
||||||
|
redirectUris: .new([.https("nexus.federated.nexus")]),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -26,8 +38,6 @@ class ClientIdController extends AsyncNotifier<String> {
|
||||||
return fetched;
|
return fetched;
|
||||||
}
|
}
|
||||||
|
|
||||||
static final provider =
|
static final provider = AsyncNotifierProvider.autoDispose
|
||||||
AsyncNotifierProvider.autoDispose<ClientIdController, String>(
|
.family<ClientIdController, String, Uri>(ClientIdController.new);
|
||||||
ClientIdController.new,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
15
lib/models/oauth_auth_code_response.dart
Normal file
15
lib/models/oauth_auth_code_response.dart
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
import "package:freezed_annotation/freezed_annotation.dart";
|
||||||
|
part "oauth_auth_code_response.freezed.dart";
|
||||||
|
part "oauth_auth_code_response.g.dart";
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
abstract class OAuthAuthCodeResponse with _$OAuthAuthCodeResponse {
|
||||||
|
const factory OAuthAuthCodeResponse({
|
||||||
|
required String state,
|
||||||
|
required String codeVerifier,
|
||||||
|
required Uri url,
|
||||||
|
}) = _OAuthAuthCodeResponse;
|
||||||
|
|
||||||
|
factory OAuthAuthCodeResponse.fromJson(Map<String, Object?> json) =>
|
||||||
|
_$OAuthAuthCodeResponseFromJson(json);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
import "package:fast_immutable_collections/fast_immutable_collections.dart";
|
||||||
|
import "package:freezed_annotation/freezed_annotation.dart";
|
||||||
|
part "get_auth_url.freezed.dart";
|
||||||
|
part "get_auth_url.g.dart";
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
abstract class OAuthGetAuthUrl with _$OAuthGetAuthUrl {
|
||||||
|
const factory OAuthGetAuthUrl({
|
||||||
|
required ResponseMode responseMode,
|
||||||
|
required Uri homeserverUrl,
|
||||||
|
required Uri redirectUri,
|
||||||
|
required IList<Scope> scopes,
|
||||||
|
required String clientId,
|
||||||
|
String? userIdHint,
|
||||||
|
}) = _OAuthGetAuthUrl;
|
||||||
|
|
||||||
|
factory OAuthGetAuthUrl.fromJson(Map<String, Object?> json) =>
|
||||||
|
_$OAuthGetAuthUrlFromJson(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Scope {
|
||||||
|
openid,
|
||||||
|
email,
|
||||||
|
@JsonValue("urn:matrix:client:api:*")
|
||||||
|
clientApi,
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonEnum(fieldRename: .snake)
|
||||||
|
enum ResponseMode { query, fragment }
|
||||||
|
|
@ -7,7 +7,7 @@ part "register_client.g.dart";
|
||||||
abstract class OAuthRegisterClientRequest with _$OAuthRegisterClientRequest {
|
abstract class OAuthRegisterClientRequest with _$OAuthRegisterClientRequest {
|
||||||
const factory OAuthRegisterClientRequest({
|
const factory OAuthRegisterClientRequest({
|
||||||
required Uri homeserverUrl,
|
required Uri homeserverUrl,
|
||||||
ApplicationType? applicationType,
|
@Default(ApplicationType.web) ApplicationType applicationType,
|
||||||
String? clientName,
|
String? clientName,
|
||||||
required Uri clientUri,
|
required Uri clientUri,
|
||||||
Uri? logoUri,
|
Uri? logoUri,
|
||||||
|
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
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";
|
|
||||||
|
|
||||||
class LoginPage extends HookConsumerWidget {
|
|
||||||
final Uri homeserver;
|
|
||||||
const LoginPage({super.key, required this.homeserver});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context, WidgetRef ref) => Scaffold(
|
|
||||||
body: ref
|
|
||||||
.watch(ClientIdController.provider)
|
|
||||||
.betterWhen(data: (value) => Text(value)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -2,10 +2,11 @@ import "package:flutter/material.dart";
|
||||||
import "package:flutter_hooks/flutter_hooks.dart";
|
import "package:flutter_hooks/flutter_hooks.dart";
|
||||||
import "package:flutter_svg/flutter_svg.dart";
|
import "package:flutter_svg/flutter_svg.dart";
|
||||||
import "package:hooks_riverpod/hooks_riverpod.dart";
|
import "package:hooks_riverpod/hooks_riverpod.dart";
|
||||||
|
import "package:nexus/controllers/auth_url.dart";
|
||||||
import "package:nexus/controllers/client.dart";
|
import "package:nexus/controllers/client.dart";
|
||||||
import "package:nexus/helpers/launch_helper.dart";
|
import "package:nexus/helpers/launch_helper.dart";
|
||||||
|
import "package:nexus/main.dart";
|
||||||
import "package:nexus/models/homeserver.dart";
|
import "package:nexus/models/homeserver.dart";
|
||||||
import "package:nexus/pages/login.dart";
|
|
||||||
import "package:nexus/widgets/appbar.dart";
|
import "package:nexus/widgets/appbar.dart";
|
||||||
import "package:nexus/widgets/divider_text.dart";
|
import "package:nexus/widgets/divider_text.dart";
|
||||||
|
|
||||||
|
|
@ -32,7 +33,7 @@ class SelectServerPage extends HookConsumerWidget {
|
||||||
final newUrl = newHomeserver == null
|
final newUrl = newHomeserver == null
|
||||||
? null
|
? null
|
||||||
: await ref
|
: await ref
|
||||||
.watch(ClientController.provider.notifier)
|
.read(ClientController.provider.notifier)
|
||||||
.discoverHomeserver(newHomeserver);
|
.discoverHomeserver(newHomeserver);
|
||||||
|
|
||||||
if (context.mounted) {
|
if (context.mounted) {
|
||||||
|
|
@ -47,11 +48,18 @@ class SelectServerPage extends HookConsumerWidget {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
await Navigator.of(context).push(
|
final authUrl = await ref.read(
|
||||||
MaterialPageRoute(builder: (_) => LoginPage(homeserver: newUrl)),
|
AuthUrlController.provider(newUrl).future,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
print(authUrl);
|
||||||
|
// await Navigator.of(context).push(
|
||||||
|
// MaterialPageRoute(builder: (_) => LoginPage(homeserver: newUrl)),
|
||||||
|
// );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch (error, stackTrace) {
|
||||||
|
showError(error, stackTrace);
|
||||||
} finally {
|
} finally {
|
||||||
isLoading.value = false;
|
isLoading.value = false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue