From 7d1664e5188323b3bd74bcb413dc082af2c4af64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Chalet?= Date: Wed, 26 Aug 2020 21:15:08 +0200 Subject: [PATCH] Update the ConvertReferenceUserCode handler to generate a new user code when a collision is detected --- .../OpenIddictServerHandlers.cs | 46 +++++++++++++------ 1 file changed, 31 insertions(+), 15 deletions(-) diff --git a/src/OpenIddict.Server/OpenIddictServerHandlers.cs b/src/OpenIddict.Server/OpenIddictServerHandlers.cs index 9630b2d0..550683b0 100644 --- a/src/OpenIddict.Server/OpenIddictServerHandlers.cs +++ b/src/OpenIddict.Server/OpenIddictServerHandlers.cs @@ -3957,20 +3957,6 @@ namespace OpenIddict.Server // only compound of 12 digits, generated using a crypto-secure random number generator. // In this case, the resulting user code is estimated to have at most ~40 bits of entropy. - var data = new byte[12]; -#if SUPPORTS_STATIC_RANDOM_NUMBER_GENERATOR_METHODS - RandomNumberGenerator.Fill(data); -#else - using var generator = RandomNumberGenerator.Create(); - generator.GetBytes(data); -#endif - var builder = new StringBuilder(data.Length); - - for (var index = 0; index < data.Length; index += 4) - { - builder.AppendFormat(CultureInfo.InvariantCulture, "{0:D4}", BitConverter.ToUInt32(data, index) % 10000); - } - var descriptor = new OpenIddictTokenDescriptor(); await _tokenManager.PopulateAsync(descriptor, token); @@ -3978,13 +3964,43 @@ namespace OpenIddict.Server // and replace the returned token by the reference identifier. descriptor.Payload = context.Response.UserCode; descriptor.Principal = principal; - descriptor.ReferenceId = builder.ToString(); + descriptor.ReferenceId = await GenerateReferenceIdentifierAsync(_tokenManager); await _tokenManager.UpdateAsync(token, descriptor); context.Response.UserCode = descriptor.ReferenceId; context.Logger.LogTrace(SR.GetResourceString(SR.ID7027), identifier, descriptor.ReferenceId); + + static async ValueTask GenerateReferenceIdentifierAsync(IOpenIddictTokenManager manager) + { + string token; + + do + { + var data = new byte[12]; +#if SUPPORTS_STATIC_RANDOM_NUMBER_GENERATOR_METHODS + RandomNumberGenerator.Fill(data); +#else + using var generator = RandomNumberGenerator.Create(); + generator.GetBytes(data); +#endif + var builder = new StringBuilder(data.Length); + + for (var index = 0; index < data.Length; index += 4) + { + builder.AppendFormat(CultureInfo.InvariantCulture, "{0:D4}", BitConverter.ToUInt32(data, index) % 10000); + } + + token = builder.ToString(); + } + + // User codes are relatively short. To help reduce the risks of collisions with + // existing entries, a database check is performed here before updating the entry. + while (await manager.FindByReferenceIdAsync(token) != null); + + return token; + } } }