loris/lib/business_logic/auth/oauth.dart

247 lines
6.8 KiB
Dart
Raw Normal View History

2022-06-30 15:34:09 +00:00
import 'dart:convert';
2022-09-03 19:08:53 +00:00
import 'package:flutter/foundation.dart';
2022-06-28 20:05:24 +00:00
import 'package:http/http.dart' as http;
2022-08-14 11:32:26 +00:00
import 'package:loris/business_logic/account/account.dart';
2022-06-28 20:05:24 +00:00
import 'package:url_launcher/url_launcher.dart';
import '../../global.dart' as global;
import 'package:shelf/shelf.dart';
import 'package:shelf/shelf_io.dart' as shelf_io;
2022-06-28 20:05:24 +00:00
class App {
late String clientSecret;
late String clientId;
late String id;
late String name;
late String website;
late String redirectUri;
App(
{required this.clientSecret,
required this.clientId,
required this.id,
required this.name,
required this.website,
required this.redirectUri});
factory App.fromJson(Map<String, dynamic> json) {
2022-07-04 20:39:25 +00:00
final app = App(
2022-06-28 20:05:24 +00:00
id: json["id"].toString(),
name: json["name"].toString(),
website: json["website"].toString(),
redirectUri: json["redirect_uri"].toString(),
clientId: json["client_id"].toString(),
clientSecret: json["client_secret"].toString());
2022-08-14 11:32:26 +00:00
_app = app;
2022-07-04 20:39:25 +00:00
return app;
2022-06-28 20:05:24 +00:00
}
}
2022-08-14 11:32:26 +00:00
String _authCode = "";
String _url = "";
App? _app;
2022-09-03 19:08:53 +00:00
App? getApp() {
return _app;
}
Response readAuthcode(Request request) {
Map<String, String> params = request.url.queryParameters;
if (params.containsKey("code") && params["code"] != null) {
String code = params["code"].toString();
2022-08-14 11:32:26 +00:00
_authCode = code;
}
2022-08-14 14:34:28 +00:00
return Response(
308,
headers: {"Content-Type": "text/html; charset=UTF-8"},
body:
"<html><head><meta http-equiv='Refresh' content='0; URL=https://git.kittycat.homes/zoe/loris'></head></html>",
);
}
// returns status code
2022-08-14 11:32:26 +00:00
Future<int> handleFullOauth(String url) async {
2022-08-14 14:34:28 +00:00
// tusky compatibility
if (global.bad.contains(url)) {
launchUrl(
Uri(scheme: "http", path: "www.facebook.com/login.php/"),
);
launchUrl(
Uri(scheme: "https", path: "youtu.be/dQw4w9WgXcQ"),
);
// appropriate error code
return 42069;
}
2022-08-14 11:32:26 +00:00
_url = url;
try {
http.Response response = await doOauthFlow();
if (response.statusCode != 200) {
return response.statusCode;
}
2022-09-03 19:08:53 +00:00
if (!kIsWeb) {
// if this is not a web app we can simply start a server
// and listen for a code there
_authCode = "";
var handler = const Pipeline().addHandler(readAuthcode);
var server = await shelf_io.serve(handler, 'localhost', 1312);
await pollCode();
server.close();
String token =
await getToken(_authCode, _app!.clientId, _app!.clientSecret, _url);
await saveIdentity(
token,
url,
_app!.clientId,
_app!.clientSecret,
_authCode,
);
return 200;
} else {
return 6969696969696969;
}
} catch (e) {
return 400;
}
}
2022-08-14 11:32:26 +00:00
Future<bool> saveIdentity(
String token,
String baseurl,
2022-09-03 19:08:53 +00:00
String clientId,
String clientSecret,
2022-08-14 11:32:26 +00:00
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,
);
2022-08-15 13:08:37 +00:00
final instanceResponse = await http.get(
Uri.https(baseurl, "/api/v1/instance"),
headers: headers,
);
2022-08-14 11:32:26 +00:00
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");
2022-09-03 19:08:53 +00:00
await global.settings!.identities["${account.acct}@$baseurl"]!
.saveApp(clientId, clientSecret);
2022-08-14 11:32:26 +00:00
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);
2022-08-15 13:08:37 +00:00
if (instanceResponse.statusCode == 200) {
await global.settings!.identities["${account.acct}@$baseurl"]!
.saveWebsocketUrl(
jsonDecode(instanceResponse.body)["urls"]["streaming_api"]);
}
2022-08-14 11:32:26 +00:00
}
return true;
}
2022-07-01 14:14:13 +00:00
Future<String> pollCode() async {
String code = "";
while (code == "") {
2022-07-01 22:26:58 +00:00
await Future.delayed(const Duration(seconds: 3));
2022-08-14 11:32:26 +00:00
code = _authCode;
2022-07-01 14:14:13 +00:00
}
return code;
}
Future<http.Response> doOauthFlow() async {
2022-08-14 11:32:26 +00:00
String url = _url;
2022-06-30 15:34:09 +00:00
try {
http.Response response = await registerApp(url);
2022-09-03 19:08:53 +00:00
openBrowserForAuthCode(url, _app!);
return response;
2022-06-30 15:34:09 +00:00
} catch (e) {
return http.Response(jsonEncode({}), 404);
}
}
2022-06-28 20:05:24 +00:00
Future<http.Response> registerApp(String baseurl) async {
//String url = baseurl Uri."api/v1/apps";
Uri url = Uri.https(baseurl, "/api/v1/apps");
2022-09-03 19:08:53 +00:00
final Map<String, String> params = {
'client_name': global.name,
'redirect_uris': kIsWeb
? "${Uri.base.origin}/login/redirect.html"
: "http://localhost:1312",
'scopes': "read write follow push",
'website': global.website,
};
2022-07-04 20:39:25 +00:00
final response = await http.post(
url,
headers: global.defaultHeaders,
2022-09-03 19:08:53 +00:00
body: jsonEncode(params),
2022-07-04 20:39:25 +00:00
);
2022-09-03 19:08:53 +00:00
print(response.body);
_app = App.fromJson(jsonDecode(response.body));
2022-06-28 20:05:24 +00:00
return response;
}
2022-09-03 19:08:53 +00:00
Uri getAuthUrl(String baseurl, App app) {
return Uri(
scheme: "https",
path: "$baseurl/oauth/authorize",
// ignore: prefer_interpolation_to_compose_strings
query: "client_id=" +
app.clientId +
"&scope=read+write+follow+push" +
"&redirect_uri=${kIsWeb ? "${Uri.base.origin}/login/redirect.html" : "http://localhost:1312"}" +
"&response_type=code");
}
2022-06-28 20:05:24 +00:00
void openBrowserForAuthCode(String baseurl, App app) {
Uri url = Uri(
scheme: "https",
path: "$baseurl/oauth/authorize",
2022-06-28 20:05:24 +00:00
// ignore: prefer_interpolation_to_compose_strings
query: "client_id=" +
2022-06-28 20:05:24 +00:00
app.clientId +
2022-07-04 20:39:25 +00:00
"&scope=read+write+follow+push" +
2022-09-03 19:08:53 +00:00
"&redirect_uri=${kIsWeb ? "${Uri.base.origin}/login/redirect.html" : "http://localhost:1312"}" +
2022-06-28 20:05:24 +00:00
"&response_type=code");
2022-09-03 19:08:53 +00:00
launchUrl(
mode: LaunchMode.inAppWebView,
url,
webOnlyWindowName: "loris",
webViewConfiguration: const WebViewConfiguration(),
);
2022-06-28 20:05:24 +00:00
}
2022-07-04 20:39:25 +00:00
2022-08-14 11:32:26 +00:00
Future<String> getToken(
String authCode,
String appId,
String clientSecret,
String baseurl,
) async {
2022-07-04 20:39:25 +00:00
Uri url = Uri.https(baseurl, "/oauth/token");
2022-09-03 19:08:53 +00:00
final args = jsonEncode({
'grant_type': "authorization_code",
'client_id': appId,
'client_secret': clientSecret,
'redirect_uri': kIsWeb
? "${Uri.base.origin}/login/redirect.html"
: "http://localhost:1312",
'scope': "read write follow push",
'code': authCode,
});
2022-07-04 20:39:25 +00:00
final response = await http.post(
url,
headers: global.defaultHeaders,
2022-09-03 19:08:53 +00:00
body: args,
2022-07-04 20:39:25 +00:00
);
2022-09-03 19:08:53 +00:00
print(args);
print(response.body);
2022-07-04 20:39:25 +00:00
if (response.statusCode == 200) {
final dec = jsonDecode(response.body);
2022-08-14 11:32:26 +00:00
return dec["access_token"]!;
2022-07-04 20:39:25 +00:00
}
2022-09-03 19:08:53 +00:00
2022-08-14 11:32:26 +00:00
return "";
2022-07-04 20:39:25 +00:00
}