working a bit

This commit is contained in:
Henry Hiles 2025-06-19 13:03:18 -04:00
commit 93b6f180f6
No known key found for this signature in database
7 changed files with 61 additions and 23 deletions

1
.gitignore vendored
View file

@ -1,2 +1,3 @@
.dart_tool/ .dart_tool/
.direnv .direnv
secret

9
.vscode/launch.json vendored
View file

@ -9,13 +9,14 @@
"program": "bin/matrixgate.dart", "program": "bin/matrixgate.dart",
"args": [ "args": [
"--homeserver", "--homeserver",
"foo", "https://matrix.federated.nexus",
"--jwtSecretFile", "--jwtSecretFile",
"foo", "secret",
"--issuer", "--issuer",
"http://localhost:8080", "http://localhost:8080/",
"--authorizeEndpoint", "--authorizeEndpoint",
"https://federated.nexus/login" "http://localhost:4321/login"
// "https://federated.nexus/login"
], ],
"request": "launch", "request": "launch",
"type": "dart" "type": "dart"

View file

@ -33,7 +33,7 @@ void main(List<String> argsRaw) async {
"/.well-known/openid-configuration", "/.well-known/openid-configuration",
apiHelper.openidConfiguration, apiHelper.openidConfiguration,
) )
..get("/userInfo", apiHelper.userinfoHandler) ..get("/userinfo", apiHelper.userinfoHandler)
..get("/jwks.json", apiHelper.jwks) ..get("/jwks.json", apiHelper.jwks)
..post("/login", apiHelper.handleLogin) ..post("/login", apiHelper.handleLogin)
..post("/token", apiHelper.tokenHandler)) ..post("/token", apiHelper.tokenHandler))

View file

@ -21,9 +21,26 @@ class ApiHelper {
final password = data["password"]; final password = data["password"];
final redirectUri = data["redirect_uri"]; final redirectUri = data["redirect_uri"];
final state = data["state"] ?? ""; final state = data["state"] ?? "";
final clientId = data["client_id"];
final scope = data["scope"];
final nonce = data["nonce"];
// Basic validation
if ([
username,
password,
redirectUri,
clientId,
nonce,
scope,
].any((v) => v == null)) {
return Response(400, body: "Missing required field(s)");
}
// Matrix login
final loginRes = await http.post( final loginRes = await http.post(
Uri.https(settings.homeserver, "_matrix/client/v3/login"), Uri.https(settings.homeserver, "_matrix/client/v3/login"),
headers: {"Content-Type": "application/json"},
body: json.encode({ body: json.encode({
"type": "m.login.password", "type": "m.login.password",
"identifier": {"type": "m.id.user", "user": username}, "identifier": {"type": "m.id.user", "user": username},
@ -39,10 +56,11 @@ class ApiHelper {
final userId = loginData["user_id"]; final userId = loginData["user_id"];
final accessToken = loginData["access_token"]; final accessToken = loginData["access_token"];
// Request OpenID token from Matrix
final openidRes = await http.post( final openidRes = await http.post(
Uri.https( Uri.https(
settings.homeserver, settings.homeserver,
"_matrix/client/v3/user/$userId/openid/request", "_matrix/client/v3/user/${Uri.encodeComponent(userId)}/openid/request",
), ),
headers: {"Authorization": "Bearer $accessToken"}, headers: {"Authorization": "Bearer $accessToken"},
); );
@ -55,13 +73,19 @@ class ApiHelper {
final openidToken = json.decode(openidRes.body)["access_token"]; final openidToken = json.decode(openidRes.body)["access_token"];
// Generate and store authorization code
final code = base64Url.encode( final code = base64Url.encode(
List<int>.generate(16, (_) => DateTime.now().millisecond % 256), List<int>.generate(16, (_) => DateTime.now().millisecond % 256),
); );
ref ref
.read(AuthCodeController.provider.notifier) .read(AuthCodeController.provider.notifier)
.set(code, MatrixUser(userId: userId, matrixToken: openidToken)); .set(
code,
MatrixUser(userId: userId, matrixToken: openidToken, nonce: nonce!),
);
// Redirect back to client
return Response.found("$redirectUri?code=$code&state=$state"); return Response.found("$redirectUri?code=$code&state=$state");
} }
@ -133,10 +157,16 @@ class ApiHelper {
); );
} }
return Response.ok(matrixResp.body); return Response.ok(
matrixResp.body,
headers: {"content-type": "application/json"},
);
} }
Response jwks(_) => Response.ok(json.encode({"keys": []})); Response jwks(_) => Response.ok(
json.encode({"keys": []}),
headers: {"content-type": "application/json"},
);
Response openidConfiguration(_) { Response openidConfiguration(_) {
final settings = ref.read(SettingsController.provider)!; final settings = ref.read(SettingsController.provider)!;
@ -145,7 +175,7 @@ class ApiHelper {
"issuer": settings.issuer, "issuer": settings.issuer,
"authorization_endpoint": settings.authorizeEndpoint, "authorization_endpoint": settings.authorizeEndpoint,
"token_endpoint": "${settings.issuer}/token", "token_endpoint": "${settings.issuer}/token",
"userinfo_endpoint": "${settings.issuer}/userInfo", "userinfo_endpoint": "${settings.issuer}/userinfo",
"jwks_uri": "${settings.issuer}/jwks.json", "jwks_uri": "${settings.issuer}/jwks.json",
"response_types_supported": ["code"], "response_types_supported": ["code"],
"subject_types_supported": ["public"], "subject_types_supported": ["public"],

View file

@ -8,6 +8,7 @@ abstract class MatrixUser with _$MatrixUser {
const factory MatrixUser({ const factory MatrixUser({
required String userId, required String userId,
required String matrixToken, required String matrixToken,
required String nonce,
}) = _MatrixUser; }) = _MatrixUser;
factory MatrixUser.fromJson(Map<String, dynamic> json) => factory MatrixUser.fromJson(Map<String, dynamic> json) =>

View file

@ -16,7 +16,7 @@ T _$identity<T>(T value) => value;
/// @nodoc /// @nodoc
mixin _$MatrixUser { mixin _$MatrixUser {
String get userId; String get matrixToken; String get userId; String get matrixToken; String get nonce;
/// Create a copy of MatrixUser /// Create a copy of MatrixUser
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@ -29,16 +29,16 @@ $MatrixUserCopyWith<MatrixUser> get copyWith => _$MatrixUserCopyWithImpl<MatrixU
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is MatrixUser&&(identical(other.userId, userId) || other.userId == userId)&&(identical(other.matrixToken, matrixToken) || other.matrixToken == matrixToken)); return identical(this, other) || (other.runtimeType == runtimeType&&other is MatrixUser&&(identical(other.userId, userId) || other.userId == userId)&&(identical(other.matrixToken, matrixToken) || other.matrixToken == matrixToken)&&(identical(other.nonce, nonce) || other.nonce == nonce));
} }
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@override @override
int get hashCode => Object.hash(runtimeType,userId,matrixToken); int get hashCode => Object.hash(runtimeType,userId,matrixToken,nonce);
@override @override
String toString() { String toString() {
return 'MatrixUser(userId: $userId, matrixToken: $matrixToken)'; return 'MatrixUser(userId: $userId, matrixToken: $matrixToken, nonce: $nonce)';
} }
@ -49,7 +49,7 @@ abstract mixin class $MatrixUserCopyWith<$Res> {
factory $MatrixUserCopyWith(MatrixUser value, $Res Function(MatrixUser) _then) = _$MatrixUserCopyWithImpl; factory $MatrixUserCopyWith(MatrixUser value, $Res Function(MatrixUser) _then) = _$MatrixUserCopyWithImpl;
@useResult @useResult
$Res call({ $Res call({
String userId, String matrixToken String userId, String matrixToken, String nonce
}); });
@ -66,10 +66,11 @@ class _$MatrixUserCopyWithImpl<$Res>
/// Create a copy of MatrixUser /// Create a copy of MatrixUser
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? userId = null,Object? matrixToken = null,}) { @pragma('vm:prefer-inline') @override $Res call({Object? userId = null,Object? matrixToken = null,Object? nonce = null,}) {
return _then(_self.copyWith( return _then(_self.copyWith(
userId: null == userId ? _self.userId : userId // ignore: cast_nullable_to_non_nullable userId: null == userId ? _self.userId : userId // ignore: cast_nullable_to_non_nullable
as String,matrixToken: null == matrixToken ? _self.matrixToken : matrixToken // ignore: cast_nullable_to_non_nullable as String,matrixToken: null == matrixToken ? _self.matrixToken : matrixToken // ignore: cast_nullable_to_non_nullable
as String,nonce: null == nonce ? _self.nonce : nonce // ignore: cast_nullable_to_non_nullable
as String, as String,
)); ));
} }
@ -81,11 +82,12 @@ as String,
@JsonSerializable() @JsonSerializable()
class _MatrixUser implements MatrixUser { class _MatrixUser implements MatrixUser {
const _MatrixUser({required this.userId, required this.matrixToken}); const _MatrixUser({required this.userId, required this.matrixToken, required this.nonce});
factory _MatrixUser.fromJson(Map<String, dynamic> json) => _$MatrixUserFromJson(json); factory _MatrixUser.fromJson(Map<String, dynamic> json) => _$MatrixUserFromJson(json);
@override final String userId; @override final String userId;
@override final String matrixToken; @override final String matrixToken;
@override final String nonce;
/// Create a copy of MatrixUser /// Create a copy of MatrixUser
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@ -100,16 +102,16 @@ Map<String, dynamic> toJson() {
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _MatrixUser&&(identical(other.userId, userId) || other.userId == userId)&&(identical(other.matrixToken, matrixToken) || other.matrixToken == matrixToken)); return identical(this, other) || (other.runtimeType == runtimeType&&other is _MatrixUser&&(identical(other.userId, userId) || other.userId == userId)&&(identical(other.matrixToken, matrixToken) || other.matrixToken == matrixToken)&&(identical(other.nonce, nonce) || other.nonce == nonce));
} }
@JsonKey(includeFromJson: false, includeToJson: false) @JsonKey(includeFromJson: false, includeToJson: false)
@override @override
int get hashCode => Object.hash(runtimeType,userId,matrixToken); int get hashCode => Object.hash(runtimeType,userId,matrixToken,nonce);
@override @override
String toString() { String toString() {
return 'MatrixUser(userId: $userId, matrixToken: $matrixToken)'; return 'MatrixUser(userId: $userId, matrixToken: $matrixToken, nonce: $nonce)';
} }
@ -120,7 +122,7 @@ abstract mixin class _$MatrixUserCopyWith<$Res> implements $MatrixUserCopyWith<$
factory _$MatrixUserCopyWith(_MatrixUser value, $Res Function(_MatrixUser) _then) = __$MatrixUserCopyWithImpl; factory _$MatrixUserCopyWith(_MatrixUser value, $Res Function(_MatrixUser) _then) = __$MatrixUserCopyWithImpl;
@override @useResult @override @useResult
$Res call({ $Res call({
String userId, String matrixToken String userId, String matrixToken, String nonce
}); });
@ -137,10 +139,11 @@ class __$MatrixUserCopyWithImpl<$Res>
/// Create a copy of MatrixUser /// Create a copy of MatrixUser
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? userId = null,Object? matrixToken = null,}) { @override @pragma('vm:prefer-inline') $Res call({Object? userId = null,Object? matrixToken = null,Object? nonce = null,}) {
return _then(_MatrixUser( return _then(_MatrixUser(
userId: null == userId ? _self.userId : userId // ignore: cast_nullable_to_non_nullable userId: null == userId ? _self.userId : userId // ignore: cast_nullable_to_non_nullable
as String,matrixToken: null == matrixToken ? _self.matrixToken : matrixToken // ignore: cast_nullable_to_non_nullable as String,matrixToken: null == matrixToken ? _self.matrixToken : matrixToken // ignore: cast_nullable_to_non_nullable
as String,nonce: null == nonce ? _self.nonce : nonce // ignore: cast_nullable_to_non_nullable
as String, as String,
)); ));
} }

View file

@ -9,10 +9,12 @@ part of 'matrix_user.dart';
_MatrixUser _$MatrixUserFromJson(Map<String, dynamic> json) => _MatrixUser( _MatrixUser _$MatrixUserFromJson(Map<String, dynamic> json) => _MatrixUser(
userId: json['userId'] as String, userId: json['userId'] as String,
matrixToken: json['matrixToken'] as String, matrixToken: json['matrixToken'] as String,
nonce: json['nonce'] as String,
); );
Map<String, dynamic> _$MatrixUserToJson(_MatrixUser instance) => Map<String, dynamic> _$MatrixUserToJson(_MatrixUser instance) =>
<String, dynamic>{ <String, dynamic>{
'userId': instance.userId, 'userId': instance.userId,
'matrixToken': instance.matrixToken, 'matrixToken': instance.matrixToken,
'nonce': instance.nonce,
}; };