Skip to content
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

refactor(oidc): Align logout behavior with MSC4254 #4674

Merged
merged 3 commits into from
Feb 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
18 changes: 5 additions & 13 deletions bindings/matrix-sdk-ffi/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -800,10 +800,8 @@ impl Client {
Ok(Arc::new(session_verification_controller))
}

/// Log out the current user. This method returns an optional URL that
/// should be presented to the user to complete logout (in the case of
/// Session having been authenticated using OIDC).
pub async fn logout(&self) -> Result<Option<String>, ClientError> {
/// Log the current user out.
pub async fn logout(&self) -> Result<(), ClientError> {
let Some(auth_api) = self.inner.auth_api() else {
return Err(anyhow!("Missing authentication API").into());
};
Expand All @@ -812,19 +810,13 @@ impl Client {
AuthApi::Matrix(a) => {
tracing::info!("Logging out via the homeserver.");
a.logout().await?;
Ok(None)
Ok(())
}

AuthApi::Oidc(api) => {
tracing::info!("Logging out via OIDC.");
let end_session_builder = api.logout().await?;

if let Some(builder) = end_session_builder {
let url = builder.build()?.url;
return Ok(Some(url.to_string()));
}

Ok(None)
api.logout().await?;
Ok(())
}
_ => Err(anyhow!("Unknown authentication API").into()),
}
Expand Down
8 changes: 8 additions & 0 deletions crates/matrix-sdk/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,14 @@ simpler methods:
- `OidcError::MissingAuthenticationIssuer` was removed.
- [**breaking**]: The `authentication::qrcode` module was moved inside
`authentication::oidc`, because it is only available through the `Oidc` API.
- [**breaking**]: The behavior of `Oidc::logout()` is now aligned with
[MSC4254](https://github.com/matrix-org/matrix-spec-proposals/pull/4254)
([#4674](https://github.com/matrix-org/matrix-rust-sdk/pull/4674))
- Support for [RP-Initiated Logout](https://openid.net/specs/openid-connect-rpinitiated-1_0.html)
was removed, so it doesn't return an `OidcEndSessionUrlBuilder` anymore.
- Only one request is made to revoke the access token, since the server is
supposed to revoke both the access token and the associated refresh token
when the request is made.

## [0.10.0] - 2025-02-04

Expand Down
15 changes: 4 additions & 11 deletions crates/matrix-sdk/src/authentication/oidc/cross_process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -609,20 +609,13 @@ mod tests {
// Restore the session.
oidc.restore_session(tests::mock_session(tokens.clone())).await?;

let end_session_builder = oidc.logout().await?;
oidc.logout().await?;

// No end session builder because our test impl doesn't provide an end session
// endpoint.
assert!(end_session_builder.is_none());

// Both the access token and the refresh tokens have been invalidated.
// The access token has been invalidated.
{
let revoked = backend.revoked_tokens.lock().unwrap();
assert_eq!(revoked.len(), 2);
assert_eq!(
*revoked,
vec![tokens.access_token.clone(), tokens.refresh_token.clone().unwrap(),]
);
assert_eq!(revoked.len(), 1);
assert_eq!(*revoked, &[tokens.access_token]);
}

{
Expand Down
112 changes: 0 additions & 112 deletions crates/matrix-sdk/src/authentication/oidc/end_session_builder.rs

This file was deleted.

37 changes: 4 additions & 33 deletions crates/matrix-sdk/src/authentication/oidc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,6 @@ mod auth_code_builder;
mod backend;
mod cross_process;
mod data_serde;
mod end_session_builder;
#[cfg(all(feature = "e2e-encryption", not(target_arch = "wasm32")))]
pub mod qrcode;
pub mod registrations;
Expand All @@ -193,7 +192,6 @@ mod tests;
pub use self::{
auth_code_builder::{OidcAuthCodeUrlBuilder, OidcAuthorizationData},
cross_process::CrossProcessRefreshLockError,
end_session_builder::{OidcEndSessionData, OidcEndSessionUrlBuilder},
};
use self::{
backend::{server::OidcServer, OidcBackend},
Expand Down Expand Up @@ -1469,13 +1467,7 @@ impl Oidc {
}

/// Log out from the currently authenticated session.
///
/// On success, if the provider supports [RP-Initiated Logout], an
/// [`OidcEndSessionUrlBuilder`] will be provided to build the URL allowing
/// the user to log out from their account in the provider's interface.
///
/// [RP-Initiated Logout]: https://openid.net/specs/openid-connect-rpinitiated-1_0.html
pub async fn logout(&self) -> Result<Option<OidcEndSessionUrlBuilder>, OidcError> {
pub async fn logout(&self) -> Result<(), OidcError> {
let provider_metadata = self.provider_metadata().await?;
let client_credentials = self.data().ok_or(OidcError::NotAuthenticated)?.credentials();

Expand All @@ -1484,42 +1476,21 @@ impl Oidc {

let tokens = self.session_tokens().ok_or(OidcError::NotAuthenticated)?;

// Revoke the access token.
// Revoke the access token, it should revoke both tokens.
self.backend
.revoke_token(
client_credentials.clone(),
client_credentials,
revocation_endpoint,
tokens.access_token,
Some(OAuthTokenTypeHint::AccessToken),
)
.await?;

// Revoke the refresh token, if any.
if let Some(refresh_token) = tokens.refresh_token {
self.backend
.revoke_token(
client_credentials.clone(),
revocation_endpoint,
refresh_token,
Some(OAuthTokenTypeHint::RefreshToken),
)
.await?;
}

let end_session_builder =
provider_metadata.end_session_endpoint.clone().map(|end_session_endpoint| {
OidcEndSessionUrlBuilder::new(
self.clone(),
end_session_endpoint,
client_credentials.client_id().to_owned(),
)
});

if let Some(manager) = self.ctx().cross_process_token_refresh_manager.get() {
manager.on_logout().await?;
}

Ok(end_session_builder)
Ok(())
}
}

Expand Down
11 changes: 1 addition & 10 deletions examples/oidc_cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -610,22 +610,13 @@ impl OidcCli {
/// Log out from this session.
async fn logout(&self) -> anyhow::Result<()> {
// Log out via OIDC.
let url_builder = self.client.oidc().logout().await?;
self.client.oidc().logout().await?;

// Delete the stored session and database.
let data_dir = self.session_file.parent().expect("The file has a parent directory");
fs::remove_dir_all(data_dir).await?;

println!("\nLogged out successfully");

if let Some(url_builder) = url_builder {
let data = url_builder.build()?;
println!(
"\nTo log out from your account in the provider's interface, visit: {}",
data.url
);
}

println!("\nExiting…");

Ok(())
Expand Down
Loading