-
Notifications
You must be signed in to change notification settings - Fork 345
feat(bindings): OIDC in AuthenticationService. #2412
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
Conversation
6ae456c to
8034ee4
Compare
| /// Log out the current user | ||
| pub fn logout(&self) -> Result<(), ClientError> { | ||
| RUNTIME.block_on(self.inner.matrix_auth().logout())?; | ||
| let Some(auth_api) = self.inner.auth_api() else { |
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.
This feels super weird to me that we don't call client.logout() and have it direct the logout request appropriately, but as client.logout() was removed since I first started this branch, I've left the logic here.
| } | ||
| _ => { | ||
| trace!("Token refresh: Token refresh failed."); | ||
| return Err(refresh_error.into()); |
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 presume there's also an error type for homeserver refresh that matches InvalidGrant to signal that the refresh was specifically denied. If so this should be handled here.
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.
According to the spec:
If the token refresh fails and the error response included a soft_logout: true property, then the client can treat it as a soft logout and attempt to obtain a new access token by re-logging in. If the error response does not include a soft_logout: true property, the client should consider the user as being logged out.
So the error doesn't matter, it's always considered a logout. I'm wondering if we should do the same for OIDC, because if the refresh token fails endlessly no request will be able to be made.
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'm going to defer this one to @hughns who suggested we should only use InvalidGrant as the signal to logout the user if OIDC token refresh fails.
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.
It's quite a while since I last discussed soft logout with @sandhose so my memory is hazy on it...
But, I suspect it is the case that Matrix's soft logout concept would no longer be possible.
Instead -
If a client founds itself with an invalid refresh token - most likely meaning that the session was signed out from another client or My Account UI - then it could ask the user whether they want to attempt re-authenticate or whether to Logout completely.
If the user asks to re-authenticate then you can reuse the existing device ID in the urn:matrix:client:device:XXX scope in the authorization request.
If the user says Logout completely then you would destroy all the local data.
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.
Ok, so for now, using InvalidGrant is correct for OIDC. Given that native refresh is going to sign the user out on any error in the current implementation, I'm just going to update this PR to do the same with a note that it isn't correct.
- Use OIDC for logout when appropriate. - Handle token refresh through OIDC too. - Support for server discovery over http. - Allow server's that support OIDC but not passwords to work. - Only sign out users if token refresh is explicitly refused. - Rebase on latest zecakeh/oidc-mas # Conflicts: # crates/matrix-sdk/src/client/mod.rs # crates/matrix-sdk/src/oidc/mod.rs
8034ee4 to
de21126
Compare
e3f0c3f to
be9dbe1
Compare
|
Looks like I'm missing some feature checks, job for tomorrow. |
7df2b28 to
2a11150
Compare
Codecov ReportPatch coverage:
Additional details and impacted files@@ Coverage Diff @@
## main #2412 +/- ##
==========================================
- Coverage 77.06% 76.89% -0.17%
==========================================
Files 181 181
Lines 19135 19186 +51
==========================================
+ Hits 14746 14754 +8
- Misses 4389 4432 +43
☔ View full report in Codecov by Sentry. |
1f2f702 to
a765b3c
Compare
a765b3c to
05f626e
Compare
|
Feature checks added, although I'm not sure I understand the CI failures, around |
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 tried to do a thorough review, though mostly focusing on interacting with the OIDC API
aa83a9f to
9026dbc
Compare
zecakeh
left a comment
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.
One error I didn't see before, and a few nitpicks
bnjbvr
left a comment
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.
Excited to have OIDC soon in ElementX; I have a few questions below.
Also, the FFI layer seems to do a lot, especially in authentication_service.rs. The role of the FFI layer is to fallibly convert data types, then transmit the data to the SDK itself for processing, then convert it back to looser types that the FFI can handle. If a SDK user wanted to handle OIDC, would they have to duplicate most of the logic of the FFI layer? If so, could we instead put some of that code in the non-FFI layer, please?
| // FFI-only fields (for now) | ||
| /// The URL for the homeserver used for this session. | ||
| pub homeserver_url: String, | ||
| /// Additional data for this session session if OpenID Connect was used |
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.
nit: session session
Also, this is a string that contains a serialized JSON blob, breaking the FFI layer boundary by allowing stringly typed data.
A few questions (very uninformed, as I don't know much about OIDC at all):
- does it need to be stored, or could it be transient and passed as a function argument somewhere?
- if it does need to be stored, could it be stored as the final deserialized format instead of JSON?
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.
On the stringly typed data and your questions:
- Yes it needs to be stored, it contains additional OIDC related data that is required for the app to restore a session correctly, so storing it alongside everything else makes the most sense to me.
- The app has absolutely no interest in the contents of this struct. If we make it a
struct, we would need to a) convert all these properties to types that Uniffi is happy to bridge and b) Then implement Codable conformances on the Swift side (and presumably similar on the Kotlin side) which seems like a lot of unnecessary work given we aren't interested in the data.
Remove check for Consent prompt. MAS supports it but isn't currently exposing that in it's well-known.
f01162d to
690da73
Compare
jplatte
left a comment
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.
This PR contains non-trivial amounts of matrix-sdk changes alongside the FFI changes. Could you please move those out into a separate PR?
This PR builds on top of #1019 exposing OIDC to the bindings.
Sessionto/from the OIDC Sessions when necessary.UnknownTokenerrors are now broadcast from the refresh logic depending on the errors encountered.SessionChanges are also broadcast to indicate that the app should update the session in whatever secure storage is being used.Left todo in this PR:
Session(for use later).logout().