Skip to content

Commit

Permalink
Add Keys Endpoints (#527)
Browse files Browse the repository at this point in the history
Closes #526
  • Loading branch information
colinbobolin authored Sep 28, 2021
1 parent 95f5884 commit 4982478
Show file tree
Hide file tree
Showing 7 changed files with 301 additions and 1 deletion.
69 changes: 69 additions & 0 deletions src/Auth0.ManagementApi/Clients/KeysClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
using Auth0.ManagementApi.Models;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Auth0.ManagementApi.Models.Keys;

namespace Auth0.ManagementApi.Clients
{
/// <summary>
/// Contains methods to access the /keys endpoints.
/// </summary>
public class KeysClient : BaseClient
{
/// <summary>
/// Initializes a new instance of the <see cref="KeysClient"/> class.
/// </summary>
/// <param name="connection"><see cref="IManagementConnection"/> used to make all API calls.</param>
/// <param name="baseUri"><see cref="Uri"/> of the endpoint to use in making API calls.</param>
/// <param name="defaultHeaders">Dictionary containing default headers included with every request this client makes.</param>
public KeysClient(IManagementConnection connection, Uri baseUri, IDictionary<string, string> defaultHeaders)
: base(connection, baseUri, defaultHeaders)
{
}

/// <summary>
/// Get all Application Signing Keys
/// </summary>
/// <param name="cancellationToken">The cancellation token to cancel operation.</param>
/// <returns>All available signing keys <see cref="Key" />.</returns>
public Task<IList<Key>> GetAllAsync(CancellationToken cancellationToken = default)
{
return Connection.GetAsync<IList<Key>>(BuildUri("keys/signing"), DefaultHeaders, cancellationToken: cancellationToken);
}

/// <summary>
/// Get an Application Signing Key by its key ID.
/// </summary>
/// <param name="kid">The ID of the key to retrieve.</param>
/// <param name="cancellationToken">The cancellation token to cancel operation.</param>
/// <returns>The <see cref="Key"/> that was requested.</returns>
public Task<Key> GetAsync(string kid, CancellationToken cancellationToken = default)
{
return Connection.GetAsync<Key>(BuildUri($"keys/signing/{EncodePath(kid)}"), DefaultHeaders, cancellationToken: cancellationToken);
}

/// <summary>
/// Rotate the Application Signing Key.
/// </summary>
/// <param name="cancellationToken">The cancellation token to cancel operation.</param>
/// <returns>The next rotated key's cert and kid.</returns>
public Task<RotateSigningKeyResponse> RotateSigningKeyAsync( CancellationToken cancellationToken = default)
{
return Connection.SendAsync<RotateSigningKeyResponse>(HttpMethod.Post, BuildUri("keys/signing/rotate"), null, DefaultHeaders, cancellationToken: cancellationToken);
}

/// <summary>
/// Revoke an Application Signing Key by its key ID.
/// </summary>
/// <param name="kid">The ID of the key to revoke.</param>
/// <param name="cancellationToken">The cancellation token to cancel operation.</param>
/// <returns>The revoked key's cert and kid.</returns>
public Task<RevokeSigningKeyResponse> RevokeSigningKeyAsync(string kid, CancellationToken cancellationToken = default)
{
return Connection.SendAsync<RevokeSigningKeyResponse>(HttpMethod.Put, BuildUri($"keys/signing/{EncodePath(kid)}/revoke"), null, DefaultHeaders, cancellationToken: cancellationToken);
}
}
}
6 changes: 6 additions & 0 deletions src/Auth0.ManagementApi/ManagementApiClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,11 @@ public class ManagementApiClient : IDisposable
/// </summary>
public JobsClient Jobs { get; }

/// <summary>
/// Contains all the methods to call the /keys endpoints.
/// </summary>
public KeysClient Keys { get; }

/// <summary>
/// Contains all the methods to call the /logs endpoints.
/// </summary>
Expand Down Expand Up @@ -174,6 +179,7 @@ public ManagementApiClient(string token, Uri baseUri, IManagementConnection mana
Guardian = new GuardianClient(managementConnection, baseUri, defaultHeaders);
Hooks = new HooksClient(managementConnection, baseUri, defaultHeaders);
Jobs = new JobsClient(managementConnection, baseUri, defaultHeaders);
Keys = new KeysClient(managementConnection, baseUri, defaultHeaders);
Logs = new LogsClient(managementConnection, baseUri, defaultHeaders);
LogStreams = new LogStreamsClient(managementConnection, baseUri, defaultHeaders);
Prompts = new PromptsClient(managementConnection, baseUri, defaultHeaders);
Expand Down
84 changes: 84 additions & 0 deletions src/Auth0.ManagementApi/Models/Keys/Key.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
using System;
using Newtonsoft.Json;

namespace Auth0.ManagementApi.Models.Keys
{

/// <summary>
/// An Application Signing Key
/// </summary>
public class Key
{
/// <summary>
/// The key id of the signing key
/// </summary>
[JsonProperty("kid")]
public string Kid { get; set; }

/// <summary>
/// The public certificate of the signing key
/// </summary>
[JsonProperty("cert")]
public string Cert { get; set; }

/// <summary>
/// The public certificate of the signing key in pkcs7 format
/// </summary>
[JsonProperty("pkcs7")]
public string Pkcs7 { get; set; }

/// <summary>
/// True if the key is the the current key
/// </summary>
[JsonProperty("current")]
public bool? Current { get; set; }

/// <summary>
/// True if the key is the the next key
/// </summary>
[JsonProperty("next")]
public bool? Next { get; set; }

/// <summary>
/// True if the key is the the previous key
/// </summary>
[JsonProperty("previous")]
public bool? Previous { get; set; }

/// <summary>
/// The date and time when the key became the current key
/// </summary>
[JsonProperty("current_since")]
public DateTime? CurrentSince { get; set; }

/// <summary>
/// The date and time when the current key was rotated
/// </summary>
[JsonProperty("current_until")]
public DateTime? CurrentUntil { get; set; }

/// <summary>
/// The cert fingerprint
/// </summary>
[JsonProperty("fingerprint")]
public string Fingerprint { get; set; }

/// <summary>
/// The cert thumbprint
/// </summary>
[JsonProperty("thumbprint")]
public string Thumbprint { get; set; }

/// <summary>
/// True if the key is revoked
/// </summary>
[JsonProperty("revoked")]
public bool? Revoked { get; set; }

/// <summary>
/// The date and time when the key was revoked
/// </summary>
[JsonProperty("revoked_at")]
public DateTime? RevokedAt { get; set; }
}
}
19 changes: 19 additions & 0 deletions src/Auth0.ManagementApi/Models/Keys/RevokeSigningKeyResponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using Newtonsoft.Json;

namespace Auth0.ManagementApi.Models.Keys
{
public class RevokeSigningKeyResponse
{
/// <summary>
/// The id of the revoked signing key
/// </summary>
[JsonProperty("kid")]
public string Kid { get; set; }

/// <summary>
/// The public certificate of the revoked signing key
/// </summary>
[JsonProperty("cert")]
public string Cert { get; set; }
}
}
19 changes: 19 additions & 0 deletions src/Auth0.ManagementApi/Models/Keys/RotateSigningKeyResponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using Newtonsoft.Json;

namespace Auth0.ManagementApi.Models.Keys
{
public class RotateSigningKeyResponse
{
/// <summary>
/// The id of the next signing key
/// </summary>
[JsonProperty("kid")]
public string Kid { get; set; }

/// <summary>
/// The public certificate of the next signing key
/// </summary>
[JsonProperty("cert")]
public string Cert { get; set; }
}
}
1 change: 0 additions & 1 deletion src/Auth0.ManagementApi/Models/SigningKey.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ namespace Auth0.ManagementApi.Models
/// </summary>
public class SigningKey
{

/// <summary>
///
/// </summary>
Expand Down
104 changes: 104 additions & 0 deletions tests/Auth0.ManagementApi.IntegrationTests/KeysTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
using System.Linq;
using System.Threading.Tasks;
using Auth0.ManagementApi.Models;
using FluentAssertions;
using Xunit;
using Auth0.Tests.Shared;

namespace Auth0.ManagementApi.IntegrationTests
{
public class KeysTests : TestBase, IAsyncLifetime
{
private ManagementApiClient _apiClient;
private Connection _connection;
private const string Password = "4cX8awB3T%@Aw-R:=h@ae@k?";

public async Task InitializeAsync()
{
await GetTokenAndInitializeAsync();

// We will need a connection to add the roles to...
_connection = await _apiClient.Connections.CreateAsync(new ConnectionCreateRequest
{
Name = "Temp-Int-Test-" + MakeRandomName(),
Strategy = "auth0",
EnabledClients = new[] {GetVariable("AUTH0_CLIENT_ID"), GetVariable("AUTH0_MANAGEMENT_API_CLIENT_ID")}
});
}

private async Task GetTokenAndInitializeAsync()
{
string token = await GenerateManagementApiToken();

_apiClient = new ManagementApiClient(token, GetVariable("AUTH0_MANAGEMENT_API_URL"), new HttpClientManagementConnection(options: new HttpClientManagementConnectionOptions { NumberOfHttpRetries = 9 }));
}

public async Task DisposeAsync()
{
await _apiClient.Connections.DeleteAsync(_connection.Id);
_apiClient.Dispose();
}

[Fact]
public async Task Test_keys_can_be_retrieved()
{
var signingKeys = await _apiClient.Keys.GetAllAsync();

signingKeys.Any().Should().BeTrue();
}

[Fact]
public async Task Test_keys_can_be_retrieved_by_kid()
{
var signingKeys = await _apiClient.Keys.GetAllAsync();

// select the current key id
var currentKeyId = signingKeys.First(key => key.Current.HasValue && key.Current.Value).Kid;

// retrieve the key by id
var currentKey = await _apiClient.Keys.GetAsync(currentKeyId);

currentKey.Kid.Should().Be(currentKeyId);
}

[Fact(Skip = "Run Manual")]
public async Task Test_keys_rotate_signing_key()
{
// Rotate the signing key
var rotateKeyResponse = await _apiClient.Keys.RotateSigningKeyAsync();

await GetTokenAndInitializeAsync();

// Get all signing key
var signingKeys = await _apiClient.Keys.GetAllAsync();

// Select the next key
var nextKey = signingKeys.First(key => key.Next.HasValue && key.Next.Value);

// Assert
nextKey.Kid.Should().Be(rotateKeyResponse.Kid);
}


[Fact(Skip = "Run Manual")]
public async Task Test_keys_can_be_revoked_by_kid()
{
// Rotate the signing key before we revoke
var rotateKeyResponse = await _apiClient.Keys.RotateSigningKeyAsync();

await GetTokenAndInitializeAsync();

// Get all signing keys
var signingKeys = await _apiClient.Keys.GetAllAsync();

// Select the previous key id
var previousKeyId = signingKeys.First(key => key.Previous.HasValue && key.Previous.Value).Kid;

// Revoke the key by id
var revoked = await _apiClient.Keys.RevokeSigningKeyAsync(previousKeyId);

// Assert
revoked.Kid.Should().Be(previousKeyId);
}
}
}

0 comments on commit 4982478

Please sign in to comment.