1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
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 {
/// 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,
/// 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)]
pub enum PkceChallengeMethod {
S256,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub enum AppScope {
/// Read the profile of other users on behalf of the user.
UserReadProfiles,
/// 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,
/// 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,
/// Read questions as the user.
UserReadQuestions,
/// Read the user's stacks.
UserReadStacks,
/// Read the user's layouts.
UserReadLayouts,
/// Read the user's domains.
UserReadDomains,
/// Read the user's services.
UserReadServices,
/// Read the user's letters.
UserReadLetters,
/// Read the user's products.
UserReadProducts,
/// Read guest logs as the user.
UserReadGuestLogs,
/// 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 layouts on behalf of the user.
UserCreateLayouts,
/// Create domains on behalf of the user.
UserCreateDomains,
/// Create services on behalf of the user.
UserCreateServices,
/// Create letters on behalf of the user.
UserCreateLetters,
/// 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 layouts.
UserManageLayouts,
/// Manage the user's domains.
UserManageDomains,
/// Manage the user's services.
UserManageServices,
/// Manage the user's channel mutes.
UserManageChannelMutes,
/// Manage the user's letters.
UserManageLetters,
/// Manage the user's guest logs.
UserManageGuestLogs,
/// 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()));
}
let decoded = match base64url.decode(self.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(())
}
}