make identities less annoying
This commit is contained in:
parent
84d85d70e3
commit
a2b2b51517
|
@ -1,6 +1,7 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:loris/business_logic/account/account.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import '../../global.dart' as global;
|
||||
import 'package:shelf/shelf.dart';
|
||||
|
@ -28,17 +29,20 @@ class App {
|
|||
redirectUri: json["redirect_uri"].toString(),
|
||||
clientId: json["client_id"].toString(),
|
||||
clientSecret: json["client_secret"].toString());
|
||||
global.settings!.identities[global.settings!.activeIdentity]!.saveApp(app);
|
||||
_app = app;
|
||||
return app;
|
||||
}
|
||||
}
|
||||
|
||||
String _authCode = "";
|
||||
String _url = "";
|
||||
App? _app;
|
||||
|
||||
Response readAuthcode(Request request) {
|
||||
Map<String, String> params = request.url.queryParameters;
|
||||
if (params.containsKey("code") && params["code"] != null) {
|
||||
String code = params["code"].toString();
|
||||
global.settings!.identities[global.settings!.activeIdentity]!
|
||||
.saveAuthCode(code);
|
||||
_authCode = code;
|
||||
}
|
||||
return Response(308,
|
||||
headers: {"Content-Type": "text/html; charset=UTF-8"},
|
||||
|
@ -47,39 +51,72 @@ Response readAuthcode(Request request) {
|
|||
}
|
||||
|
||||
// returns status code
|
||||
Future<int> handleFullOauth() async {
|
||||
Future<int> handleFullOauth(String url) async {
|
||||
_url = url;
|
||||
try {
|
||||
http.Response response = await doOauthFlow();
|
||||
if (response.statusCode != 200) {
|
||||
return response.statusCode;
|
||||
}
|
||||
|
||||
await global.settings!.identities[global.settings!.activeIdentity]!
|
||||
.saveAuthCode("");
|
||||
_authCode = "";
|
||||
var handler = const Pipeline().addHandler(readAuthcode);
|
||||
var server = await shelf_io.serve(handler, 'localhost', 1312);
|
||||
await pollCode();
|
||||
server.close();
|
||||
await refreshToken();
|
||||
String token =
|
||||
await getToken(_authCode, _app!.clientId, _app!.clientSecret, _url);
|
||||
await saveIdentity(
|
||||
token,
|
||||
url,
|
||||
_app!,
|
||||
_authCode,
|
||||
);
|
||||
return 200;
|
||||
} catch (e) {
|
||||
return 400;
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> saveIdentity(
|
||||
String token,
|
||||
String baseurl,
|
||||
App app,
|
||||
String authCode,
|
||||
) async {
|
||||
Map<String, String> headers = {"Authorization": "Bearer $token"};
|
||||
headers.addAll(global.defaultHeaders);
|
||||
final response = await http.get(
|
||||
Uri.https(baseurl, "/api/v1/accounts/verify_credentials"),
|
||||
headers: headers,
|
||||
);
|
||||
if (response.statusCode == 200) {
|
||||
final account = AccountModel.fromJson(jsonDecode(response.body));
|
||||
await global.settings!.addNewIdentity("${account.acct}@$baseurl");
|
||||
await global.settings!.saveActiveIdentity("${account.acct}@$baseurl");
|
||||
await global.settings!.identities["${account.acct}@$baseurl"]!.saveApp(app);
|
||||
await global.settings!.identities["${account.acct}@$baseurl"]!
|
||||
.saveAuthCode(authCode);
|
||||
await global.settings!.identities["${account.acct}@$baseurl"]!
|
||||
.saveInstanceUrl(baseurl);
|
||||
await global.settings!.identities["${account.acct}@$baseurl"]!
|
||||
.saveToken(token);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Future<String> pollCode() async {
|
||||
String code = "";
|
||||
while (code == "") {
|
||||
await Future.delayed(const Duration(seconds: 3));
|
||||
code =
|
||||
global.settings!.identities[global.settings!.activeIdentity]!.authCode;
|
||||
code = _authCode;
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
||||
Future<http.Response> doOauthFlow() async {
|
||||
String url =
|
||||
global.settings!.identities[global.settings!.activeIdentity]!.instanceUrl;
|
||||
String url = _url;
|
||||
try {
|
||||
http.Response response = await registerApp(url);
|
||||
openBrowserForAuthCode(url, App.fromJson(jsonDecode(response.body)));
|
||||
|
@ -123,14 +160,12 @@ void openBrowserForAuthCode(String baseurl, App app) {
|
|||
launchUrl(url);
|
||||
}
|
||||
|
||||
Future<int> refreshToken() async {
|
||||
print("refreshing_token");
|
||||
final activeId = global.settings!.activeIdentity;
|
||||
final authCode = global.settings!.identities[activeId]!.authCode;
|
||||
final appId = global.settings!.identities[activeId]!.clientId;
|
||||
final clientSecret = global.settings!.identities[activeId]!.clientSecret;
|
||||
final baseurl = global.settings!.identities[activeId]!.instanceUrl;
|
||||
|
||||
Future<String> getToken(
|
||||
String authCode,
|
||||
String appId,
|
||||
String clientSecret,
|
||||
String baseurl,
|
||||
) async {
|
||||
Uri url = Uri.https(baseurl, "/oauth/token");
|
||||
final response = await http.post(
|
||||
url,
|
||||
|
@ -146,9 +181,7 @@ Future<int> refreshToken() async {
|
|||
);
|
||||
if (response.statusCode == 200) {
|
||||
final dec = jsonDecode(response.body);
|
||||
final accessToken = dec["access_token"]!;
|
||||
await global.settings!.identities[global.settings!.activeIdentity]!
|
||||
.saveToken(accessToken);
|
||||
return dec["access_token"]!;
|
||||
}
|
||||
return response.statusCode;
|
||||
return "";
|
||||
}
|
||||
|
|
|
@ -36,11 +36,11 @@ class AccountSettings {
|
|||
}
|
||||
|
||||
Future<void> delete() async {
|
||||
prefs.remove("$identity.$instanceUrlKey");
|
||||
prefs.remove("$identity.$authCodeKey");
|
||||
prefs.remove("$identity.$clientSecretKey");
|
||||
prefs.remove("$identity.$clientIdKey");
|
||||
prefs.remove("$identity.$tokenKey");
|
||||
await prefs.remove("$identity.$instanceUrlKey");
|
||||
await prefs.remove("$identity.$authCodeKey");
|
||||
await prefs.remove("$identity.$clientSecretKey");
|
||||
await prefs.remove("$identity.$clientIdKey");
|
||||
await prefs.remove("$identity.$tokenKey");
|
||||
}
|
||||
|
||||
Future<bool> saveInstanceUrl(String url) async {
|
||||
|
@ -56,8 +56,8 @@ class AccountSettings {
|
|||
Future<void> saveApp(oauth.App app) async {
|
||||
clientId = app.clientId;
|
||||
clientSecret = app.clientSecret;
|
||||
prefs.setString("$identity.$clientSecretKey", app.clientSecret);
|
||||
prefs.setString("$identity.$clientIdKey", app.clientId);
|
||||
await prefs.setString("$identity.$clientSecretKey", app.clientSecret);
|
||||
await prefs.setString("$identity.$clientIdKey", app.clientId);
|
||||
}
|
||||
|
||||
Future<bool> saveToken(String token) async {
|
||||
|
@ -126,9 +126,12 @@ class Settings {
|
|||
}
|
||||
|
||||
Future<bool> removeIdentity(String key) async {
|
||||
identities[key]!.delete();
|
||||
await identities[key]!.delete();
|
||||
identities.remove(key);
|
||||
return prefs.setStringList(
|
||||
if (identities.isNotEmpty) {
|
||||
activeIdentity = identities.keys.first;
|
||||
}
|
||||
return await prefs.setStringList(
|
||||
identitiesKey,
|
||||
identities.keys.toList(),
|
||||
);
|
||||
|
|
|
@ -20,7 +20,7 @@ void main() async {
|
|||
|
||||
// check if all information is available
|
||||
if (global.settings!.identities.isNotEmpty) {
|
||||
await oauth.refreshToken();
|
||||
// await oauth.refreshToken();
|
||||
}
|
||||
runApp(const Loris());
|
||||
}
|
||||
|
@ -36,23 +36,21 @@ class _LorisState extends State<Loris> {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
LocalJsonLocalization.delegate.directories = ['lib/i18n'];
|
||||
return Phoenix(
|
||||
child: MaterialApp(
|
||||
theme: theme,
|
||||
locale: activeLocale,
|
||||
supportedLocales: global.availableLocales,
|
||||
localizationsDelegates: [
|
||||
GlobalCupertinoLocalizations.delegate,
|
||||
GlobalWidgetsLocalizations.delegate,
|
||||
GlobalMaterialLocalizations.delegate,
|
||||
LocalJsonLocalization.delegate,
|
||||
],
|
||||
initialRoute: global.settings!.identities.isEmpty ? "/login" : "/",
|
||||
routes: {
|
||||
'/': (context) => const MainScaffold(),
|
||||
'/login': (context) => const Login(),
|
||||
},
|
||||
),
|
||||
return MaterialApp(
|
||||
theme: theme,
|
||||
locale: activeLocale,
|
||||
supportedLocales: global.availableLocales,
|
||||
localizationsDelegates: [
|
||||
GlobalCupertinoLocalizations.delegate,
|
||||
GlobalWidgetsLocalizations.delegate,
|
||||
GlobalMaterialLocalizations.delegate,
|
||||
LocalJsonLocalization.delegate,
|
||||
],
|
||||
initialRoute: global.settings!.identities.isEmpty ? "/login" : "/",
|
||||
routes: {
|
||||
'/': (context) => const MainScaffold(),
|
||||
'/login': (context) => const Login(),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ class LoginForm extends StatefulWidget {
|
|||
|
||||
class _LoginFormState extends State<LoginForm> {
|
||||
final formKey = GlobalKey<FormState>();
|
||||
String identity = "";
|
||||
String instanceUrl = "";
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Form(
|
||||
|
@ -45,10 +45,7 @@ class _LoginFormState extends State<LoginForm> {
|
|||
Text("greeting".i18n(), style: Theme.of(context).textTheme.headline1),
|
||||
TextFormField(
|
||||
onSaved: (value) async {
|
||||
await global.settings!.addNewIdentity(identity);
|
||||
await global.settings!.saveActiveIdentity(identity);
|
||||
await global.settings!.identities[identity]!
|
||||
.saveInstanceUrl(value!);
|
||||
instanceUrl = value ?? "";
|
||||
},
|
||||
decoration: InputDecoration(
|
||||
labelText: "instance-url".i18n(),
|
||||
|
@ -58,11 +55,6 @@ class _LoginFormState extends State<LoginForm> {
|
|||
),
|
||||
autofocus: true,
|
||||
),
|
||||
TextFormField(
|
||||
onChanged: ((value) {
|
||||
identity = value;
|
||||
}),
|
||||
),
|
||||
TextButton.icon(
|
||||
onPressed: () {
|
||||
bool isValid = formKey.currentState!.validate();
|
||||
|
@ -79,7 +71,7 @@ class _LoginFormState extends State<LoginForm> {
|
|||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const AuthPage(),
|
||||
builder: (context) => AuthPage(url: instanceUrl),
|
||||
));
|
||||
}
|
||||
},
|
||||
|
@ -95,8 +87,8 @@ class _LoginFormState extends State<LoginForm> {
|
|||
page that handles authenticating user
|
||||
*/
|
||||
class AuthPage extends StatefulWidget {
|
||||
const AuthPage({Key? key}) : super(key: key);
|
||||
|
||||
const AuthPage({required this.url, Key? key}) : super(key: key);
|
||||
final String url;
|
||||
@override
|
||||
State<AuthPage> createState() => _AuthPageState();
|
||||
}
|
||||
|
@ -111,7 +103,7 @@ class _AuthPageState extends State<AuthPage> {
|
|||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
FutureBuilder<int>(
|
||||
future: oauth.handleFullOauth(),
|
||||
future: oauth.handleFullOauth(widget.url),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasError) {
|
||||
return Text("login-failed-snackbar-text".i18n());
|
||||
|
@ -131,11 +123,14 @@ class _AuthPageState extends State<AuthPage> {
|
|||
}
|
||||
}
|
||||
return TextButton.icon(
|
||||
onPressed: () {
|
||||
Navigator.pushReplacementNamed(context, "/");
|
||||
},
|
||||
icon: const Icon(Icons.arrow_forward),
|
||||
label: Text("confirm".i18n()));
|
||||
onPressed: () async {
|
||||
await Navigator.pushReplacementNamed(context, "/");
|
||||
},
|
||||
icon: const Icon(Icons.arrow_forward),
|
||||
label: Text(
|
||||
"confirm".i18n(),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return const CircularProgressIndicator();
|
||||
}
|
||||
|
@ -145,11 +140,14 @@ class _AuthPageState extends State<AuthPage> {
|
|||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
TextButton.icon(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
icon: const Icon(Icons.arrow_back),
|
||||
label: Text("back-button".i18n())),
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
icon: const Icon(Icons.arrow_back),
|
||||
label: Text(
|
||||
"back-button".i18n(),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_phoenix/flutter_phoenix.dart';
|
||||
import 'package:localization/localization.dart';
|
||||
import '../../global.dart' as global;
|
||||
|
||||
|
@ -30,12 +27,9 @@ class LogoutButton extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
String url = global
|
||||
.settings!.identities[global.settings!.activeIdentity]!.instanceUrl;
|
||||
|
||||
return Row(
|
||||
children: [
|
||||
Text(url + ": " + identity),
|
||||
SelectableText(identity),
|
||||
TextButton.icon(
|
||||
onPressed: () {
|
||||
logout(context, identity);
|
||||
|
@ -47,7 +41,13 @@ class LogoutButton extends StatelessWidget {
|
|||
}
|
||||
}
|
||||
|
||||
void logout(context, String identity) async {
|
||||
global.settings!.removeIdentity(identity);
|
||||
Phoenix.rebirth(context);
|
||||
void addNewIdentity(context) {
|
||||
Navigator.of(context).pushReplacementNamed("/login");
|
||||
}
|
||||
|
||||
void logout(context, String identity) async {
|
||||
await global.settings!.removeIdentity(identity);
|
||||
if (global.settings!.identities.isEmpty) {
|
||||
Navigator.of(context).pushReplacementNamed("/login");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -216,7 +216,7 @@ packages:
|
|||
name: path_provider_windows
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
version: "2.1.2"
|
||||
platform:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
Loading…
Reference in New Issue