Skip to content

Commit

Permalink
Generate sync AccessTokenProvider
Browse files Browse the repository at this point in the history
  • Loading branch information
AnaCoda committed Aug 21, 2023
2 parents 2fcba53 + 2022e1d commit 418a2e2
Show file tree
Hide file tree
Showing 20 changed files with 82 additions and 27 deletions.
2 changes: 1 addition & 1 deletion Directory.Packages.props
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project>
<ItemGroup>
<PackageVersion Include="BenchmarkDotNet" Version="0.13.1" />
<PackageVersion Include="D2L.CodeStyle.Analyzers" Version="0.196.0" />
<PackageVersion Include="D2L.CodeStyle.Analyzers" Version="0.203.0" />
<PackageVersion Include="D2L.CodeStyle.Annotations" Version="0.31.0" />
<PackageVersion Include="D2L.Services.Core.Exceptions" Version="2.0.1.15" />
<PackageVersion Include="FluentAssertions" Version="6.6.0" />
Expand Down
6 changes: 5 additions & 1 deletion src/D2L.Security.OAuth2/Caching/ICache.cs
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
using System;
using System.Threading.Tasks;
using D2L.CodeStyle.Annotations;

namespace D2L.Security.OAuth2.Caching {

/// <summary>
/// A generic (string-value-based) caching interface
/// </summary>
public interface ICache {
public partial interface ICache {

/// <summary>
/// Attempts to retrieve the value for <paramref name="key"/> from the cache
/// </summary>
/// <param name="key">The key of the value to retrieve</param>
/// <returns>For a cache hit, the value in the cache matching <paramref name="key"/>; for a cache miss, a <see cref="CacheResponse"/> with <see cref="CacheResponse.Success"/> set to false</returns>
[GenerateSync]
Task<CacheResponse> GetAsync(
string key
);
Expand All @@ -23,6 +25,7 @@ string key
/// <param name="key">The key of the <paramref name="value"/> to cache</param>
/// <param name="value">The value to cache</param>
/// <param name="expiry">The maximum time the value can live in the cache for</param>
[GenerateSync]
Task SetAsync(
string key,
string value,
Expand All @@ -33,6 +36,7 @@ TimeSpan expiry
/// Remove the item matching <paramref name="key"/> from the cache
/// </summary>
/// <param name="key">The key of the item to remove</param>
[GenerateSync]
Task RemoveAsync(
string key
);
Expand Down
18 changes: 14 additions & 4 deletions src/D2L.Security.OAuth2/Caching/NullCache.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,25 @@
using System;
using System.Threading.Tasks;
using D2L.CodeStyle.Annotations;

namespace D2L.Security.OAuth2.Caching {
internal sealed class NullCache : ICache {
internal sealed partial class NullCache : ICache {

private static readonly CacheResponse NULL_RESPONSE = new CacheResponse( success: false, value: null );

Task<CacheResponse> ICache.GetAsync( string key ) => Task.FromResult( NULL_RESPONSE );
[GenerateSync]
Task<CacheResponse> ICache.GetAsync( string key ) {
return Task.FromResult( NULL_RESPONSE );
}

Task ICache.SetAsync( string key, string value, TimeSpan expiry ) => Task.CompletedTask;
[GenerateSync]
Task ICache.SetAsync( string key, string value, TimeSpan expiry ) {
return Task.CompletedTask;
}

Task ICache.RemoveAsync( string key ) => Task.CompletedTask;
[GenerateSync]
Task ICache.RemoveAsync( string key ) {
return Task.CompletedTask;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using Microsoft.IdentityModel.Tokens;
using System.Security.Cryptography;
using System.Threading.Tasks;
using D2L.CodeStyle.Annotations;

namespace D2L.Security.OAuth2.Keys.Default {
internal sealed partial class EcDsaPrivateKeyProvider : IPrivateKeyProvider {
Expand All @@ -17,6 +18,7 @@ ECCurve curve
m_curve = curve;
}

[GenerateSync]
Task<D2LSecurityToken> IPrivateKeyProvider.GetSigningCredentialsAsync() {
var ecdsa = ECDsa.Create( m_curve );
var parameters = ecdsa.ExportParameters( includePrivateParameters: true );
Expand Down
4 changes: 3 additions & 1 deletion src/D2L.Security.OAuth2/Keys/Default/IPrivateKeyProvider.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
using System.Threading.Tasks;
using D2L.CodeStyle.Annotations;

namespace D2L.Security.OAuth2.Keys.Default {
internal interface IPrivateKeyProvider {
internal partial interface IPrivateKeyProvider {
[GenerateSync]
Task<D2LSecurityToken> GetSigningCredentialsAsync();
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using D2L.CodeStyle.Annotations;
using D2L.Security.OAuth2.Utilities;
using D2L.Services;

namespace D2L.Security.OAuth2.Keys.Default {
internal sealed class RotatingPrivateKeyProvider : IPrivateKeyProvider {
internal sealed partial class RotatingPrivateKeyProvider : IPrivateKeyProvider {

private readonly IPrivateKeyProvider m_inner;
private readonly IDateTimeProvider m_dateTimeProvider;
Expand All @@ -25,6 +26,7 @@ TimeSpan keyRotationPeriod
m_keyRotationPeriod = keyRotationPeriod;
}

[GenerateSync]
async Task<D2LSecurityToken> IPrivateKeyProvider.GetSigningCredentialsAsync() {

// Hold a local reference so that we know we are talking about the same key
Expand Down Expand Up @@ -61,4 +63,4 @@ TimeSpan keyRotationPeriod
return key == null || m_dateTimeProvider.UtcNow >= key.ValidTo - m_keyRotationPeriod;
}
}
}
}
2 changes: 2 additions & 0 deletions src/D2L.Security.OAuth2/Keys/Default/RsaPrivateKeyProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using Microsoft.IdentityModel.Tokens;
using System.Security.Cryptography;
using System.Threading.Tasks;
using D2L.CodeStyle.Annotations;

namespace D2L.Security.OAuth2.Keys.Default {
internal sealed partial class RsaPrivateKeyProvider : IPrivateKeyProvider {
Expand All @@ -14,6 +15,7 @@ ID2LSecurityTokenFactory d2lSecurityTokenFactory
m_d2lSecurityTokenFactory = d2lSecurityTokenFactory;
}

[GenerateSync]
Task<D2LSecurityToken> IPrivateKeyProvider.GetSigningCredentialsAsync() {
RSAParameters privateKey;
using( var csp = new RSACryptoServiceProvider( Constants.GENERATED_RSA_KEY_SIZE ) { PersistKeyInCsp = false } ) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
using System;
using System.Threading.Tasks;
using D2L.CodeStyle.Annotations;
using D2L.Services;

namespace D2L.Security.OAuth2.Keys.Default {
internal sealed class SavingPrivateKeyProvider : IPrivateKeyProvider {
internal sealed partial class SavingPrivateKeyProvider : IPrivateKeyProvider {
private readonly IPrivateKeyProvider m_inner;
private readonly IPublicKeyDataProvider m_publicKeyDataProvider;

Expand All @@ -15,6 +16,7 @@ ISanePublicKeyDataProvider publicKeyDataProvider
m_publicKeyDataProvider = publicKeyDataProvider;
}

[GenerateSync]
async Task<D2LSecurityToken> IPrivateKeyProvider.GetSigningCredentialsAsync() {
D2LSecurityToken result = await m_inner.GetSigningCredentialsAsync().ConfigureAwait( false );

Expand Down
4 changes: 3 additions & 1 deletion src/D2L.Security.OAuth2/Keys/Default/TokenSigner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using D2L.CodeStyle.Annotations;
using D2L.Security.OAuth2.Validation.Exceptions;

namespace D2L.Security.OAuth2.Keys.Default {
public sealed class TokenSigner : ITokenSigner {
public sealed partial class TokenSigner : ITokenSigner {

private readonly IPrivateKeyProvider m_privateKeyProvider;

Expand All @@ -19,6 +20,7 @@ IPrivateKeyProvider privateKeyProvider
m_privateKeyProvider = privateKeyProvider;
}

[GenerateSync]
async Task<string> ITokenSigner.SignAsync( UnsignedToken token ) {
JwtSecurityToken jwt;
using( D2LSecurityToken securityToken = await m_privateKeyProvider
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@
using System.Security.Cryptography;
using System.Threading.Tasks;
using D2L.Security.OAuth2.Keys.Default;
using D2L.CodeStyle.Annotations;

namespace D2L.Security.OAuth2.Keys.Development {

/// <summary>
/// A private key provider with a fixed key for testing purposes
/// </summary>
[Obsolete( "Only use this in tests and for prototyping." )]
internal sealed class StaticPrivateKeyProvider : IPrivateKeyProvider {
internal sealed partial class StaticPrivateKeyProvider : IPrivateKeyProvider {
private readonly string m_keyId;
private readonly RSAParameters m_rsaParameters;

Expand All @@ -22,6 +23,7 @@ RSAParameters rsaParameters
m_rsaParameters = rsaParameters;
}

[GenerateSync]
public Task<D2LSecurityToken> GetSigningCredentialsAsync() {
var creds = new D2LSecurityToken(
id: m_keyId,
Expand Down
4 changes: 3 additions & 1 deletion src/D2L.Security.OAuth2/Keys/IKeyManagementService.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
using System;
using System.Threading.Tasks;
using D2L.CodeStyle.Annotations;

namespace D2L.Security.OAuth2.Keys {
public interface IKeyManagementService {
public partial interface IKeyManagementService {
/// <summary>
/// Generates a new key (storing it's public and private keys) if the
/// existing ones are getting near their expiry.
Expand All @@ -19,6 +20,7 @@ public interface IKeyManagementService {
/// <returns>
/// The amount of time to wait before calling this again.
/// </returns>
[GenerateSync]
Task<TimeSpan> RefreshKeyAsync();
}
}
4 changes: 3 additions & 1 deletion src/D2L.Security.OAuth2/Keys/IPrivateKeyDataProvider.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using D2L.CodeStyle.Annotations;

namespace D2L.Security.OAuth2.Keys {
/// <summary>
/// An externally-implemented storage for private keys.
/// </summary>
public interface IPrivateKeyDataProvider {
public partial interface IPrivateKeyDataProvider {
/// <summary>
/// Save a private key.
/// </summary>
Expand All @@ -17,6 +18,7 @@ public interface IPrivateKeyDataProvider {
/// </summary>
/// <param name="validUntilAtLeast">The earliest possible expiry time toreturn (should be in the future.)</param>
/// <returns>All non-expired/expiring private keys</returns>
[GenerateSync]
Task<IEnumerable<PrivateKeyData>> GetAllAsync(
DateTimeOffset validUntilAtLeast
);
Expand Down
4 changes: 3 additions & 1 deletion src/D2L.Security.OAuth2/Keys/ITokenSigner.cs
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
using System.Threading.Tasks;
using D2L.CodeStyle.Annotations;

namespace D2L.Security.OAuth2.Keys {
/// <summary>
/// An abstraction for signing tokens
/// </summary>
public interface ITokenSigner {
public partial interface ITokenSigner {

/// <summary>
/// Signs a token
/// </summary>
/// <param name="token">The token to be signed</param>
/// <returns>The raw signed token as a <see cref="string"/></returns>
[GenerateSync]
Task<string> SignAsync( UnsignedToken token );
}
}
8 changes: 6 additions & 2 deletions src/D2L.Security.OAuth2/Keys/KeyManagementService.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using D2L.Security.OAuth2.Keys.Default;
using D2L.CodeStyle.Annotations;
using D2L.Security.OAuth2.Keys.Default;
using D2L.Security.OAuth2.Utilities;
using Microsoft.IdentityModel.Tokens;
using System;
Expand All @@ -8,7 +9,7 @@
using System.Threading.Tasks;

namespace D2L.Security.OAuth2.Keys {
public sealed class KeyManagementService : IKeyManagementService, IPrivateKeyProvider, IDisposable {
public sealed partial class KeyManagementService : IKeyManagementService, IPrivateKeyProvider, IDisposable {
private readonly IPublicKeyDataProvider m_publicKeys;
private readonly IPrivateKeyDataProvider m_privateKeys;
private readonly IDateTimeProvider m_clock;
Expand Down Expand Up @@ -44,6 +45,7 @@ OAuth2Configuration config
config.CheckSanity();
}

[GenerateSync]
async Task<D2LSecurityToken> IPrivateKeyProvider.GetSigningCredentialsAsync() {
var current = Volatile.Read( ref m_current );

Expand All @@ -65,6 +67,7 @@ await RefreshKeyAsync( now )
return current.Ref();
}

[GenerateSync]
async Task<TimeSpan> IKeyManagementService.RefreshKeyAsync() {
var now = m_clock.UtcNow;

Expand Down Expand Up @@ -149,6 +152,7 @@ await RefreshKeyAsync( now )
).ConfigureAwait( false );
}

[GenerateSync]
private async Task RefreshKeyAsync( DateTimeOffset now ) {
var keys = await m_privateKeys.GetAllAsync(
validUntilAtLeast: now
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using D2L.CodeStyle.Annotations;
using D2L.Security.OAuth2.Keys;
using D2L.Security.OAuth2.Scopes;
using D2L.Services;

namespace D2L.Security.OAuth2.Provisioning.Default {

internal sealed class AccessTokenProvider : INonCachingAccessTokenProvider {
internal sealed partial class AccessTokenProvider : INonCachingAccessTokenProvider {

private readonly IAuthServiceClient m_client;
private readonly ITokenSigner m_tokenSigner;
Expand All @@ -22,6 +23,7 @@ IAuthServiceClient authServiceClient
m_client = authServiceClient;
}

[GenerateSync]
Task<IAccessToken> INonCachingAccessTokenProvider.ProvisionAccessTokenAsync(
ClaimSet claimSet,
IEnumerable<Scope> scopes
Expand All @@ -30,6 +32,7 @@ IEnumerable<Scope> scopes
return @this.ProvisionAccessTokenAsync( claimSet.ToClaims(), scopes );
}

[GenerateSync]
async Task<IAccessToken> INonCachingAccessTokenProvider.ProvisionAccessTokenAsync(
IEnumerable<Claim> claimSet,
IEnumerable<Scope> scopes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using D2L.CodeStyle.Annotations;
using D2L.Security.OAuth2.Scopes;
using D2L.Security.OAuth2.Utilities;
using D2L.Services.Core.Exceptions;
Expand All @@ -25,7 +26,7 @@ namespace D2L.Security.OAuth2.Provisioning.Default {
"D2L0096:Aliasing attribute class names not supported",
Justification = "Newtonsoft.Json.JsonPropertyAttribute is aliased to match System.Text.Json.Serialization.JsonPropertyNameAttribute to reduce duplication below."
)]
internal sealed class AuthServiceClient : IAuthServiceClient {
internal sealed partial class AuthServiceClient : IAuthServiceClient {

private const string TOKEN_PATH = "connect/token";

Expand Down Expand Up @@ -74,6 +75,7 @@ Uri authEndpoint
/// The auth service could not be reached, or it did not respond with
/// a status code indicating success.
/// </exception>
[GenerateSync]
async Task<IAccessToken> IAuthServiceClient.ProvisionAccessTokenAsync(
string assertion,
IEnumerable<Scope> scopes
Expand All @@ -82,7 +84,7 @@ IEnumerable<Scope> scopes
HttpResponseMessage response = null;
try {
try {
response = await MakeRequest( requestBody ).ConfigureAwait( false );
response = await MakeRequestAsync( requestBody ).ConfigureAwait( false );
} catch( TaskCanceledException exception ) {
throw new AuthServiceException(
errorType: ServiceErrorType.Timeout,
Expand Down Expand Up @@ -158,7 +160,8 @@ IEnumerable<Scope> scopes
}
}

private Task<HttpResponseMessage> MakeRequest( string body ) {
[GenerateSync]
private Task<HttpResponseMessage> MakeRequestAsync( string body ) {
var request = new HttpRequestMessage( HttpMethod.Post, m_tokenProvisioningEndpoint );
request.Content = new StringContent( body, Encoding.UTF8, "application/x-www-form-urlencoded" );

Expand Down
Loading

0 comments on commit 418a2e2

Please sign in to comment.