TODO: add grant creation api, grant tab in sessions tab of settings, grant api endpoints
109 lines
4.1 KiB
Rust
109 lines
4.1 KiB
Rust
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 {
|
|
pub id: usize,
|
|
/// The name of the application associated with this grant.
|
|
pub name: String,
|
|
/// 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,
|
|
/// 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)]
|
|
pub enum PkceChallengeMethod {
|
|
S256,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
pub enum AppScope {
|
|
/// Read the user's profile (username, bio, etc).
|
|
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,
|
|
/// Create posts as the user.
|
|
UserCreatePosts,
|
|
/// Create messages as the user.
|
|
UserCreateMessages,
|
|
/// Delete posts owned by the user.
|
|
UserDeletePosts,
|
|
/// Delete messages owned by the user.
|
|
UserDeleteMessages,
|
|
/// Manage stacks owned by the user.
|
|
UserManageStacks,
|
|
/// Manage the user's following/unfollowing.
|
|
UserManageRelationships,
|
|
/// Manage the user's settings.
|
|
UserManageSettings,
|
|
}
|
|
|
|
impl AppScope {
|
|
/// Parse the given input string as a list of scopes.
|
|
pub fn parse(input: &str) -> Vec<AppScope> {
|
|
let mut out: Vec<AppScope> = Vec::new();
|
|
for scope in input.split(" ") {
|
|
out.push(match scope {
|
|
"user-read-profile" => Self::UserReadProfile,
|
|
"user-read-settings" => Self::UserReadSettings,
|
|
"user-read-sessions" => Self::UserReadSessions,
|
|
"user-read-posts" => Self::UserReadPosts,
|
|
"user-read-messages" => Self::UserReadMessages,
|
|
"user-create-posts" => Self::UserCreatePosts,
|
|
"user-create-messages" => Self::UserCreateMessages,
|
|
"user-delete-posts" => Self::UserDeletePosts,
|
|
"user-delete-messages" => Self::UserDeleteMessages,
|
|
"user-manage-stacks" => Self::UserManageStacks,
|
|
"user-manage-relationships" => Self::UserManageRelationships,
|
|
"user-manage-settings" => Self::UserManageSettings,
|
|
_ => continue,
|
|
})
|
|
}
|
|
out
|
|
}
|
|
}
|
|
|
|
/// Check a verifier against the stored challenge (using the given [`PkceChallengeMethod`]).
|
|
pub fn check_verifier(verifier: &str, challenge: &str, method: PkceChallengeMethod) -> Result<()> {
|
|
if method != PkceChallengeMethod::S256 {
|
|
return Err(Error::MiscError("only S256 is supported".to_string()));
|
|
}
|
|
|
|
let decoded = match base64url.decode(challenge.as_bytes()) {
|
|
Ok(hash) => hash,
|
|
Err(e) => return Err(Error::MiscError(e.to_string())),
|
|
};
|
|
|
|
let hash = hash(verifier.to_string());
|
|
|
|
if hash.as_bytes() != decoded {
|
|
// the verifier we received does not match the verifier from the stored challenge
|
|
return Err(Error::NotAllowed);
|
|
}
|
|
|
|
Ok(())
|
|
}
|