-
Notifications
You must be signed in to change notification settings - Fork 56
BREAKING: implement Native OIDC as per MSC 3861 #2024
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
eeac412
5939954
390c254
e3cdc0e
bc446fc
fbf3b1c
d070d13
acd5f46
c6b0cb2
d27dc1d
852e8c8
823252b
d1f1985
d254546
be69f2c
51ef282
116d3d2
786f658
c9f5a73
ff04de0
34ab897
8ab97e0
82ad905
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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>(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 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 { | ||
TheOneWithTheBraid marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this related?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?