forked from Nexus/nexus
OAuth Support (#53)
Rips out the old auth method, adds OAuth support. Fixes #51. Tested Platforms: - [x] Linux - [x] Flatpak - [x] Native (Nix) - [x] Android - [ ] Windows - [ ] MacOS Reviewed-on: Nexus/nexus#53
This commit is contained in:
parent
5df975f607
commit
7d106ad3a2
86 changed files with 558 additions and 1344 deletions
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";
|
||||
import "package:nexus/models/oauth_auth_code_response.dart";
|
||||
import "package:nexus/models/requests/oauth/get_auth_url.dart";
|
||||
|
||||
class AuthUrlController extends AsyncNotifier<OAuthAuthCodeResponse> {
|
||||
final Uri homeserver;
|
||||
AuthUrlController(this.homeserver);
|
||||
|
||||
@override
|
||||
Future<OAuthAuthCodeResponse> build() async => ref
|
||||
.watch(ClientController.provider.notifier)
|
||||
.getAuthUrl(
|
||||
.new(
|
||||
homeserverUrl: homeserver,
|
||||
redirectUri: .new(scheme: "nexus.federated.nexus", path: "/"),
|
||||
responseMode: .query,
|
||||
scopes: .new([Scope.clientApi, Scope.device]),
|
||||
clientId: await ref.watch(
|
||||
ClientIdController.provider(homeserver).future,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
static final provider =
|
||||
AsyncNotifierProvider.family<
|
||||
AuthUrlController,
|
||||
OAuthAuthCodeResponse,
|
||||
Uri
|
||||
>(AuthUrlController.new);
|
||||
}
|
||||
|
|
@ -15,13 +15,16 @@ import "package:nexus/controllers/top_level_spaces.dart";
|
|||
import "package:nexus/helpers/extensions/gomuks_buffer.dart";
|
||||
import "package:nexus/main.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/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/exchange_token.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/paginate.dart";
|
||||
import "package:nexus/models/requests/redact_event.dart";
|
||||
import "package:nexus/models/requests/report.dart";
|
||||
|
|
@ -263,6 +266,8 @@ class ClientController extends AsyncNotifier<int> {
|
|||
Future<void> setMembership(SetMembershipRequest request) =>
|
||||
_sendCommand("set_membership", request.toJson());
|
||||
|
||||
Future<void> logout() => _sendCommand("logout");
|
||||
|
||||
Future<void> markRead(Room room) async {
|
||||
final eventRowId = room.timeline[room.timeline.keys.reduce(max)];
|
||||
final event = eventRowId == null ? null : room.events[eventRowId];
|
||||
|
|
@ -275,14 +280,19 @@ 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<OAuthAuthCodeResponse> getAuthUrl(OAuthGetAuthUrl request) async =>
|
||||
.fromJson(
|
||||
await _sendCommand("oauth_get_authorization_url", request.toJson()),
|
||||
);
|
||||
|
||||
Future<void> exchangeToken(OAuthExchangeTokenRequest request) async =>
|
||||
await _sendCommand("oauth_exchange_token", request.toJson());
|
||||
|
||||
Future<Uri?> discoverHomeserver(Uri homeserver) async {
|
||||
try {
|
||||
|
|
|
|||
33
lib/controllers/client_id.dart
Normal file
33
lib/controllers/client_id.dart
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
import "package:flutter_riverpod/flutter_riverpod.dart";
|
||||
import "package:nexus/controllers/client.dart";
|
||||
|
||||
class ClientIdController extends AsyncNotifier<String> {
|
||||
final Uri homeserver;
|
||||
ClientIdController(this.homeserver);
|
||||
|
||||
@override
|
||||
Future<String> build() => ref
|
||||
.watch(ClientController.provider.notifier)
|
||||
.registerClient(
|
||||
.new(
|
||||
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"),
|
||||
redirectUris: .new([
|
||||
.new(scheme: "nexus.federated.nexus", path: "/"),
|
||||
]),
|
||||
),
|
||||
);
|
||||
|
||||
static final provider =
|
||||
AsyncNotifierProvider.family<ClientIdController, String, Uri>(
|
||||
ClientIdController.new,
|
||||
);
|
||||
}
|
||||
|
|
@ -3,6 +3,8 @@ import "package:fast_immutable_collections/fast_immutable_collections.dart";
|
|||
import "package:flutter/material.dart";
|
||||
import "package:flutter_riverpod/flutter_riverpod.dart";
|
||||
import "package:intl/intl.dart";
|
||||
import "package:m3e_buttons/m3e_buttons.dart";
|
||||
import "package:nexus/controllers/client.dart";
|
||||
import "package:nexus/controllers/settings.dart";
|
||||
import "package:nexus/models/settings_category.dart";
|
||||
import "package:nexus/main.dart";
|
||||
|
|
@ -87,11 +89,46 @@ class SettingsSectionsController
|
|||
]),
|
||||
),
|
||||
]),
|
||||
"Account": .new([
|
||||
.new(title: "Profile", icon: Icons.person, settings: .new([])),
|
||||
.new(
|
||||
title: "Other",
|
||||
icon: Icons.key,
|
||||
settings: .new([
|
||||
.new(
|
||||
title: "Log Out",
|
||||
description:
|
||||
"Log out of your account, returning you to the login page.",
|
||||
builder: (title, description, icon) => Builder(
|
||||
builder: (context) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
return M3EButton.icon(
|
||||
onPressed: () async {
|
||||
await ref
|
||||
.watch(ClientController.provider.notifier)
|
||||
.logout();
|
||||
if (context.mounted) Navigator.of(context).pop();
|
||||
},
|
||||
label: Text(title),
|
||||
icon: Icon(icon),
|
||||
tooltip: description,
|
||||
decoration: .styleFrom(
|
||||
backgroundColor: colorScheme.errorContainer,
|
||||
foregroundColor: colorScheme.onErrorContainer,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
icon: Icons.logout,
|
||||
),
|
||||
]),
|
||||
),
|
||||
]),
|
||||
});
|
||||
}
|
||||
|
||||
static final provider =
|
||||
AsyncNotifierProvider<
|
||||
AsyncNotifierProvider.autoDispose<
|
||||
SettingsSectionsController,
|
||||
IMap<String, IList<SettingsCategory>>
|
||||
>(SettingsSectionsController.new);
|
||||
|
|
|
|||
Loading…
Reference in a new issue