mirror of
https://forgejo.ellis.link/continuwuation/continuwuity.git
synced 2026-05-26 20:49:55 +00:00
feat: Implement oauth token revocation
This commit is contained in:
@@ -4,27 +4,29 @@ mod token;
|
|||||||
|
|
||||||
use axum::{
|
use axum::{
|
||||||
Json, Router,
|
Json, Router,
|
||||||
|
extract::State,
|
||||||
routing::method_routing::{get, post},
|
routing::method_routing::{get, post},
|
||||||
};
|
};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
pub(crate) use server_metadata::*;
|
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<crate::State> {
|
pub(crate) fn router() -> Router<crate::State> {
|
||||||
Router::new().nest(BASE_PATH, oauth_router())
|
Router::new().nest(BASE_PATH, oauth_router())
|
||||||
// TODO(unspecced): used by old versions of the matrix-js-sdk
|
// TODO(unspecced): used by old versions of the matrix-js-sdk
|
||||||
// .route("/.well-known/openid-configuration", get(
|
.route("/.well-known/openid-configuration", get(
|
||||||
// async |State(services): State<crate::State>| {
|
async |State(services): State<crate::State>| {
|
||||||
// Json(authorization_server_metadata(&services).await)
|
Json(authorization_server_metadata(&services).await)
|
||||||
// }
|
}
|
||||||
// ))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn oauth_router() -> Router<crate::State> {
|
fn oauth_router() -> Router<crate::State> {
|
||||||
Router::new()
|
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
|
// TODO(unspecced): used by old versions of the matrix-js-sdk
|
||||||
.route("/client/keys.json", get(async || Json(json!({"keys": []}))))
|
.route(JWKS_URI_PATH, get(async || Json(json!({"keys": []}))))
|
||||||
.route("/grant/token", post(token::token_route))
|
.route(TOKEN_PATH, post(token::token_route))
|
||||||
|
.route(TOKEN_REVOKE_PATH, post(token::revoke_token_route))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,12 @@ use service::Services;
|
|||||||
|
|
||||||
use crate::Ruma;
|
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(
|
pub(crate) async fn get_authorization_server_metadata_route(
|
||||||
State(services): State<crate::State>,
|
State(services): State<crate::State>,
|
||||||
_body: Ruma<get_authorization_server_metadata::v1::Request>,
|
_body: Ruma<get_authorization_server_metadata::v1::Request>,
|
||||||
@@ -23,16 +29,16 @@ pub(crate) async fn authorization_server_metadata(services: &Services) -> Value
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
json!({
|
json!({
|
||||||
"authorization_endpoint": endpoint_base.join("grant/authorization_code").unwrap(),
|
"authorization_endpoint": endpoint_base.join(AUTH_CODE_PATH).unwrap(),
|
||||||
"code_challenge_methods_supported": ["S256"],
|
"code_challenge_methods_supported": ["S256"],
|
||||||
"grant_types_supported": ["authorization_code", "refresh_token"],
|
"grant_types_supported": ["authorization_code", "refresh_token"],
|
||||||
"issuer": services.config.get_client_domain(),
|
"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"],
|
"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_modes_supported": ["query", "fragment"],
|
||||||
"response_types_supported": ["code"],
|
"response_types_supported": ["code"],
|
||||||
"revocation_endpoint": endpoint_base.join("client/revoke").unwrap(),
|
"revocation_endpoint": endpoint_base.join(TOKEN_REVOKE_PATH).unwrap(),
|
||||||
"token_endpoint": endpoint_base.join("grant/token").unwrap(),
|
"token_endpoint": endpoint_base.join(TOKEN_PATH).unwrap(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,23 @@
|
|||||||
use axum::{Form, Json, extract::State, response::IntoResponse};
|
use axum::{Form, Json, extract::State, response::IntoResponse};
|
||||||
use http::StatusCode;
|
use http::StatusCode;
|
||||||
use service::oauth::grant::TokenRequest;
|
use service::oauth::grant::{RevokeTokenRequest, TokenRequest};
|
||||||
|
|
||||||
pub(crate) async fn token_route(
|
pub(crate) async fn token_route(
|
||||||
State(services): State<crate::State>,
|
State(services): State<crate::State>,
|
||||||
Form(request): Form<TokenRequest>,
|
Form(request): Form<TokenRequest>,
|
||||||
) -> impl IntoResponse {
|
) -> impl IntoResponse {
|
||||||
match services.oauth.issue_token(request).await {
|
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<crate::State>,
|
||||||
|
Form(request): Form<RevokeTokenRequest>,
|
||||||
|
) -> impl IntoResponse {
|
||||||
|
match services.oauth.revoke_token(request.token).await {
|
||||||
|
| Ok(()) => Ok(StatusCode::OK),
|
||||||
| Err(err) => Err((StatusCode::BAD_REQUEST, err.message())),
|
| Err(err) => Err((StatusCode::BAD_REQUEST, err.message())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -144,3 +144,8 @@ pub struct TokenResponse {
|
|||||||
pub enum TokenType {
|
pub enum TokenType {
|
||||||
Bearer,
|
Bearer,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct RevokeTokenRequest {
|
||||||
|
pub token: String,
|
||||||
|
}
|
||||||
|
|||||||
@@ -306,6 +306,30 @@ impl Service {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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::<RefreshTokenInfo>()
|
||||||
|
{
|
||||||
|
(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(
|
async fn create_session(
|
||||||
&self,
|
&self,
|
||||||
authorizing_user: OwnedUserId,
|
authorizing_user: OwnedUserId,
|
||||||
|
|||||||
Reference in New Issue
Block a user