diff --git a/src/api/client/oauth/mod.rs b/src/api/client/oauth/mod.rs index 2fb373984..e9b5fcb5b 100644 --- a/src/api/client/oauth/mod.rs +++ b/src/api/client/oauth/mod.rs @@ -4,27 +4,29 @@ use axum::{ Json, Router, + extract::State, routing::method_routing::{get, post}, }; use serde_json::json; pub(crate) use server_metadata::*; -const BASE_PATH: &str = "/_continuwuity/oauth2/"; +const BASE_PATH: &str = const_str::concat!(conduwuit_core::ROUTE_PREFIX, "/oauth2/"); pub(crate) fn router() -> Router { Router::new().nest(BASE_PATH, oauth_router()) // TODO(unspecced): used by old versions of the matrix-js-sdk - // .route("/.well-known/openid-configuration", get( - // async |State(services): State| { - // Json(authorization_server_metadata(&services).await) - // } - // )) + .route("/.well-known/openid-configuration", get( + async |State(services): State| { + Json(authorization_server_metadata(&services).await) + } + )) } fn oauth_router() -> Router { Router::new() - .route("/client/register", post(register_client::register_client_route)) + .route(CLIENT_REGISTER_PATH, post(register_client::register_client_route)) // TODO(unspecced): used by old versions of the matrix-js-sdk - .route("/client/keys.json", get(async || Json(json!({"keys": []})))) - .route("/grant/token", post(token::token_route)) + .route(JWKS_URI_PATH, get(async || Json(json!({"keys": []})))) + .route(TOKEN_PATH, post(token::token_route)) + .route(TOKEN_REVOKE_PATH, post(token::revoke_token_route)) } diff --git a/src/api/client/oauth/server_metadata.rs b/src/api/client/oauth/server_metadata.rs index a2f646f1b..2993bce8f 100644 --- a/src/api/client/oauth/server_metadata.rs +++ b/src/api/client/oauth/server_metadata.rs @@ -6,6 +6,12 @@ use crate::Ruma; +pub(super) const AUTH_CODE_PATH: &str = "grant/authorization_code"; +pub(super) const JWKS_URI_PATH: &str = "client/keys.json"; +pub(super) const CLIENT_REGISTER_PATH: &str = "client/register"; +pub(super) const TOKEN_REVOKE_PATH: &str = "client/revoke"; +pub(super) const TOKEN_PATH: &str = "grant/token"; + pub(crate) async fn get_authorization_server_metadata_route( State(services): State, _body: Ruma, @@ -23,16 +29,16 @@ pub(crate) async fn authorization_server_metadata(services: &Services) -> Value .unwrap(); json!({ - "authorization_endpoint": endpoint_base.join("grant/authorization_code").unwrap(), + "authorization_endpoint": endpoint_base.join(AUTH_CODE_PATH).unwrap(), "code_challenge_methods_supported": ["S256"], "grant_types_supported": ["authorization_code", "refresh_token"], "issuer": services.config.get_client_domain(), - "jwks_uri": endpoint_base.join("client/keys.json").unwrap(), + "jwks_uri": endpoint_base.join(JWKS_URI_PATH).unwrap(), "prompt_values_supported": ["create"], - "registration_endpoint": endpoint_base.join("client/register").unwrap(), + "registration_endpoint": endpoint_base.join(CLIENT_REGISTER_PATH).unwrap(), "response_modes_supported": ["query", "fragment"], "response_types_supported": ["code"], - "revocation_endpoint": endpoint_base.join("client/revoke").unwrap(), - "token_endpoint": endpoint_base.join("grant/token").unwrap(), + "revocation_endpoint": endpoint_base.join(TOKEN_REVOKE_PATH).unwrap(), + "token_endpoint": endpoint_base.join(TOKEN_PATH).unwrap(), }) } diff --git a/src/api/client/oauth/token.rs b/src/api/client/oauth/token.rs index 7b2d0a2d5..fc6186c47 100644 --- a/src/api/client/oauth/token.rs +++ b/src/api/client/oauth/token.rs @@ -1,13 +1,23 @@ use axum::{Form, Json, extract::State, response::IntoResponse}; use http::StatusCode; -use service::oauth::grant::TokenRequest; +use service::oauth::grant::{RevokeTokenRequest, TokenRequest}; pub(crate) async fn token_route( State(services): State, Form(request): Form, ) -> impl IntoResponse { match services.oauth.issue_token(request).await { - | Ok(response) => Ok(Json(response).into_response()), + | Ok(response) => Ok(Json(response)), + | Err(err) => Err((StatusCode::BAD_REQUEST, err.message())), + } +} + +pub(crate) async fn revoke_token_route( + State(services): State, + Form(request): Form, +) -> impl IntoResponse { + match services.oauth.revoke_token(request.token).await { + | Ok(()) => Ok(StatusCode::OK), | Err(err) => Err((StatusCode::BAD_REQUEST, err.message())), } } diff --git a/src/service/oauth/grant.rs b/src/service/oauth/grant.rs index b60c0fd41..06ad5ad47 100644 --- a/src/service/oauth/grant.rs +++ b/src/service/oauth/grant.rs @@ -144,3 +144,8 @@ pub struct TokenResponse { pub enum TokenType { Bearer, } + +#[derive(Deserialize)] +pub struct RevokeTokenRequest { + pub token: String, +} diff --git a/src/service/oauth/mod.rs b/src/service/oauth/mod.rs index 45dba301a..53fb76e09 100644 --- a/src/service/oauth/mod.rs +++ b/src/service/oauth/mod.rs @@ -306,6 +306,30 @@ pub async fn issue_token(&self, request: TokenRequest) -> Result } } + pub async fn revoke_token(&self, token: String) -> Result<()> { + let (user_id, device_id) = if let Ok(refresh_token_info) = self + .db + .refreshtoken_refreshtokeninfo + .get(&token) + .await + .deserialized::() + { + (refresh_token_info.user_id, refresh_token_info.device_id) + } else if let Some(user) = self.services.users.find_from_token(&token).await { + user + } else { + return Err!("Invalid token"); + }; + + // This will also call [`Self::remove_session`] + self.services + .users + .remove_device(&user_id, &device_id) + .await; + + Ok(()) + } + async fn create_session( &self, authorizing_user: OwnedUserId,