serenity/utils/token.rs
1//! Utilities to parse and validate Discord tokens.
2
3use std::{fmt, str};
4
5/// Validates that a token is likely in a valid format.
6///
7/// This performs the following checks on a given token:
8/// - Is not empty;
9/// - Contains 3 parts (split by the period char `'.'`);
10/// - The second part of the token is at least 6 characters long;
11///
12/// # Examples
13///
14/// Validate that a token is valid and that a number of malformed tokens are actually invalid:
15///
16/// ```
17/// use serenity::utils::token::validate;
18///
19/// // ensure a valid token is in fact a valid format:
20/// assert!(validate("Mjg4NzYwMjQxMzYzODc3ODg4.C_ikow.j3VupLBuE1QWZng3TMGH0z_UAwg").is_ok());
21///
22/// assert!(validate("Mjg4NzYwMjQxMzYzODc3ODg4").is_err());
23/// assert!(validate("").is_err());
24/// ```
25///
26/// # Errors
27///
28/// Returns a [`InvalidToken`] when one of the above checks fail. The type of failure is not
29/// specified.
30pub fn validate(token: impl AsRef<str>) -> Result<(), InvalidToken> {
31 // Tokens can be preceded by "Bot " (that's how the Discord API expects them)
32 let mut parts = token.as_ref().trim_start_matches("Bot ").split('.');
33
34 let is_valid = parts.next().is_some_and(|p| !p.is_empty())
35 && parts.next().is_some_and(|p| !p.is_empty())
36 && parts.next().is_some_and(|p| !p.is_empty())
37 && parts.next().is_none();
38
39 if is_valid {
40 Ok(())
41 } else {
42 Err(InvalidToken)
43 }
44}
45
46/// Error that can be return by [`validate`].
47#[derive(Debug)]
48pub struct InvalidToken;
49
50impl std::error::Error for InvalidToken {}
51
52impl fmt::Display for InvalidToken {
53 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
54 f.write_str("The provided token was invalid")
55 }
56}