diff --git a/src/Discord.Net.Core/Utils/TokenUtils.cs b/src/Discord.Net.Core/Utils/TokenUtils.cs
index b52ba3dd6..2efb1822a 100644
--- a/src/Discord.Net.Core/Utils/TokenUtils.cs
+++ b/src/Discord.Net.Core/Utils/TokenUtils.cs
@@ -119,6 +119,25 @@ namespace Discord
return DecodeBase64UserId(segments[0]).HasValue;
}
+ ///
+ /// The set of all characters that are not allowed inside of a token.
+ ///
+ internal static char[] IllegalTokenCharacters = new char[]
+ {
+ ' ', '\t', '\r', '\n'
+ };
+
+ ///
+ /// Checks if the given token contains a whitespace or newline character
+ /// that would fail to log in.
+ ///
+ /// The token to validate.
+ ///
+ /// True if the token contains a whitespace or newline character.
+ ///
+ internal static bool CheckContainsIllegalCharacters(string token)
+ => token.IndexOfAny(IllegalTokenCharacters) != -1;
+
///
/// Checks the validity of the supplied token of a specific type.
///
@@ -131,6 +150,9 @@ namespace Discord
// 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.");
+ // ensure that there are no whitespace or newline characters
+ if (CheckContainsIllegalCharacters(token))
+ throw new ArgumentException(message: "The token contains a whitespace or newline character. Ensure that the token has been properly trimmed.", paramName: nameof(token));
switch (tokenType)
{
diff --git a/test/Discord.Net.Tests/Tests.TokenUtils.cs b/test/Discord.Net.Tests/Tests.TokenUtils.cs
index d9ed60ae8..428323c1d 100644
--- a/test/Discord.Net.Tests/Tests.TokenUtils.cs
+++ b/test/Discord.Net.Tests/Tests.TokenUtils.cs
@@ -99,6 +99,16 @@ namespace Discord
[InlineData("937it3ow87i4ery69876wqire")]
// 57 char bot token
[InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kK")]
+ // ends with invalid characters
+ [InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7k ")]
+ [InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7k\n")]
+ [InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7k\t")]
+ [InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7k\r\n")]
+ // starts with invalid characters
+ [InlineData(" MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7k")]
+ [InlineData("\nMTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7k")]
+ [InlineData("\tMTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7k")]
+ [InlineData("\r\nMTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7k")]
[InlineData("This is an invalid token, but it passes the check for string length.")]
// valid token, but passed in twice
[InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kKWsMTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kKWs")]