using System; using System.Text; namespace Discord { /// /// Provides a series of helper methods for handling Discord login tokens. /// public static class TokenUtils { /// /// The minimum length of a Bot token. /// /// /// This value was determined by comparing against the examples in the Discord /// documentation, and pre-existing tokens. /// internal const int MinBotTokenLength = 58; /// /// Checks the validity of a bot token by attempting to decode a ulong userid /// from the bot token. /// /// /// The bot token to validate. /// /// /// True if the bot token was valid, false if it was not. /// internal static bool CheckBotTokenValidity(string message) { if (string.IsNullOrWhiteSpace(message)) return false; // split each component of the JWT var segments = message.Split('.'); // ensure that there are three parts if (segments.Length != 3) return false; try { // decode the first segment as base64 var bytes = Convert.FromBase64String(segments[0]); var idStr = Encoding.UTF8.GetString(bytes); // discard id return ulong.TryParse(idStr, out var id); } catch (FormatException) { // ignore exception, if contains invalid base64 characters return false return false; } catch (ArgumentException) { // ignore exceptions thrown by BitConverter return false; } } /// /// Checks the validity of the supplied token of a specific type. /// /// The type of token to validate. /// The token value to validate. /// Thrown when the supplied token string is null, empty, or contains only whitespace. /// Thrown when the supplied or token value is invalid. public static void ValidateToken(TokenType tokenType, string token) { // A Null or WhiteSpace token of any type is invalid. if (string.IsNullOrWhiteSpace(token)) throw new ArgumentNullException(paramName: nameof(token), message: "A token cannot be null, empty, or contain only whitespace."); switch (tokenType) { case TokenType.Webhook: // no validation is performed on Webhook tokens break; case TokenType.Bearer: // no validation is performed on Bearer tokens break; case TokenType.Bot: // bot tokens are assumed to be at least 58 characters in length // this value was determined by referencing examples in the discord documentation, and by comparing with // pre-existing tokens if (token.Length < MinBotTokenLength) throw new ArgumentException(message: $"A Bot token must be at least {MinBotTokenLength} characters in length. " + "Ensure that the Bot Token provided is not an OAuth client secret.", paramName: nameof(token)); // check the validity of the bot token by decoding the ulong userid from the jwt if (!CheckBotTokenValidity(token)) throw new ArgumentException(message: "The Bot token was invalid. " + "Ensure that the Bot Token provided is not an OAuth client secret.", paramName: nameof(token)); break; default: // All unrecognized TokenTypes (including User tokens) are considered to be invalid. throw new ArgumentException(message: "Unrecognized TokenType.", paramName: nameof(token)); } } } }