Skip to content

Conversation

@pixlwave
Copy link
Member

@pixlwave pixlwave commented Aug 16, 2023

This PR builds on top of #1019 exposing OIDC to the bindings.

  • The authentication service can be configured for OIDC (performing dynamic registration) and supports login via OIDC.
  • The FFI client now converts Session to/from the OIDC Sessions when necessary.
  • UnknownToken errors 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:

  • Check if the account URL needs to be stored or is already part of the Session (for use later).
  • Detect cancellation from the WebView properly.
  • Return the URL for RP initiated sign out when calling logout().

/// 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 {
Copy link
Member Author

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());
Copy link
Member Author

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.

Copy link
Collaborator

@zecakeh zecakeh Aug 18, 2023

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.

Copy link
Member Author

@pixlwave pixlwave Aug 18, 2023

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.

Copy link
Member

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.

Copy link
Member Author

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
@pixlwave pixlwave marked this pull request as ready for review August 16, 2023 17:40
@pixlwave pixlwave requested a review from a team as a code owner August 16, 2023 17:40
@pixlwave pixlwave requested review from poljar and removed request for a team August 16, 2023 17:40
@pixlwave
Copy link
Member Author

Looks like I'm missing some feature checks, job for tomorrow.

@codecov
Copy link

codecov bot commented Aug 16, 2023

Codecov Report

Patch coverage: 28.08% and project coverage change: -0.17% ⚠️

Comparison is base (0dac508) 77.06% compared to head (839e795) 76.89%.
Report is 44 commits behind head on main.

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     
Files Changed Coverage Δ
crates/matrix-sdk/src/lib.rs 100.00% <ø> (ø)
crates/matrix-sdk/src/oidc/data_serde.rs 0.00% <ø> (ø)
crates/matrix-sdk/src/oidc/mod.rs 0.00% <0.00%> (ø)
crates/matrix-sdk/src/client/mod.rs 75.51% <18.36%> (-4.99%) ⬇️
crates/matrix-sdk/src/client/futures.rs 50.00% <38.88%> (-31.82%) ⬇️
crates/matrix-sdk/src/matrix_auth/mod.rs 88.28% <100.00%> (+0.21%) ⬆️

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@pixlwave pixlwave requested review from a team and bnjbvr and removed request for a team and poljar August 17, 2023 12:23
@pixlwave
Copy link
Member Author

pixlwave commented Aug 17, 2023

Feature checks added, although I'm not sure I understand the CI failures, around error: unused import: error::ErrorKind. That was already present before this PR 🤔

Copy link
Collaborator

@zecakeh zecakeh left a 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

Copy link
Collaborator

@zecakeh zecakeh left a 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

Copy link
Member

@bnjbvr bnjbvr left a 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
Copy link
Member

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?

Copy link
Member Author

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.
@pixlwave pixlwave requested a review from bnjbvr August 21, 2023 11:51
Copy link
Collaborator

@jplatte jplatte left a 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?

@jplatte jplatte enabled auto-merge (squash) August 22, 2023 09:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants