You've already forked authentication-service
mirror of
https://github.com/matrix-org/matrix-authentication-service.git
synced 2025-07-31 09:24:31 +03:00
Write tests for the token revocation endpoint
This commit is contained in:
@ -76,8 +76,11 @@ oauth2-types = { path = "../oauth2-types" }
|
||||
[dev-dependencies]
|
||||
indoc = "2.0.0"
|
||||
insta = "1.26.0"
|
||||
tracing-subscriber = "0.3.16"
|
||||
|
||||
[features]
|
||||
default = ["webpki-roots"]
|
||||
|
||||
# Use the native root certificates
|
||||
native-roots = ["mas-axum-utils/native-roots", "mas-http/native-roots"]
|
||||
# Use the webpki root certificates
|
||||
|
@ -367,6 +367,7 @@ where
|
||||
#[cfg(test)]
|
||||
async fn test_state(pool: sqlx::PgPool) -> Result<AppState, anyhow::Error> {
|
||||
use mas_email::MailTransport;
|
||||
use mas_keystore::{JsonWebKey, JsonWebKeySet, PrivateKey};
|
||||
|
||||
use crate::passwords::Hasher;
|
||||
|
||||
@ -378,8 +379,13 @@ async fn test_state(pool: sqlx::PgPool) -> Result<AppState, anyhow::Error> {
|
||||
|
||||
let templates = Templates::load(workspace_root.join("templates"), url_builder.clone()).await?;
|
||||
|
||||
// TODO: add test keys to the store
|
||||
let key_store = Keystore::default();
|
||||
// TODO: add more test keys to the store
|
||||
let rsa =
|
||||
PrivateKey::load_pem(include_str!("../../keystore/tests/keys/rsa.pkcs1.pem")).unwrap();
|
||||
let rsa = JsonWebKey::new(rsa).with_kid("test-rsa");
|
||||
|
||||
let jwks = JsonWebKeySet::new(vec![rsa]);
|
||||
let key_store = Keystore::new(jwks);
|
||||
|
||||
let encrypter = Encrypter::new(&[0x42; 32]);
|
||||
|
||||
|
@ -198,3 +198,178 @@ pub(crate) async fn post(
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use hyper::{
|
||||
header::{AUTHORIZATION, CONTENT_TYPE},
|
||||
Request,
|
||||
};
|
||||
use mas_data_model::AuthorizationCode;
|
||||
use mas_router::SimpleRoute;
|
||||
use mas_storage::{RepositoryAccess, RepositoryTransaction, SystemClock};
|
||||
use mas_storage_pg::PgRepository;
|
||||
use oauth2_types::{
|
||||
registration::ClientRegistrationResponse,
|
||||
requests::{AccessTokenResponse, ResponseMode},
|
||||
scope::{Scope, OPENID},
|
||||
};
|
||||
use rand::SeedableRng;
|
||||
use sqlx::PgPool;
|
||||
use tower::{Service, ServiceExt};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[sqlx::test(migrator = "mas_storage_pg::MIGRATOR")]
|
||||
async fn test_revoke_access_token(pool: PgPool) {
|
||||
tracing_subscriber::fmt()
|
||||
.with_max_level(tracing::Level::INFO)
|
||||
.with_test_writer()
|
||||
.init();
|
||||
|
||||
let clock = SystemClock::default();
|
||||
let mut rng = rand_chacha::ChaChaRng::seed_from_u64(42);
|
||||
|
||||
let state = crate::test_state(pool.clone()).await.unwrap();
|
||||
let mut app = crate::api_router().with_state(state);
|
||||
|
||||
let request = Request::post(mas_router::OAuth2RegistrationEndpoint::PATH)
|
||||
.header(CONTENT_TYPE, "application/json")
|
||||
.body(
|
||||
serde_json::json!({
|
||||
"client_uri": "https://example.com/",
|
||||
"redirect_uris": ["https://example.com/callback"],
|
||||
"contacts": ["contact@example.com"],
|
||||
"token_endpoint_auth_method": "client_secret_post",
|
||||
"response_types": ["code"],
|
||||
"grant_types": ["authorization_code"],
|
||||
})
|
||||
.to_string(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let response = app.ready().await.unwrap().call(request).await.unwrap();
|
||||
assert_eq!(response.status(), StatusCode::CREATED);
|
||||
|
||||
let body = hyper::body::to_bytes(response.into_body()).await.unwrap();
|
||||
let client_registration: ClientRegistrationResponse =
|
||||
serde_json::from_slice(&body).unwrap();
|
||||
|
||||
let client_id = client_registration.client_id;
|
||||
let client_secret = client_registration.client_secret.unwrap();
|
||||
|
||||
// Let's provision a user and create a session for them. This part is hard to
|
||||
// test with just HTTP requests, so we'll use the repository directly.
|
||||
let mut repo = PgRepository::from_pool(&pool).await.unwrap();
|
||||
|
||||
let user = repo
|
||||
.user()
|
||||
.add(&mut rng, &clock, "alice".to_owned())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let browser_session = repo
|
||||
.browser_session()
|
||||
.add(&mut rng, &clock, &user)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Lookup the client in the database.
|
||||
let client = repo
|
||||
.oauth2_client()
|
||||
.find_by_client_id(&client_id)
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
// Start a grant
|
||||
let grant = repo
|
||||
.oauth2_authorization_grant()
|
||||
.add(
|
||||
&mut rng,
|
||||
&clock,
|
||||
&client,
|
||||
"https://example.com/redirect".parse().unwrap(),
|
||||
Scope::from_iter([OPENID]),
|
||||
Some(AuthorizationCode {
|
||||
code: "thisisaverysecurecode".to_owned(),
|
||||
pkce: None,
|
||||
}),
|
||||
Some("state".to_owned()),
|
||||
Some("nonce".to_owned()),
|
||||
None,
|
||||
ResponseMode::Query,
|
||||
true,
|
||||
false,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let session = repo
|
||||
.oauth2_session()
|
||||
.create_from_grant(&mut rng, &clock, &grant, &browser_session)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let grant = repo
|
||||
.oauth2_authorization_grant()
|
||||
.fulfill(&clock, &session, grant)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
Box::new(repo).save().await.unwrap();
|
||||
|
||||
// Now call the token endpoint to get an access token.
|
||||
let request = Request::post(mas_router::OAuth2TokenEndpoint::PATH)
|
||||
.header(CONTENT_TYPE, "application/x-www-form-urlencoded")
|
||||
.body(
|
||||
format!(
|
||||
"grant_type=authorization_code&code={code}&redirect_uri={redirect_uri}&client_id={client_id}&client_secret={client_secret}",
|
||||
code = grant.code.unwrap().code,
|
||||
redirect_uri = grant.redirect_uri,
|
||||
),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let response = app.ready().await.unwrap().call(request).await.unwrap();
|
||||
let status = response.status();
|
||||
assert_eq!(status, StatusCode::OK);
|
||||
|
||||
let body = hyper::body::to_bytes(response.into_body()).await.unwrap();
|
||||
let token: AccessTokenResponse = serde_json::from_slice(&body).unwrap();
|
||||
|
||||
// Let's call the userinfo endpoint to make sure we can access it.
|
||||
let request = Request::get(mas_router::OidcUserinfo::PATH)
|
||||
.header(AUTHORIZATION, format!("Bearer {}", token.access_token))
|
||||
.body(String::new())
|
||||
.unwrap();
|
||||
|
||||
let response = app.ready().await.unwrap().call(request).await.unwrap();
|
||||
let status = response.status();
|
||||
assert_eq!(status, StatusCode::OK);
|
||||
|
||||
// Now let's revoke the access token.
|
||||
let request = Request::post(mas_router::OAuth2Revocation::PATH)
|
||||
.header(CONTENT_TYPE, "application/x-www-form-urlencoded")
|
||||
.body(format!(
|
||||
"token={token}&token_type_hint=access_token&client_id={client_id}&client_secret={client_secret}",
|
||||
token = token.access_token
|
||||
))
|
||||
.unwrap();
|
||||
|
||||
let response = app.ready().await.unwrap().call(request).await.unwrap();
|
||||
let status = response.status();
|
||||
assert_eq!(status, StatusCode::OK);
|
||||
|
||||
// Call the userinfo endpoint again to make sure we can't access it anymore.
|
||||
let request = Request::get(mas_router::OidcUserinfo::PATH)
|
||||
.header(AUTHORIZATION, format!("Bearer {}", token.access_token))
|
||||
.body(String::new())
|
||||
.unwrap();
|
||||
|
||||
let response = app.ready().await.unwrap().call(request).await.unwrap();
|
||||
let status = response.status();
|
||||
assert_eq!(status, StatusCode::UNAUTHORIZED);
|
||||
// TODO: test refreshing the access token, test refresh token revocation
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user