Skip to content

Commit

Permalink
Merge pull request #15 from craysiii/refactor
Browse files Browse the repository at this point in the history
Refactor for 0.2.0
  • Loading branch information
craysiii committed Apr 2, 2024
2 parents 11e1b71 + 44ec571 commit 918b908
Show file tree
Hide file tree
Showing 65 changed files with 1,706 additions and 398 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v3
with:
dotnet-version: 6.0
dotnet-version: 8.0

- name: Build
run: dotnet build $SOLUTION --configuration $BUILD_CONFIG -p:Version=$BUILD_VERSION --no-restore
Expand Down
22 changes: 6 additions & 16 deletions NinjaOne-Api.Library/Authentication/AccessTokenInstance.cs
Original file line number Diff line number Diff line change
@@ -1,22 +1,12 @@
namespace NinjaOne_Api.Library.Authentication;

public class AccessTokenInstance
public class AccessTokenInstance(TokenResponse tokenResponse)
{
public string AccessToken { get; init; }
public string Scope { get; init; }
public string? RefreshToken { get; set; }
public string TokenType { get; init; }
private DateTime Expiry { get; init; }

public AccessTokenInstance(TokenResponse tokenResponse)
{
AccessToken = tokenResponse.AccessToken!;
Scope = tokenResponse.Scope!;
RefreshToken = tokenResponse.RefreshToken;
TokenType = tokenResponse.TokenType!;

Expiry = DateTime.Now.AddSeconds(tokenResponse.ExpiresIn);
}
public string AccessToken { get; } = tokenResponse.AccessToken!;
public string Scope { get; init; } = tokenResponse.Scope!;
public string? RefreshToken { get; } = tokenResponse.RefreshToken;
public string TokenType { get; } = tokenResponse.TokenType!;
private DateTime Expiry { get; } = DateTime.Now.AddSeconds(tokenResponse.ExpiresIn);

public bool IsExpired()
{
Expand Down
102 changes: 67 additions & 35 deletions NinjaOne-Api.Library/Authentication/NinjaAuthenticator.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
namespace NinjaOne_Api.Library.Authentication;
using System.Security.Authentication;

namespace NinjaOne_Api.Library.Authentication;

public class NinjaAuthenticator : AuthenticatorBase
{
Expand All @@ -8,64 +10,94 @@ public class NinjaAuthenticator : AuthenticatorBase
private readonly string _clientId;
private readonly string _clientSecret;
private readonly string _scopes;
private readonly ILogger<NinjaAuthenticator> _log;

public NinjaAuthenticator(string baseUrl, string clientId, string clientSecret, ApplicationScopes scopes) : base("")
public NinjaAuthenticator(
string baseUrl,
string clientId,
string clientSecret,
ApplicationScopes scopes,
LoggerFactory? loggerFactory,
LogLevel logLevel
) : base("")
{
_baseUrl = baseUrl;
_clientId = clientId;
_clientSecret = clientSecret;
_scopes = scopes.ToString().ToLower().Replace(",", "");
_scopes = scopes.ToString().ToLower().Replace(",", string.Empty);
var logFactory = loggerFactory ?? LoggerFactory.Create(builder =>
{
builder
.AddFilter(Logging.FilterAuthenticator, logLevel)
.AddSimpleConsole(options =>
{
options.SingleLine = true;
options.ColorBehavior = LoggerColorBehavior.Enabled;
options.UseUtcTimestamp = true;
options.TimestampFormat = Logging.TimestampFormat;
});
});

_log = logFactory.CreateLogger<NinjaAuthenticator>();
}

protected override async ValueTask<Parameter> GetAuthenticationParameter(string accessToken)
{
if (TokenInstance is null || (TokenInstance.IsExpired() && !TokenInstance.IsRefreshable()))
{
TokenInstance = await GetToken();
}

if (TokenInstance.IsExpired() && TokenInstance.IsRefreshable())
if (TokenInstance is null || TokenInstance.IsExpired())
{
TokenInstance = await RefreshToken();
TokenInstance = await GetToken(
refresh: TokenInstance is not null && TokenInstance.IsExpired() && TokenInstance.IsRefreshable()
);
}

Token = TokenInstance.AccessToken;
return new HeaderParameter(KnownHeaders.Authorization, $"{TokenInstance.TokenType} {TokenInstance.AccessToken}");

return new HeaderParameter(
KnownHeaders.Authorization,
$"{TokenInstance.TokenType} {TokenInstance.AccessToken}"
);
}

private async Task<AccessTokenInstance> GetToken()
private async Task<AccessTokenInstance> GetToken(bool refresh = false)
{
var options = GetAuthenticationClientOptions();
using var client = new RestClient(options);

var request = new RestRequest(Resource.Token)
.AddParameter("grant_type", "client_credentials")
.AddParameter("scope", _scopes);

var response = await client.PostAsync<TokenResponse>(request);
return new AccessTokenInstance(response!);
}

private async Task<AccessTokenInstance> RefreshToken()
{
var options = GetAuthenticationClientOptions();
using var client = new RestClient(options);
var request = new RestRequest(Resource.Token);
request.AddParameter(Param.GrantType,
(refresh ? GrantType.REFRESH_TOKEN : GrantType.CLIENT_CREDENTIALS).ToString().ToLower());
if (refresh)
{
request.AddParameter(Param.RefreshToken, TokenInstance!.RefreshToken);
}
else
{
request.AddParameter(Param.Scope, _scopes);
}

var request = new RestRequest(Resource.Token)
.AddParameter("grant_type", "refresh_token")
.AddParameter("refresh_token", TokenInstance!.RefreshToken); // Cannot be null
var response = await client.ExecuteAsync(request, Method.Post);
_log.LogDebug(
"Auth Request Grant: {} Resource: {} Response: {} {}",
request.Parameters.TryFind(Param.GrantType)!.Value,
request.Resource,
(int)response.StatusCode,
response.StatusDescription
);

var jsonElement = JsonDocument.Parse(response.Content ?? string.Empty).RootElement;

var response = await client.PostAsync<TokenResponse>(request);
return new AccessTokenInstance(response!);
if (response.IsSuccessStatusCode)
return new AccessTokenInstance(Serializer.DeserializeObject<TokenResponse>(jsonElement));

if (!Client.IsValidJson(response.Content)) throw new AuthenticationException(response.Content);

var error = Serializer.DeserializeObject<NinjaApiError>(jsonElement);
throw new AuthenticationException(error.ErrorMessage);
}

private RestClientOptions GetAuthenticationClientOptions()
{
var options = new RestClientOptions(_baseUrl)
return new RestClientOptions(_baseUrl)
{
Authenticator = new HttpBasicAuthenticator(_clientId, _clientSecret)
};

return options;
}
}
10 changes: 5 additions & 5 deletions NinjaOne-Api.Library/Authentication/TokenResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@

public record TokenResponse
{
[JsonPropertyName("access_token")]
[JsonPropertyName(Property.AccessToken)]
public string? AccessToken { get; init; }
[JsonPropertyName("expires_in")]
[JsonPropertyName(Property.ExpiresIn)]
public int ExpiresIn { get; init; }
[JsonPropertyName("scope")]
[JsonPropertyName(Property.Scope)]
public string? Scope { get; init; }
[JsonPropertyName("refresh_token")]
[JsonPropertyName(Property.RefreshToken)]
public string? RefreshToken { get; init; }
[JsonPropertyName("token_type")]
[JsonPropertyName(Property.TokenType)]
public string? TokenType { get; init; }
}
Loading

0 comments on commit 918b908

Please sign in to comment.