Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
eeac412
BREAKING: implement Native OIDC as per MSC 3861
TheOneWithTheBraid Feb 7, 2025
5939954
fix: wrong response_type parameter
TheOneWithTheBraid Feb 7, 2025
390c254
feat: implement a callback to hand over the OAuth2.0 authorization ur…
TheOneWithTheBraid Feb 7, 2025
e3cdc0e
feat: add parameter to enforce new dynamic client registration
TheOneWithTheBraid Feb 8, 2025
bc446fc
fix: still fetch auth metadata when well known is not served
TheOneWithTheBraid Feb 8, 2025
fbf3b1c
feat: improve documentation
TheOneWithTheBraid Feb 8, 2025
d070d13
fix: include scope into oauth2 redirect
TheOneWithTheBraid Feb 8, 2025
acd5f46
fix: revert unwanted change in Client
TheOneWithTheBraid Feb 8, 2025
c6b0cb2
fix: properly calculate PKCE for OAuth2 token exchange
TheOneWithTheBraid Feb 8, 2025
d27dc1d
fix: correctly assign request form body for OAuth2 token requests
TheOneWithTheBraid Feb 8, 2025
852e8c8
fix: migration of device ID to individual database entry
TheOneWithTheBraid Feb 8, 2025
823252b
chore: better parse redirect uri query
TheOneWithTheBraid Feb 9, 2025
d1f1985
feat: support all possible methods for OIDC discovery
TheOneWithTheBraid Feb 9, 2025
d254546
fix: return .well-known after checking OIDC capabilities
TheOneWithTheBraid Feb 11, 2025
be69f2c
feat: separate OIDC discovery from .well-known
TheOneWithTheBraid Feb 11, 2025
51ef282
fix: avoid rate limits in Bootstrap.askSetupCrossSigning
TheOneWithTheBraid Feb 11, 2025
116d3d2
fix: ensure to store device IDs for legacy login
TheOneWithTheBraid Feb 16, 2025
786f658
fix: non-nullable database in Client.init
TheOneWithTheBraid May 20, 2025
c9f5a73
fix: clean up bad version response
TheOneWithTheBraid May 20, 2025
ff04de0
fix: PR review
TheOneWithTheBraid Aug 2, 2025
34ab897
fix: do not wait for auth metadata present
TheOneWithTheBraid Aug 2, 2025
8ab97e0
feat: add example
TheOneWithTheBraid Aug 2, 2025
82ad905
chore: migrate client box keys to enum
TheOneWithTheBraid Aug 12, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions lib/encryption/utils/bootstrap.dart
Original file line number Diff line number Diff line change
Expand Up @@ -469,12 +469,12 @@ class Bootstrap {
}
}
if (newSsssKey != null) {
final storeFutures = <Future<void>>[];
Logs().v('Store new SSSS key entries...');
// NOTE(TheOneWithTheBraid): do not use Future.wait due to rate limits
// and token refresh trouble
for (final entry in secretsToStore.entries) {
storeFutures.add(newSsssKey!.store(entry.key, entry.value));
await newSsssKey!.store(entry.key, entry.value);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this related?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the token rotation might otherwise happen during the requests as mentioned in the comment in the line above.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't get it. Can you please explain?

}
Logs().v('Store new SSSS key entries...');
await Future.wait(storeFutures);
}

final keysToSign = <SignableKey>[];
Expand Down
1 change: 1 addition & 0 deletions lib/matrix.dart
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ export 'msc_extensions/extension_recent_emoji/recent_emoji.dart';
export 'msc_extensions/msc_3935_cute_events/msc_3935_cute_events.dart';
export 'msc_extensions/msc_1236_widgets/msc_1236_widgets.dart';
export 'msc_extensions/msc_2835_uia_login/msc_2835_uia_login.dart';
export 'msc_extensions/msc_3861_native_oidc/msc_3861_native_oidc.dart';
export 'msc_extensions/msc_3814_dehydrated_devices/msc_3814_dehydrated_devices.dart';
export 'msc_extensions/extension_timeline_export/timeline_export.dart';

Expand Down
10 changes: 9 additions & 1 deletion lib/msc_extensions/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,13 @@ Please try to cover the following conventions:
- MSC 1236 - Widget API V2
- MSC 2835 - UIA login
- MSC 3814 - Dehydrated Devices
- MSC 3861 - Next-generation auth for Matrix, based on OAuth 2.0/OIDC
- MSC 1597 - Better spec for matrix identifiers
- MSC 2964 - Usage of OAuth 2.0 authorization code grant and refresh token grant
- MSC 2965 - OAuth 2.0 Authorization Server Metadata discovery
- MSC 2966 - Usage of OAuth 2.0 Dynamic Client Registration in Matrix
- MSC 2967 - API scopes
- MSC 3824 - OIDC aware clients
- MSC 4191 - Account management deep-linking
- MSC 3935 - Cute Events
- `io.element.recent_emoji` - recent emoji sync in account data
- `io.element.recent_emoji` - recent emoji sync in account data
56 changes: 56 additions & 0 deletions lib/msc_extensions/msc_3861_native_oidc/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Implement \[matrix\] OIDC using the Matrix Dart SDK

```dart
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:matrix/matrix.dart';
import 'package:url_launcher:url_launcher.dart';
Future<void> main() async {
final client = Client(
// [...]
);
await client.checkHomeserver(myHomeserver);
final name = 'My [matrix] client on web';
final registrationData = OidcDynamicRegistrationData.localized(
contacts: {myOidcAdminContact},
url: 'https://example.com',
defaultLocale: myDefaultLocale,
localizations: {
'fr_FR': LocalizedOidcClientMetadata(
// [...]
)
},
redirect: kIsWeb
? { 'http://localhost:0'}
: {
Uri.parse('com.example:/oauth2redirect/'),
Uri.parse('http://localhost/oauth2redirect/'),
},
applicationType: kIsWeb ? 'web' : 'native',
);
final oidcClientId = await client.oidcEnsureDynamicClientId(
registrationData,
);
// You can e.g. use `package:app_links` to forward the called deep link into this completer
final nativeCompleter = Completer<OidcCallbackResponse>();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seeing this I've no idea how to use it with for example flutter_web_auth(2) package. In the end I get a URL I need to open where I pass a redirect_uri and receive a token back, right? How is this done here?

Is a Completer really the best choice? Would it be possible to use a callback instead? Something like String Function(Uri uri) onAuth which we then set like this:

await client.oidcAuthorizationGrantFlow(
  // ...
  onAuth: (Uri uri) async {
    final token = await FlutterWebAuth2.authenticate(uri);
    return token;
  }
);

Another question: In browser apps it might be desired to open it in the same tab which in a flutter context would mean that the app unloads and then starts again. So we need to store information somewhere and continue with the received token later. Is this also possible here?

await client.oidcAuthorizationGrantFlow(
nativeCompleter: nativeCompleter,
oidcClientId: oidcClientId,
redirectUri: client.oAuth2RedirectUri,
launchOAuth2Uri: launchUrl,
responseMode: kIsWeb ? 'fragment' : 'query',
prompt: 'consent',
initialDeviceDisplayName: name,
enforceNewDeviceId: true,
);
}
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import 'dart:math';

import 'package:matrix/matrix.dart';

extension GenerateDeviceIdExtension on Client {
/// Checks whether the client already generated a device ID and creates one in case there is no.
/// Returns the generated device ID.
///
/// This is particularly useful when authenticating via OIDC since clients must supply a locally generated device ID for login via OIDC.
/// - [MSC 2964](https://github.com/matrix-org/matrix-spec-proposals/pull/2964) defines the code grant flow requiring a device ID in the OAuth2.0 scopes
/// - [MSC 2967](https://github.com/matrix-org/matrix-spec-proposals/pull/2967) requires a device ID to be present for requesting OAuth2.0 scopes
Future<String> oidcEnsureDeviceId([bool enforceNewDevice = false]) async {
if (!enforceNewDevice) {
final storedDeviceId =
deviceID ?? await database.getClientData(ClientData.deviceId);
if (storedDeviceId is String) {
Logs().d('[OIDC] Restoring device ID $storedDeviceId.');
return storedDeviceId;
}
}

// [MSC 1597](https://github.com/matrix-org/matrix-spec-proposals/pull/1597)
//
// [A-Z] but without I and O (smth too similar to 1 and 0)
const chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ';
final deviceId = String.fromCharCodes(
List.generate(
10,
(_) => chars.codeUnitAt(Random().nextInt(chars.length)),
),
);

await database.putClientData(ClientData.deviceId, deviceId);
Logs().d('[OIDC] Generated device ID $deviceId.');
return deviceId;
}
}
Loading
Loading