tetratto/crates/core/src/model/oauth.rs

187 lines
6.5 KiB
Rust
Raw Normal View History

2025-04-27 23:11:37 -04:00
use base64::{engine::general_purpose::URL_SAFE as base64url, Engine};
use serde::{Serialize, Deserialize};
use tetratto_shared::hash::hash;
use super::{Result, Error};
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct AuthGrant {
2025-06-14 14:45:52 -04:00
/// The ID of the application associated with this grant.
pub app: usize,
/// The code challenge for PKCE verifiers associated with this grant.
///
/// This challenge is *all* that is required to refresh this grant's auth token.
/// While there can only be one token at a time, it can be refreshed whenever as long
/// as the provided verifier matches that of the challenge.
///
/// The challenge should never be changed. To change the challenge, the grant
/// should be removed and recreated.
pub challenge: String,
/// The encoding method for the initial verifier in the challenge.
pub method: PkceChallengeMethod,
/// The access token associated with the account. This is **not** the same as
/// regular account access tokens, as the token can only be used with the requested `scopes`.
pub token: String,
2025-06-14 20:26:54 -04:00
/// The time in which the token was last refreshed. Tokens should stop being
/// accepted after a week has passed since this time.
pub last_updated: usize,
/// Scopes define what the grant's token is actually allowed to do.
///
/// No scope shall ever be allowed to change scopes or manage grants on behalf of the user.
/// A regular user token **must** be provided to manage grants.
pub scopes: Vec<AppScope>,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
2025-04-27 23:11:37 -04:00
pub enum PkceChallengeMethod {
S256,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
2025-04-27 23:11:37 -04:00
pub enum AppScope {
2025-06-14 20:41:18 -04:00
/// Read the profile of other users on behalf of the user.
UserReadProfiles,
/// Read the user's profile (username, bio, etc).
2025-04-27 23:11:37 -04:00
UserReadProfile,
/// Read the user's settings.
UserReadSettings,
/// Read the user's sessions and info.
UserReadSessions,
/// Read posts as the user.
UserReadPosts,
/// Read messages as the user.
UserReadMessages,
/// Read drafts as the user.
UserReadDrafts,
/// Read the user's communities.
UserReadCommunities,
/// Connect to sockets on the user's behalf.
UserReadSockets,
/// Read the user's notifications.
UserReadNotifications,
/// Read the user's requests.
UserReadRequests,
2025-06-13 23:25:10 -04:00
/// Read questions as the user.
UserReadQuestions,
/// Read the user's stacks.
UserReadStacks,
/// Read the user's journals.
UserReadJournals,
/// Read the user's notes.
UserReadNotes,
2025-07-02 20:14:04 -04:00
/// Read the user's layouts.
UserReadLayouts,
/// Create posts as the user.
UserCreatePosts,
/// Create messages as the user.
UserCreateMessages,
/// Ask questions as the user.
UserCreateQuestions,
/// Create IP blocks as the user.
UserCreateIpBlock,
/// Create drafts on behalf of the user.
UserCreateDrafts,
/// Create communities on behalf of the user.
UserCreateCommunities,
/// Create stacks on behalf of the user.
UserCreateStacks,
/// Create journals on behalf of the user.
UserCreateJournals,
/// Create notes on behalf of the user.
UserCreateNotes,
2025-07-02 20:14:04 -04:00
/// Create layouts on behalf of the user.
UserCreateLayouts,
/// Delete posts owned by the user.
UserDeletePosts,
/// Delete messages owned by the user.
UserDeleteMessages,
/// Delete questions as the user.
UserDeleteQuestions,
/// Delete drafts as the user.
UserDeleteDrafts,
/// Edit the user's settings and upload avatars/banners on behalf of the user.
UserManageProfile,
/// Manage stacks owned by the user.
UserManageStacks,
/// Manage the user's following/unfollowing.
UserManageRelationships,
/// Manage the user's community memberships.
///
/// Also includes managing the membership of users in the user's communities.
UserManageMemberships,
/// Follow/unfollow users on behalf of the user.
UserManageFollowing,
/// Accept follow requests on behalf of the user.
UserManageFollowers,
/// Block/unblock users on behalf of the user.
UserManageBlocks,
/// Manage the user's notifications.
UserManageNotifications,
/// Manage the user's requests.
UserManageRequests,
/// Manage the user's uploads.
UserManageUploads,
/// Manage the user's journals.
UserManageJournals,
/// Manage the user's notes.
UserManageNotes,
2025-07-02 20:14:04 -04:00
/// Manage the user's layouts.
UserManageLayouts,
/// Edit posts created by the user.
UserEditPosts,
/// Edit drafts created by the user.
UserEditDrafts,
/// Vote in polls as the user.
UserVote,
/// React to posts on behalf of the user. Also allows the removal of reactions.
UserReact,
/// Join communities on behalf of the user.
UserJoinCommunities,
/// Permanently delete posts.
ModPurgePosts,
/// Restore deleted posts.
ModDeletePosts,
/// Manage user warnings.
ModManageWarnings,
/// Get a list of all emojis available to the user.
UserReadEmojis,
/// Create emojis on behalf of the user.
CommunityCreateEmojis,
/// Manage emojis on behalf of the user.
CommunityManageEmojis,
/// Delete communities on behalf of the user.
CommunityDelete,
/// Manage communities on behalf of the user.
CommunityManage,
/// Transfer ownership of communities on behalf of the user.
CommunityTransferOwnership,
/// Read the membership of users in communities owned by the current user.
CommunityReadMemberships,
/// Create channels in the user's communities.
CommunityCreateChannels,
/// Manage channels in the user's communities.
CommunityManageChannels,
}
impl AuthGrant {
/// Check a verifier against the stored challenge (using the given [`PkceChallengeMethod`]).
pub fn check_verifier(&self, verifier: &str) -> Result<()> {
if self.method != PkceChallengeMethod::S256 {
return Err(Error::MiscError("only S256 is supported".to_string()));
}
2025-04-27 23:11:37 -04:00
let decoded = match base64url.decode(self.challenge.as_bytes()) {
Ok(hash) => hash,
Err(e) => return Err(Error::MiscError(e.to_string())),
};
2025-04-27 23:11:37 -04:00
let hash = hash(verifier.to_string());
2025-04-27 23:11:37 -04:00
if hash.as_bytes() != decoded {
// the verifier we received does not match the verifier from the stored challenge
return Err(Error::NotAllowed);
}
2025-04-27 23:11:37 -04:00
Ok(())
}
2025-04-27 23:11:37 -04:00
}