Skip to content

Commit

Permalink
Adds support for session management and refresh-tokens (#737)
Browse files Browse the repository at this point in the history
  • Loading branch information
kailash-b authored Oct 23, 2024
2 parents 93dd21d + bb6c6e6 commit 9ea28be
Show file tree
Hide file tree
Showing 31 changed files with 951 additions and 7 deletions.
3 changes: 3 additions & 0 deletions src/Auth0.ManagementApi/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
using System.Runtime.CompilerServices;

[assembly: InternalsVisibleTo("Auth0.ManagementApi.IntegrationTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100453e57bfa7549abf0f1775df9384d2f279d25c2ab4c78d5a69d7e6da9567d2b984da533229a0d530a3b75c7f5a12c341799b448102995b8a123d1288aa12ca3c1c354c3da97e64626d1223ca7c6e95cba845bce6edcee8b326c2cd015cc84995e5b630ef5c7fa69928dea64a53ee71a493267de7e18d0e9f31e1e00bb8e01cae")]
26 changes: 26 additions & 0 deletions src/Auth0.ManagementApi/Clients/IRefreshTokenClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using System.Threading;
using System.Threading.Tasks;

using Auth0.ManagementApi.Models.RefreshTokens;

namespace Auth0.ManagementApi.Clients
{
public interface IRefreshTokenClient
{
/// <summary>
/// Retrieve refresh token information.
/// </summary>
/// <param name="request"><see cref="RefreshTokenGetRequest"/></param>
/// <param name="cancellationToken"> <see cref="CancellationToken"/></param>
/// <returns></returns>
Task<RefreshTokenInformation> GetAsync(RefreshTokenGetRequest request, CancellationToken cancellationToken = default);

/// <summary>
/// Delete a refresh token by its Id.
/// </summary>
/// <param name="id">Id of the refresh token to delete.</param>
/// <param name="cancellationToken"> <see cref="CancellationToken"/></param>
/// <returns></returns>
Task DeleteAsync(string id, CancellationToken cancellationToken = default);
}
}
33 changes: 33 additions & 0 deletions src/Auth0.ManagementApi/Clients/ISessionsClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using System.Threading;
using System.Threading.Tasks;
using Auth0.ManagementApi.Models.Sessions;

namespace Auth0.ManagementApi.Clients
{
public interface ISessionsClient
{
/// <summary>
/// Retrieve session information.
/// </summary>
/// <param name="request"><see cref="SessionsGetRequest"/></param>
/// <param name="cancellationToken"> <see cref="CancellationToken"/></param>
/// <returns></returns>
Task<Sessions> GetAsync(SessionsGetRequest request, CancellationToken cancellationToken = default);

/// <summary>
/// Delete a session by Id.
/// </summary>
/// <param name="id">Id of the session to delete.</param>
/// <param name="cancellationToken"> <see cref="CancellationToken"/></param>
/// <returns></returns>
Task DeleteAsync(string id, CancellationToken cancellationToken = default);

/// <summary>
/// Revokes a session by Id and all associated refresh tokens.
/// </summary>
/// <param name="id">Id of the session to revoke</param>
/// <param name="cancellationToken"> <see cref="CancellationToken"/></param>
/// <returns></returns>
Task RevokeAsync(string id, CancellationToken cancellationToken = default);
}
}
40 changes: 40 additions & 0 deletions src/Auth0.ManagementApi/Clients/IUsersClient.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@


namespace Auth0.ManagementApi.Clients
{
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Auth0.ManagementApi.Models.Users;
using Auth0.ManagementApi.Models.RefreshTokens;
using Auth0.ManagementApi.Models.Sessions;
using Models;
using Paging;

Expand Down Expand Up @@ -267,5 +271,41 @@ public interface IUsersClient
/// <param name="cancellationToken">The cancellation token to cancel operation.</param>
/// <returns>A <see cref="Task"/> that represents the asynchronous delete operation.</returns>
Task DeleteAuthenticationMethodAsync(string userId, string authenticationMethodId, CancellationToken cancellationToken = default);

/// <summary>
/// Retrieve details for a user's refresh tokens.
/// </summary>
/// <param name="request"><see cref="UserRefreshTokensGetRequest"/></param>
/// <param name="pagination"><see cref="CheckpointPaginationInfo"/></param>
/// <param name="cancellationToken"><see cref="CancellationToken"/></param>
/// <returns>Collection of <see cref="RefreshTokenInformation"/></returns>
Task<ICheckpointPagedList<RefreshTokenInformation>> GetRefreshTokensAsync(
UserRefreshTokensGetRequest request, CheckpointPaginationInfo pagination, CancellationToken cancellationToken = default);

/// <summary>
/// Delete all refresh tokens for a user.
/// </summary>
/// <param name="userId">ID of the user to remove refresh tokens for</param>
/// <param name="cancellationToken"><see cref="CancellationToken"/></param>
/// <returns></returns>
Task DeleteRefreshTokensAsync(string userId, CancellationToken cancellationToken = default);

/// <summary>
/// Retrieve details for a user's sessions.
/// </summary>
/// <param name="request"><see cref="UserSessionsGetRequest"/></param>
/// <param name="pagination"><see cref="CheckpointPaginationInfo"/></param>
/// <param name="cancellationToken"><see cref="CancellationToken"/></param>
/// <returns>Collection of <see cref="Sessions"/></returns>
Task<ICheckpointPagedList<Sessions>> GetUserSessionsAsync(
UserSessionsGetRequest request, CheckpointPaginationInfo pagination, CancellationToken cancellationToken = default);

/// <summary>
/// Delete all sessions for a user.
/// </summary>
/// <param name="userId">ID of the user to remove sessions for</param>
/// <param name="cancellationToken"><see cref="CancellationToken"/></param>
/// <returns></returns>
Task DeleteSessionsAsync(string userId, CancellationToken cancellationToken = default);
}
}
50 changes: 50 additions & 0 deletions src/Auth0.ManagementApi/Clients/RefreshTokenClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Auth0.ManagementApi.Models.RefreshTokens;

namespace Auth0.ManagementApi.Clients
{

/// <inheritdoc cref="Auth0.ManagementApi.Clients.IRefreshTokenClient"/>
public class RefreshTokenClient : BaseClient, IRefreshTokenClient
{
private const string RefreshTokensBasePath = "refresh-tokens";

/// <summary>
/// Initializes a new instance on <see cref="RefreshTokenClient"/>
/// </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 RefreshTokenClient(IManagementConnection connection, Uri baseUri, IDictionary<string, string> defaultHeaders)
: base(connection, baseUri, defaultHeaders)
{
}

/// <inheritdoc cref="Auth0.ManagementApi.Clients.IRefreshTokenClient.GetAsync"/>
public Task<RefreshTokenInformation> GetAsync(RefreshTokenGetRequest request, CancellationToken cancellationToken = default)
{
if (request == null)
throw new ArgumentNullException(nameof(request));

if(string.IsNullOrEmpty(request.Id))
throw new ArgumentException("Value cannot be null or empty.", nameof(request.Id));

return Connection.GetAsync<RefreshTokenInformation>(
BuildUri($"{RefreshTokensBasePath}/{EncodePath(request.Id)}"),
DefaultHeaders, cancellationToken: cancellationToken);
}

/// <inheritdoc cref="Auth0.ManagementApi.Clients.IRefreshTokenClient.DeleteAsync"/>
public Task DeleteAsync(string id, CancellationToken cancellationToken = default)
{
return Connection.SendAsync<object>(
HttpMethod.Delete,
BuildUri($"{RefreshTokensBasePath}/{EncodePath(id)}"),
null, DefaultHeaders, cancellationToken: cancellationToken);
}
}
}
59 changes: 59 additions & 0 deletions src/Auth0.ManagementApi/Clients/SessionsClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Auth0.ManagementApi.Models.Sessions;

namespace Auth0.ManagementApi.Clients
{

/// <inheritdoc cref="Auth0.ManagementApi.Clients.ISessionsClient"/>
public class SessionsClient : BaseClient, ISessionsClient
{
private const string SessionsBasePath = "sessions";

/// <summary>
/// Initializes a new instance of <see cref="SessionsClient"/>.
/// </summary>
/// <param name="connection"></param>
/// <param name="baseUri"></param>
/// <param name="defaultHeaders"></param>
public SessionsClient(IManagementConnection connection, Uri baseUri, IDictionary<string, string> defaultHeaders)
: base(connection, baseUri, defaultHeaders)
{
}

/// <inheritdoc cref="Auth0.ManagementApi.Clients.ISessionsClient.GetAsync"/>
public Task<Sessions> GetAsync(SessionsGetRequest request, CancellationToken cancellationToken = default)
{
if (request == null)
throw new ArgumentNullException(nameof(request));

if(string.IsNullOrEmpty(request.Id))
throw new ArgumentException("Value cannot be null or empty.", nameof(request.Id));

return Connection.GetAsync<Sessions>(
BuildUri($"{SessionsBasePath}/{EncodePath(request.Id)}"),
DefaultHeaders, cancellationToken: cancellationToken);
}

/// <inheritdoc cref="Auth0.ManagementApi.Clients.ISessionsClient.DeleteAsync"/>
public Task DeleteAsync(string id, CancellationToken cancellationToken = default)
{
return Connection.SendAsync<object>(
HttpMethod.Delete,
BuildUri($"{SessionsBasePath}/{EncodePath(id)}"),
null, DefaultHeaders, cancellationToken: cancellationToken);
}

/// <inheritdoc cref="Auth0.ManagementApi.Clients.ISessionsClient.RevokeAsync"/>
public Task RevokeAsync(string id, CancellationToken cancellationToken = default)
{
return Connection.SendAsync<Task>(HttpMethod.Post,
BuildUri($"{SessionsBasePath}/{EncodePath(id)}/revoke"),
null,
DefaultHeaders, cancellationToken: cancellationToken);
}
}
}
68 changes: 68 additions & 0 deletions src/Auth0.ManagementApi/Clients/UsersClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Auth0.ManagementApi.Models.RefreshTokens;
using Auth0.ManagementApi.Models.Sessions;

namespace Auth0.ManagementApi.Clients
{
Expand All @@ -21,6 +23,8 @@ public class UsersClient : BaseClient, IUsersClient
readonly JsonConverter[] permissionsConverters = new JsonConverter[] { new PagedListConverter<UserPermission>("permissions") };
readonly JsonConverter[] organizationsConverters = new JsonConverter[] { new PagedListConverter<Organization>("organizations") };
readonly JsonConverter[] authenticationMethodConverters = new JsonConverter[] { new PagedListConverter<AuthenticationMethod>("authenticators") };
readonly JsonConverter[] refreshTokensConverter = new JsonConverter[] { new CheckpointPagedListConverter<RefreshTokenInformation>("tokens") };
readonly JsonConverter[] sessionsConverter = new JsonConverter[] { new CheckpointPagedListConverter<Sessions>("sessions") };

/// <summary>
/// Initializes a new instance of <see cref="UsersClient"/>.
Expand Down Expand Up @@ -471,5 +475,69 @@ public Task DeleteAuthenticationMethodAsync(string userId, string authentication
{
return Connection.SendAsync<object>(HttpMethod.Delete, BuildUri($"users/{EncodePath(userId)}/authentication-methods/{EncodePath(authenticationMethodId)}"), null, DefaultHeaders, cancellationToken: cancellationToken);
}

/// <inheritdoc cref="IUsersClient.GetRefreshTokensAsync"/>
public Task<ICheckpointPagedList<RefreshTokenInformation>> GetRefreshTokensAsync(UserRefreshTokensGetRequest request, CheckpointPaginationInfo pagination, CancellationToken cancellationToken = default)
{
if (request == null)
throw new ArgumentNullException(nameof(request));

if(string.IsNullOrEmpty(request.UserId))
throw new ArgumentException("Value cannot be null or empty.", nameof(request.UserId));

var queryStrings = new Dictionary<string, string>
{
{"from", pagination.From},
{"take", pagination.Take.ToString()},
};

return Connection.GetAsync<ICheckpointPagedList<RefreshTokenInformation>>(
BuildUri($"users/{EncodePath(request.UserId)}/refresh-tokens", queryStrings),
DefaultHeaders,
refreshTokensConverter, cancellationToken: cancellationToken);
}

/// <inheritdoc cref="IUsersClient.DeleteRefreshTokensAsync"/>
public Task DeleteRefreshTokensAsync(string userId, CancellationToken cancellationToken = default)
{
return Connection.SendAsync<object>(
HttpMethod.Delete,
BuildUri($"users/{EncodePath(userId)}/refresh-tokens"),
null,
DefaultHeaders, cancellationToken: cancellationToken);
}

/// <inheritdoc cref="IUsersClient.GetUserSessionsAsync"/>
public Task<ICheckpointPagedList<Sessions>> GetUserSessionsAsync(
UserSessionsGetRequest request, CheckpointPaginationInfo pagination,
CancellationToken cancellationToken = default)
{
if (request == null)
throw new ArgumentNullException(nameof(request));

if(string.IsNullOrEmpty(request.UserId))
throw new ArgumentException("Value cannot be null or empty.", nameof(request.UserId));

var queryStrings = new Dictionary<string, string>
{
{"from", pagination.From},
{"take", pagination.Take.ToString()},
};

return Connection.GetAsync<ICheckpointPagedList<Sessions>>(
BuildUri($"users/{EncodePath(request.UserId)}/sessions", queryStrings),
DefaultHeaders,
sessionsConverter, cancellationToken: cancellationToken);
}

/// <inheritdoc cref="IUsersClient.DeleteSessionsAsync"/>
public Task DeleteSessionsAsync(string userId, CancellationToken cancellationToken = default)
{
return Connection.SendAsync<object>(
HttpMethod.Delete,
BuildUri($"users/{EncodePath(userId)}/sessions"),
null,
DefaultHeaders, cancellationToken: cancellationToken);
}
}
}
11 changes: 8 additions & 3 deletions src/Auth0.ManagementApi/HttpClientManagementConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -119,12 +119,17 @@ private async Task<T> SendRequest<T>(HttpRequestMessage request, JsonConverter[]

return typeof(T) == typeof(string)
? (T)(object)content
: JsonConvert.DeserializeObject<T>(content,
converters == null ? jsonSerializerSettings : new JsonSerializerSettings() { Converters = converters });
: DeserializeContent<T>(content, converters);
}
}

internal T DeserializeContent<T>(string content, JsonConverter[] converters)
{
return JsonConvert.DeserializeObject<T>(content,
converters == null ? jsonSerializerSettings : new JsonSerializerSettings() { Converters = converters });
}

private void ApplyHeaders(HttpHeaders current, IDictionary<string, string> input)
internal void ApplyHeaders(HttpHeaders current, IDictionary<string, string> input)
{
if (input == null) return;

Expand Down
10 changes: 10 additions & 0 deletions src/Auth0.ManagementApi/IManagementApiClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,16 @@ public interface IManagementApiClient : IDisposable
/// Contains all the methods to call the /users endpoints.
/// </summary>
IUsersClient Users { get; }

/// <summary>
/// Contains all the methods to call the /refresh-tokens endpoints.
/// </summary>
IRefreshTokenClient RefreshTokens { get; }

/// <summary>
/// Contains all the methods to call the /sessions endpoints.
/// </summary>
ISessionsClient Sessions { get; }

/// <summary>
/// Update the Access Token used with every request.
Expand Down
8 changes: 8 additions & 0 deletions src/Auth0.ManagementApi/ManagementApiClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,12 @@ public class ManagementApiClient : IManagementApiClient
/// Contains all the methods to call the /users endpoints.
/// </summary>
public IUsersClient Users { get; }

/// <inheritdoc cref="Auth0.ManagementApi.IManagementApiClient.RefreshTokens"/>
public IRefreshTokenClient RefreshTokens { get; }

/// <inheritdoc cref="Auth0.ManagementApi.IManagementApiClient.Sessions"/>
public ISessionsClient Sessions { get; }

private Dictionary<string, string> DefaultHeaders { get; set; }

Expand Down Expand Up @@ -213,6 +219,8 @@ public ManagementApiClient(string token, Uri baseUri, IManagementConnection mana
Tickets = new TicketsClient(managementConnection, baseUri, DefaultHeaders);
UserBlocks = new UserBlocksClient(managementConnection, baseUri, DefaultHeaders);
Users = new UsersClient(managementConnection, baseUri, DefaultHeaders);
RefreshTokens = new RefreshTokenClient(managementConnection, baseUri, DefaultHeaders);
Sessions = new SessionsClient(managementConnection, baseUri, DefaultHeaders);
}

/// <summary>
Expand Down
Loading

0 comments on commit 9ea28be

Please sign in to comment.