Skip to content

Commit 11a852e

Browse files
committed
introspection: Support personal access tokens
1 parent 4005d53 commit 11a852e

File tree

1 file changed

+91
-3
lines changed

1 file changed

+91
-3
lines changed

crates/handlers/src/oauth2/introspection.rs

Lines changed: 91 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ use mas_axum_utils::{
1515
client_authorization::{ClientAuthorization, CredentialsVerificationError},
1616
record_error,
1717
};
18-
use mas_data_model::{BoxClock, Clock, Device, TokenFormatError, TokenType};
18+
use mas_data_model::{
19+
BoxClock, Clock, Device, TokenFormatError, TokenType, personal::session::PersonalSessionOwner,
20+
};
1921
use mas_iana::oauth::{OAuthClientAuthenticationMethod, OAuthTokenTypeHint};
2022
use mas_keystore::Encrypter;
2123
use mas_matrix::HomeserverConnection;
@@ -641,8 +643,94 @@ pub(crate) async fn post(
641643
}
642644

643645
TokenType::PersonalAccessToken => {
644-
// TODO
645-
return Err(RouteError::UnknownToken(TokenType::PersonalAccessToken));
646+
let access_token = repo
647+
.personal_access_token()
648+
.find_by_token(token)
649+
.await?
650+
.ok_or(RouteError::UnknownToken(TokenType::AccessToken))?;
651+
652+
if !access_token.is_valid(clock.now()) {
653+
return Err(RouteError::InvalidToken(TokenType::AccessToken));
654+
}
655+
656+
let session = repo
657+
.personal_session()
658+
.lookup(access_token.session_id)
659+
.await?
660+
.ok_or(RouteError::CantLoadPersonalSession(access_token.session_id))?;
661+
662+
if !session.is_valid() {
663+
return Err(RouteError::InvalidPersonalSession(session.id));
664+
}
665+
666+
let actor_user = repo
667+
.user()
668+
.lookup(session.actor_user_id)
669+
.await?
670+
.ok_or(RouteError::CantLoadUser(session.actor_user_id))?;
671+
672+
if !actor_user.is_valid() {
673+
return Err(RouteError::InvalidUser(actor_user.id));
674+
}
675+
676+
let client_id = match session.owner {
677+
PersonalSessionOwner::User(owner_user_id) => {
678+
let owner_user = repo
679+
.user()
680+
.lookup(owner_user_id)
681+
.await?
682+
.ok_or(RouteError::CantLoadUser(owner_user_id))?;
683+
684+
if !owner_user.is_valid() {
685+
return Err(RouteError::InvalidUser(owner_user.id));
686+
}
687+
688+
None
689+
}
690+
PersonalSessionOwner::OAuth2Client(owner_client_id) => {
691+
let owner_client = repo
692+
.oauth2_client()
693+
.lookup(owner_client_id)
694+
.await?
695+
.ok_or(RouteError::CantLoadOAuth2Client(owner_client_id))?;
696+
697+
// OAuth2 clients are always valid if they're in the database
698+
Some(owner_client.client_id.clone())
699+
}
700+
};
701+
702+
activity_tracker
703+
.record_personal_access_token_session(&clock, &session, ip)
704+
.await;
705+
706+
INTROSPECTION_COUNTER.add(
707+
1,
708+
&[
709+
KeyValue::new(KIND, "personal_access_token"),
710+
KeyValue::new(ACTIVE, true),
711+
],
712+
);
713+
714+
let scope = normalize_scope(session.scope);
715+
716+
IntrospectionResponse {
717+
active: true,
718+
scope: Some(scope),
719+
client_id,
720+
username: Some(actor_user.username),
721+
token_type: Some(OAuthTokenTypeHint::AccessToken),
722+
exp: access_token.expires_at,
723+
expires_in: access_token
724+
.expires_at
725+
.map(|expires_at| expires_at.signed_duration_since(clock.now())),
726+
iat: Some(access_token.created_at),
727+
nbf: Some(access_token.created_at),
728+
sub: Some(actor_user.sub),
729+
aud: None,
730+
iss: None,
731+
jti: None,
732+
device_id: None,
733+
}
646734
}
647735
};
648736

0 commit comments

Comments
 (0)