dotnet / dotnet/aspnetcore

Consider using URL safe base64 tokens for email verification instead of URL encoding

Open
#5,789 2 comments 5 reactions 0 assignees View on GitHub
affected-few area-identity bug severity-minor triaged
Dominant language
C#
Stars
38.4k
Forks
10.9k
Avg merge
2d 5h
Merged PRs (30d)
276

Description

As shown in this email verification example, it is required to URL encode the base64 email confirmation token before sending them in an email:

https://github.com/aspnet/Identity/blob/47060f5e529ee4b872d9fcf66774935dafb051fc/samples/IdentitySample.DefaultUI/Areas/Identity/Pages/Account/Register.cshtml.cs#L100

This works the majority of the time. However, at no fault of our own, there are email providers out there that will still re-encode `%` characters that appear in anchor tags with `%25`, resulting in invalid tokens.

For example, `==` (original b64 characters) → `%3D%3D` (correct) → `%253D%253D` (incorrect).

This is obviously a bug with the email client itself, but we have come across the case several times with multiple different email clients (for example, mailbox.org will do this).

Since this really effects everyone (though most may not have noticed), I would propose that the team here should consider using URL safe base64 strings by default within the framework. This would eliminate the need for us to manually create our own TokenProvider, which we've had to resort to today. URL safe base64 strings are used, for example, in JWT tokens as a standard. Here is some sample code that we use to achieve it:

public static string Base64UrlEncode(byte[] input)
{
var output = Convert.ToBase64String(input)
.Replace('+', '-')
.Replace('/', '_')
.Replace("=", string.Empty);
return output;
}

public static byte[] Base64UrlDecode(string input)
{
var output = input;
// 62nd char of encoding
output = output.Replace('-', '+');
// 63rd char of encoding
output = output.Replace('_', '/');
// Pad with trailing '='s
switch(output.Length % 4)
{
case 0:
// No pad chars in this case
break;
case 2:
// Two pad chars
output += "=="; break;
case 3:
// One pad char
output += "="; break;
default:
throw new InvalidOperationException("Illegal base64url string!");
}

// Standard base64 decoder
return Convert.FromBase64String(output);
}

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.