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

103 lines
4 KiB
Rust
Raw Normal View History

2025-06-14 14:45:52 -04:00
use serde::{Deserialize, Serialize};
use tetratto_shared::{snow::Snowflake, unix_epoch_timestamp};
2025-06-14 20:26:54 -04:00
use crate::model::oauth::AppScope;
2025-06-14 14:45:52 -04:00
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub enum AppQuota {
/// The app is limited to 5 grants.
Limited,
/// The app is allowed to maintain an unlimited number of grants.
Unlimited,
}
impl Default for AppQuota {
fn default() -> Self {
Self::Limited
}
}
/// An app is required to request grants on user accounts.
///
/// Users must approve grants through a web portal.
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ThirdPartyApp {
pub id: usize,
pub created: usize,
/// The ID of the owner of the app.
pub owner: usize,
/// The name of the app.
pub title: String,
/// The URL of the app's homepage.
pub homepage: String,
/// The redirect URL for the app.
///
/// Upon accepting a grant request, the user will be redirected to this URL
/// with a query parameter named `token`, which should be saved by the app
/// for future authentication.
2025-06-14 20:26:54 -04:00
///
/// The developer dashboard lists the URL you should send users to in order to
/// create a grant on their account in the information section under the label
/// "Grant URL".
///
/// Any search parameters sent with your grant URL (such as an internal user ID)
/// will also be sent back when the user is redirected to your redirect URL.
///
/// You can use this behaviour to keep track of what user you should save the grant
/// token under.
///
/// 1. Redirect user to grant URL with their ID: `{grant_url}?my_app_user_id={id}`
/// 2. In your redirect endpoint, read that ID and the added `token` parameter to
/// store the `token` under the given `my_app_user_id`
///
/// The redirect URL will also have a `verifier` search parameter appended.
/// This verifier is required to refresh the grant's token (which is what is
/// used in the `Atto-Grant` cookie).
///
/// Tokens only last a week after they were generated (with the verifier),
/// but you can refresh them by sending a request to:
/// `{tetratto}/api/v1/auth/user/{user_id}/grants/{app_id}/refresh`.
///
/// Tetratto will generate the verifier and challenge for you. The challenge
/// is an SHA-256 hashed + base64 url encoded version of the verifier. This means
/// if the verifier doesn't match, it won't pass the challenge.
///
/// Requests to API endpoints using your grant token should be sent with a
/// cookie (in the `Cookie` header) named `Atto-Grant`. This cookie should
/// contain the token you received from either the initial connection,
/// or a token refresh.
2025-06-14 14:45:52 -04:00
pub redirect: String,
/// The app's quota status, which determines how many grants the app is allowed to maintain.
pub quota_status: AppQuota,
/// If the app is banned. A banned app cannot use any of its grants.
pub banned: bool,
/// The number of accepted grants the app maintains.
pub grants: usize,
2025-06-14 20:26:54 -04:00
/// The scopes used for every grant the app maintains.
///
/// These scopes are only cloned into **new** grants created for the app.
/// An app *cannot* change scopes and have them affect users who already have the
/// app connected. Users must delete the app's grant and authenticate it again
/// to update their scopes.
///
/// Your app should handle informing users when scopes change.
pub scopes: Vec<AppScope>,
2025-06-14 14:45:52 -04:00
}
impl ThirdPartyApp {
/// Create a new [`ThirdPartyApp`].
pub fn new(title: String, owner: usize, homepage: String, redirect: String) -> Self {
Self {
id: Snowflake::new().to_string().parse::<usize>().unwrap(),
2025-06-14 20:26:54 -04:00
created: unix_epoch_timestamp(),
2025-06-14 14:45:52 -04:00
owner,
title,
homepage,
redirect,
quota_status: AppQuota::Limited,
banned: false,
grants: 0,
2025-06-14 20:26:54 -04:00
scopes: Vec::new(),
2025-06-14 14:45:52 -04:00
}
}
}