Add validation to bot tokens based on string length (#1128)

* Add input validation for bot tokens based on their length

* Add token validation to BaseDiscordClient#LoginAsync

Adds a TokenUtils class which is used to validate that tokens are correct

* Revert changes to DiscordRestApiClient

* Add Unit tests to the TokenUtils class, fix a logic error that was caught by those tests

* Allow for API to throw exceptions

Moves the validation of tokens to be inside of LoginInternalAsync, and writes a Warning to the console when the supplied tokens are invalid
This commit is contained in:
Chris Johnston
2018-08-30 14:27:37 -07:00
committed by Christopher F
parent efdb4f9266
commit 2de6cef18c
3 changed files with 187 additions and 2 deletions

View File

@@ -0,0 +1,46 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Discord
{
public static class TokenUtils
{
/// <summary>
/// Checks the validity of the supplied token of a specific type.
/// </summary>
/// <param name="tokenType"> The type of token to validate. </param>
/// <param name="token"> The token value to validate. </param>
/// <exception cref="ArgumentNullException"> Thrown when the supplied token string is null, empty, or contains only whitespace.</exception>
/// <exception cref="ArgumentException"> Thrown when the supplied TokenType or token value is invalid. </exception>
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("A token cannot be null, empty, or contain only whitespace.", nameof(token));
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 59 characters in length
// this value was determined by referencing examples in the discord documentation, and by comparing with
// pre-existing tokens
if (token.Length < 59)
throw new ArgumentException("A Bot token must be at least 59 characters in length.", nameof(token));
break;
default:
// All unrecognized TokenTypes (including User tokens) are considered to be invalid.
throw new ArgumentException("Unrecognized TokenType.", nameof(token));
}
}
}
}

View File

@@ -55,11 +55,11 @@ namespace Discord.Rest
await _stateLock.WaitAsync().ConfigureAwait(false);
try
{
await LoginInternalAsync(tokenType, token).ConfigureAwait(false);
await LoginInternalAsync(tokenType, token, validateToken).ConfigureAwait(false);
}
finally { _stateLock.Release(); }
}
private async Task LoginInternalAsync(TokenType tokenType, string token)
private async Task LoginInternalAsync(TokenType tokenType, string token, bool validateToken)
{
if (_isFirstLogin)
{
@@ -73,6 +73,21 @@ namespace Discord.Rest
try
{
// If token validation is enabled, validate the token and let it throw any ArgumentExceptions
// that result from invalid parameters
if (validateToken)
{
try
{
TokenUtils.ValidateToken(tokenType, token);
}
catch (ArgumentException ex)
{
// log these ArgumentExceptions and allow for the client to attempt to log in anyways
await LogManager.WarningAsync("Discord", "A supplied token was invalid", ex).ConfigureAwait(false);
}
}
await ApiClient.LoginAsync(tokenType, token).ConfigureAwait(false);
await OnLoginAsync(tokenType, token).ConfigureAwait(false);
LoginState = LoginState.LoggedIn;