From 3de38411c02910941f5c296d863c39524bac05d1 Mon Sep 17 00:00:00 2001 From: Marc Becker Date: Tue, 1 Sep 2026 17:23:20 +0200 Subject: [PATCH] oauth: use native Base64Url encoder replace Base64Url converter with `System.Buffers.Text.Base64Url` drop requirement to specify padding, was always `false` --- .../OAuth2CryptographicCodeGeneratorTests.cs | 3 +- src/Core.Tests/Base64UrlConvertTests.cs | 35 ------------------- .../OAuth/OAuth2CryptographicGenerator.cs | 11 +++--- src/Core/Base64UrlConvert.cs | 25 ------------- .../Objects/TestOAuth2Server.cs | 6 ++-- 5 files changed, 9 insertions(+), 71 deletions(-) delete mode 100644 src/Core.Tests/Base64UrlConvertTests.cs delete mode 100644 src/Core/Base64UrlConvert.cs diff --git a/src/Core.Tests/Authentication/OAuth2CryptographicCodeGeneratorTests.cs b/src/Core.Tests/Authentication/OAuth2CryptographicCodeGeneratorTests.cs index 367c4cdd76..2eb1ef7440 100644 --- a/src/Core.Tests/Authentication/OAuth2CryptographicCodeGeneratorTests.cs +++ b/src/Core.Tests/Authentication/OAuth2CryptographicCodeGeneratorTests.cs @@ -1,4 +1,5 @@ using System.Linq; +using System.Buffers.Text; using System.Security.Cryptography; using System.Text; using GitCredentialManager.Authentication.OAuth; @@ -78,7 +79,7 @@ public void OAuth2CryptographicCodeGenerator_CreatePkceCodeChallenge_Sha256_Retu hashedBytes = sha256.ComputeHash(verifierAsciiBytes); } - var expectedChallenge = Base64UrlConvert.Encode(hashedBytes, false); + var expectedChallenge = Base64Url.EncodeToString(hashedBytes); var actualChallenge = generator.CreatePkceCodeChallenge(OAuth2PkceChallengeMethod.Sha256, verifier); Assert.Equal(expectedChallenge, actualChallenge); diff --git a/src/Core.Tests/Base64UrlConvertTests.cs b/src/Core.Tests/Base64UrlConvertTests.cs deleted file mode 100644 index f9728f8acc..0000000000 --- a/src/Core.Tests/Base64UrlConvertTests.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Xunit; - -namespace GitCredentialManager.Tests -{ - public class Base64UrlConvertTests - { - [Theory] - [InlineData(new byte[0], "")] - [InlineData(new byte[]{4}, "BA==")] - [InlineData(new byte[]{4,5}, "BAU=")] - [InlineData(new byte[]{4,5,6}, "BAUG")] - [InlineData(new byte[]{4,5,6,7}, "BAUGBw==")] - [InlineData(new byte[]{4,5,6,7,8}, "BAUGBwg=")] - [InlineData(new byte[]{4,5,6,7,8,9}, "BAUGBwgJ")] - public void Base64UrlConvert_Encode_WithPadding(byte[] data, string expected) - { - string actual = Base64UrlConvert.Encode(data, includePadding: true); - Assert.Equal(expected, actual); - } - - [Theory] - [InlineData(new byte[0], "")] - [InlineData(new byte[]{4}, "BA")] - [InlineData(new byte[]{4,5}, "BAU")] - [InlineData(new byte[]{4,5,6}, "BAUG")] - [InlineData(new byte[]{4,5,6,7}, "BAUGBw")] - [InlineData(new byte[]{4,5,6,7,8}, "BAUGBwg")] - [InlineData(new byte[]{4,5,6,7,8,9}, "BAUGBwgJ")] - public void Base64UrlConvert_Encode_WithoutPadding(byte[] data, string expected) - { - string actual = Base64UrlConvert.Encode(data, includePadding: false); - Assert.Equal(expected, actual); - } - } -} diff --git a/src/Core/Authentication/OAuth/OAuth2CryptographicGenerator.cs b/src/Core/Authentication/OAuth/OAuth2CryptographicGenerator.cs index a492c1461c..9b997b117f 100644 --- a/src/Core/Authentication/OAuth/OAuth2CryptographicGenerator.cs +++ b/src/Core/Authentication/OAuth/OAuth2CryptographicGenerator.cs @@ -1,4 +1,5 @@ using System; +using System.Buffers.Text; using System.Security.Cryptography; using System.Text; @@ -35,9 +36,6 @@ public interface IOAuth2CodeGenerator public class OAuth2CryptographicCodeGenerator : IOAuth2CodeGenerator { - // Do not include padding of the base64url string to avoid percent-encoding when passed in a URI - private const bool PkceIncludeBase64UrlPadding = false; - public string CreateNonce() { return Guid.NewGuid().ToString("N"); @@ -72,7 +70,7 @@ public string CreatePkceCodeVerifier() var rng = RandomNumberGenerator.Create(); rng.GetBytes(buf); - return Base64UrlConvert.Encode(buf, PkceIncludeBase64UrlPadding); + return Base64Url.EncodeToString(buf); } public string CreatePkceCodeChallenge(OAuth2PkceChallengeMethod challengeMethod, string codeVerifier) @@ -89,11 +87,10 @@ public string CreatePkceCodeChallenge(OAuth2PkceChallengeMethod challengeMethod, // using (var sha256 = SHA256.Create()) { - return Base64UrlConvert.Encode( + return Base64Url.EncodeToString( sha256.ComputeHash( Encoding.ASCII.GetBytes(codeVerifier) - ), - PkceIncludeBase64UrlPadding + ) ); } diff --git a/src/Core/Base64UrlConvert.cs b/src/Core/Base64UrlConvert.cs deleted file mode 100644 index 7b2fce0357..0000000000 --- a/src/Core/Base64UrlConvert.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; - -namespace GitCredentialManager -{ - public static class Base64UrlConvert - { - public static string Encode(byte[] data, bool includePadding = true) - { - const char base64PadCharacter = '='; - const char base64Character62 = '+'; - const char base64Character63 = '/'; - const char base64UrlCharacter62 = '-'; - const char base64UrlCharacter63 = '_'; - - // The base64url format is the same as regular base64 format except: - // 1. character 62 is "-" (minus) not "+" (plus) - // 2. character 63 is "_" (underscore) not "/" (slash) - string base64Url = Convert.ToBase64String(data) - .Replace(base64Character62, base64UrlCharacter62) - .Replace(base64Character63, base64UrlCharacter63); - - return includePadding ? base64Url : base64Url.TrimEnd(base64PadCharacter); - } - } -} diff --git a/src/TestInfrastructure/Objects/TestOAuth2Server.cs b/src/TestInfrastructure/Objects/TestOAuth2Server.cs index d72ceb5215..f1188e1724 100644 --- a/src/TestInfrastructure/Objects/TestOAuth2Server.cs +++ b/src/TestInfrastructure/Objects/TestOAuth2Server.cs @@ -1,4 +1,5 @@ using System; +using System.Buffers.Text; using System.Collections.Generic; using System.Linq; using System.Net; @@ -421,11 +422,10 @@ public TokenEndpointResponseJson CreateTokenByAuthorizationGrant( case OAuth2PkceChallengeMethod.Sha256: using (var sha256 = SHA256.Create()) { - string challenge = Base64UrlConvert.Encode( + string challenge = Base64Url.EncodeToString( sha256.ComputeHash( Encoding.ASCII.GetBytes(codeVerifier) - ), - false + ) ); if (challenge != grant.CodeChallenge)