make initial push ssh keys working

This commit is contained in:
lemoer 2025-06-29 12:51:54 +02:00
parent e6336e057a
commit 6357c4e2ae
8 changed files with 342 additions and 0 deletions

View File

@ -1,4 +1,5 @@
import 'dart:convert';
import 'dart:io';
import 'package:dartssh2/dartssh2.dart';
import 'package:flutter/material.dart';
@ -9,11 +10,91 @@ import 'package:flutter/services.dart';
// secret storage
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:oauth2/oauth2.dart' as oauth2;
import 'package:url_launcher/url_launcher.dart';
void main() {
runApp(const MyApp());
}
Future<oauth2.Client> getOAuth2Client() async {
// This is a placeholder for OAuth2 client initialization.
// Replace with your actual OAuth2 client setup.
final authorizationEndpoint = Uri.parse(
'https://auth.leinelab.org/application/o/authorize/',
);
final tokenEndpoint = Uri.parse(
'https://auth.leinelab.org/application/o/token/',
);
final identifier = 'UwSMm8gTwBTUURSaxp5uPpuwX1OkGO4FRHeO9v3i';
final secret = null; // = 'my client secret';
final redirectUrl = Uri.parse('http://localhost:30165/');
final credentialsFile = File('~/.myapp/credentials.json');
//....
var exists = await credentialsFile.exists();
if (exists) {
var credentials = oauth2.Credentials.fromJson(
await credentialsFile.readAsString(),
);
return oauth2.Client(credentials, identifier: identifier, secret: secret);
}
var grant = oauth2.AuthorizationCodeGrant(
identifier,
authorizationEndpoint,
tokenEndpoint,
secret: secret,
);
var authorizationUrl = grant.getAuthorizationUrl(
redirectUrl,
scopes: ["profile", "email", "goauthentik.io/api", "openid"],
);
// TODO: clicking the button twice might try to bind the server twice
var server = await HttpServer.bind("127.0.0.1", 30165);
await launchUrl(authorizationUrl);
var queryParameters;
await server.forEach((HttpRequest request) {
request.response.write(
'Success! You can close this window now and go back to the app.',
);
queryParameters = request.uri.queryParameters;
request.response.close();
server.close();
});
return await grant.handleAuthorizationResponse(queryParameters);
}
void makeAlert(BuildContext context, String title, String message) {
showDialog<String>(
context: context,
builder: (BuildContext context) => AlertDialog(
title: Text(title),
content: Text(message),
actions: <Widget>[
TextButton(
onPressed: () => Navigator.pop(context, 'OK'),
child: const Text('OK'),
),
],
),
);
}
Future<OpenSSHEd25519KeyPair> generateKeyPair() async {
final algorithm = Ed25519();
@ -66,6 +147,122 @@ class _MyHomePageState extends State<MyHomePage> {
OpenSSHEd25519KeyPair? keyPair;
String _output = '';
String key = '';
Future<void> doOAuth() async {
final client = await getOAuth2Client();
// TODO: Handle errors better
try {
final jsonMe = await client.read(
Uri.parse('https://auth.leinelab.org/api/v3/core/users/me/'),
);
// final me = jsonDecode(jsonMe);
// final user = me['user'];
// final transformed = {
// "username": user['username'],
// "name": user['name'],
// "email": user['email'],
// "attributes.settings.locale": user['settings']['locale'],
// "attributes.sshPublicKeys":
// "foooooooobar!", // fix oder aus anderer Quelle
// "component": "ak-stage-prompt",
// };
// final push = jsonEncode(transformed);
final push =
'{"username":"lemoer","name":"Leonardo Mörlein","email":"me@irrelefant.net","attributes.settings.locale":"","attributes.sshPublicKeys":"BLA!","component":"ak-stage-prompt"}';
print(push);
final response0 = await client.get(
Uri.parse(
'https://auth.leinelab.org/api/v3/flows/instances/default-user-settings-flow/execute/',
),
);
var sessionCookieHeader = response0.headers['set-cookie'];
if (sessionCookieHeader == null) {
throw Exception('No session cookie found in response headers.');
}
String? sessionCookie;
int index = sessionCookieHeader.indexOf(';');
sessionCookie = (index == -1)
? sessionCookieHeader
: sessionCookieHeader.substring(0, index);
final responsea = await client.get(
Uri.parse(
'https://auth.leinelab.org/api/v3/flows/executor/default-user-settings-flow/?query=',
),
headers: {'Cookie': sessionCookie},
);
print("Response A status code: ${responsea.statusCode}");
print("Response body:");
print(responsea.body);
print("Session cookie: $sessionCookie");
final response = await client.post(
Uri.parse(
'https://auth.leinelab.org/api/v3/flows/executor/default-user-settings-flow/?query=',
),
body: push,
headers: {'Content-Type': 'application/json', 'Cookie': sessionCookie},
);
if (response.statusCode != 302) {
throw Exception(
"Expected a redirect (302) response, but got ${response.statusCode}",
);
}
// if (response.statusCode == 200) {
// print("User data updated successfully.");
// print("Response body:");
// print(response.body);
// response.headers.toString().split('\n').forEach(print);
// } else {
// print("Error updating user data: ${response.statusCode}");
// print("Response body:");
// print(response.body);
// print(response.headers.toString());
// }
final newLocation = response.headers['location'];
if (newLocation != null) {
print("Redirecting to: $newLocation");
await launchUrl(Uri.parse(newLocation));
} else {
throw Exception("No redirect location found in response headers.");
}
final responseFinal = await client.get(
Uri.parse('https://auth.leinelab.org/' + newLocation),
headers: {'Cookie': sessionCookie},
);
if (responseFinal.statusCode == 200) {
print("User data updated successfully.");
print("responseFinal body:");
print(responseFinal.body);
responseFinal.headers.toString().split('\n').forEach(print);
} else {
print("Error updating user data: ${responseFinal.statusCode}");
print("responseFinal body:");
print(responseFinal.body);
print(responseFinal.headers.toString());
}
} catch (e) {
print(e.toString());
}
;
}
Future<void> doSSH() async {
SSHSocket? socket;
@ -151,6 +348,7 @@ class _MyHomePageState extends State<MyHomePage> {
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextButton(onPressed: doOAuth, child: Text("Oauth2 Login")),
Text('Current output:'),
Text(outputText, style: Theme.of(context).textTheme.headlineMedium),
],

View File

@ -7,9 +7,13 @@
#include "generated_plugin_registrant.h"
#include <flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h>
#include <url_launcher_linux/url_launcher_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin");
flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar);
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
}

View File

@ -4,6 +4,7 @@
list(APPEND FLUTTER_PLUGIN_LIST
flutter_secure_storage_linux
url_launcher_linux
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST

View File

@ -7,8 +7,12 @@ import Foundation
import flutter_secure_storage_macos
import path_provider_foundation
import url_launcher_macos
import webview_flutter_wkwebview
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
WebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "WebViewFlutterPlugin"))
}

View File

@ -176,6 +176,22 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
http:
dependency: transitive
description:
name: http
sha256: "2c11f3f94c687ee9bad77c171151672986360b2b001d109814ee7140b2cf261b"
url: "https://pub.dev"
source: hosted
version: "1.4.0"
http_parser:
dependency: transitive
description:
name: http_parser
sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
url: "https://pub.dev"
source: hosted
version: "4.1.2"
js:
dependency: transitive
description:
@ -240,6 +256,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.16.0"
oauth2:
dependency: "direct main"
description:
name: oauth2
sha256: c84470642cbb2bec450ccab2f8520c079cd1ca546a76ffd5c40589e07f4e8bf4
url: "https://pub.dev"
source: hosted
version: "2.0.3"
path:
dependency: transitive
description:
@ -389,6 +413,70 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.4.0"
url_launcher:
dependency: "direct main"
description:
name: url_launcher
sha256: "9d06212b1362abc2f0f0d78e6f09f726608c74e3b9462e8368bb03314aa8d603"
url: "https://pub.dev"
source: hosted
version: "6.3.1"
url_launcher_android:
dependency: transitive
description:
name: url_launcher_android
sha256: "8582d7f6fe14d2652b4c45c9b6c14c0b678c2af2d083a11b604caeba51930d79"
url: "https://pub.dev"
source: hosted
version: "6.3.16"
url_launcher_ios:
dependency: transitive
description:
name: url_launcher_ios
sha256: "7f2022359d4c099eea7df3fdf739f7d3d3b9faf3166fb1dd390775176e0b76cb"
url: "https://pub.dev"
source: hosted
version: "6.3.3"
url_launcher_linux:
dependency: transitive
description:
name: url_launcher_linux
sha256: "4e9ba368772369e3e08f231d2301b4ef72b9ff87c31192ef471b380ef29a4935"
url: "https://pub.dev"
source: hosted
version: "3.2.1"
url_launcher_macos:
dependency: transitive
description:
name: url_launcher_macos
sha256: "17ba2000b847f334f16626a574c702b196723af2a289e7a93ffcb79acff855c2"
url: "https://pub.dev"
source: hosted
version: "3.2.2"
url_launcher_platform_interface:
dependency: transitive
description:
name: url_launcher_platform_interface
sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029"
url: "https://pub.dev"
source: hosted
version: "2.3.2"
url_launcher_web:
dependency: transitive
description:
name: url_launcher_web
sha256: "4bd2b7b4dc4d4d0b94e5babfffbca8eac1a126c7f3d6ecbc1a11013faa3abba2"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
url_launcher_windows:
dependency: transitive
description:
name: url_launcher_windows
sha256: "3284b6d2ac454cf34f114e1d3319866fdd1e19cdc329999057e44ffe936cfa77"
url: "https://pub.dev"
source: hosted
version: "3.1.4"
vector_math:
dependency: transitive
description:
@ -405,6 +493,46 @@ packages:
url: "https://pub.dev"
source: hosted
version: "15.0.0"
web:
dependency: transitive
description:
name: web
sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a"
url: "https://pub.dev"
source: hosted
version: "1.1.1"
webview_flutter:
dependency: "direct main"
description:
name: webview_flutter
sha256: c3e4fe614b1c814950ad07186007eff2f2e5dd2935eba7b9a9a1af8e5885f1ba
url: "https://pub.dev"
source: hosted
version: "4.13.0"
webview_flutter_android:
dependency: transitive
description:
name: webview_flutter_android
sha256: f6e6afef6e234801da77170f7a1847ded8450778caf2fe13979d140484be3678
url: "https://pub.dev"
source: hosted
version: "4.7.0"
webview_flutter_platform_interface:
dependency: transitive
description:
name: webview_flutter_platform_interface
sha256: f0dc2dc3a2b1e3a6abdd6801b9355ebfeb3b8f6cde6b9dc7c9235909c4a1f147
url: "https://pub.dev"
source: hosted
version: "2.13.1"
webview_flutter_wkwebview:
dependency: transitive
description:
name: webview_flutter_wkwebview
sha256: a3d461fe3467014e05f3ac4962e5fdde2a4bf44c561cb53e9ae5c586600fdbc3
url: "https://pub.dev"
source: hosted
version: "3.22.0"
win32:
dependency: transitive
description:

View File

@ -37,6 +37,9 @@ dependencies:
dartssh2: ^2.12.0
cryptography: ^2.7.0
flutter_secure_storage: ^9.2.4
oauth2: ^2.0.3
webview_flutter: ^4.13.0
url_launcher: ^6.3.1
dev_dependencies:
flutter_test:

View File

@ -7,8 +7,11 @@
#include "generated_plugin_registrant.h"
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
#include <url_launcher_windows/url_launcher_windows.h>
void RegisterPlugins(flutter::PluginRegistry* registry) {
FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
UrlLauncherWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
}

View File

@ -4,6 +4,7 @@
list(APPEND FLUTTER_PLUGIN_LIST
flutter_secure_storage_windows
url_launcher_windows
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST