1
0
mirror of https://github.com/matrix-org/matrix-authentication-service.git synced 2025-11-20 12:02:22 +03:00

Implement the client credentials grant

This commit is contained in:
Quentin Gliech
2023-09-04 19:45:53 +02:00
parent 00fe5f902b
commit 542d0a6073
17 changed files with 498 additions and 127 deletions

View File

@@ -218,7 +218,7 @@ async fn get_requester(
};
// If there is a user for this session, check that it is not locked
let user_valid = user.as_ref().map_or(false, User::is_valid);
let user_valid = user.as_ref().map_or(true, User::is_valid);
if !token.is_valid(clock.now()) || !session.is_valid() || !user_valid {
return Err(RouteError::InvalidToken);

View File

@@ -16,8 +16,13 @@ use axum::http::Request;
use chrono::Duration;
use hyper::StatusCode;
use mas_data_model::{AccessToken, Client, TokenType, User};
use mas_router::SimpleRoute;
use mas_storage::{oauth2::OAuth2ClientRepository, RepositoryAccess};
use oauth2_types::scope::{Scope, ScopeToken, OPENID};
use oauth2_types::{
registration::ClientRegistrationResponse,
requests::AccessTokenResponse,
scope::{Scope, ScopeToken, OPENID},
};
use sqlx::PgPool;
use crate::test_utils::{init_tracing, RequestBuilderExt, ResponseExt, TestState};
@@ -349,3 +354,106 @@ async fn test_oauth2_admin(pool: PgPool) {
})
);
}
/// Test that we can query the GraphQL endpoint with a token from a
/// client_credentials grant.
#[sqlx::test(migrator = "mas_storage_pg::MIGRATOR")]
async fn test_oauth2_client_credentials(pool: PgPool) {
init_tracing();
let state = TestState::from_pool(pool).await.unwrap();
// Provision a client
let request =
Request::post(mas_router::OAuth2RegistrationEndpoint::PATH).json(serde_json::json!({
"client_uri": "https://example.com/",
// XXX: we shouldn't have to specify the redirect URI here, but the policy denies it for now
"redirect_uris": ["https://example.com/callback"],
"contacts": ["contact@example.com"],
"token_endpoint_auth_method": "client_secret_post",
"grant_types": ["client_credentials"],
"response_types": [],
}));
let response = state.request(request).await;
response.assert_status(StatusCode::CREATED);
let response: ClientRegistrationResponse = response.json();
let client_id = response.client_id;
let client_secret = response.client_secret.expect("to have a client secret");
// Call the token endpoint with an empty scope
let request = Request::post(mas_router::OAuth2TokenEndpoint::PATH).form(serde_json::json!({
"grant_type": "client_credentials",
"client_id": client_id,
"client_secret": client_secret,
"scope": "urn:mas:graphql:*",
}));
let response = state.request(request).await;
response.assert_status(StatusCode::OK);
let AccessTokenResponse { access_token, .. } = response.json();
let request = Request::post("/graphql")
.bearer(&access_token)
.json(serde_json::json!({
"query": r#"
query {
viewer {
__typename
}
viewerSession {
__typename
}
}
"#,
}));
let response = state.request(request).await;
response.assert_status(StatusCode::OK);
let response: GraphQLResponse = response.json();
assert!(response.errors.is_empty());
assert_eq!(
response.data,
serde_json::json!({
"viewer": {
// There is no user associated with the client credentials grant
"__typename": "Anonymous",
},
"viewerSession": {
// But there is a session
"__typename": "Oauth2Session",
},
})
);
// Check that we can't do a query once the token is revoked
let request = Request::post(mas_router::OAuth2Revocation::PATH).form(serde_json::json!({
"token": access_token,
"client_id": client_id,
"client_secret": client_secret,
}));
let response = state.request(request).await;
response.assert_status(StatusCode::OK);
// Do the same request again
let request = Request::post("/graphql")
.bearer(&access_token)
.json(serde_json::json!({
"query": r#"
query {
viewer {
__typename
}
viewerSession {
__typename
}
}
"#,
}));
let response = state.request(request).await;
response.assert_status(StatusCode::UNAUTHORIZED);
}