-
Notifications
You must be signed in to change notification settings - Fork 56
Add support for Introspect token API #474
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| using System; | ||
|
|
||
| namespace Notion.Client | ||
| { | ||
| public static class BasicAuthParamValidator | ||
| { | ||
| public static void Validate(IBasicAuthenticationParameters basicAuthParams) | ||
| { | ||
| if (basicAuthParams == null) | ||
| { | ||
| throw new ArgumentNullException(nameof(basicAuthParams), "Basic authentication parameters must be provided."); | ||
| } | ||
|
|
||
| if (string.IsNullOrWhiteSpace(basicAuthParams.ClientId)) | ||
| { | ||
| throw new ArgumentException("ClientId must be provided.", nameof(basicAuthParams.ClientId)); | ||
| } | ||
|
|
||
| if (string.IsNullOrWhiteSpace(basicAuthParams.ClientSecret)) | ||
| { | ||
| throw new ArgumentException("ClientSecret must be provided.", nameof(basicAuthParams.ClientSecret)); | ||
| } | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| using System; | ||
| using System.Threading; | ||
| using System.Threading.Tasks; | ||
|
|
||
| namespace Notion.Client | ||
| { | ||
| public sealed partial class AuthenticationClient | ||
| { | ||
| public async Task<IntrospectTokenResponse> IntrospectTokenAsync( | ||
| IntrospectTokenRequest introspectTokenRequest, | ||
| CancellationToken cancellationToken = default) | ||
| { | ||
| if (introspectTokenRequest is null) | ||
| { | ||
| throw new ArgumentNullException(nameof(introspectTokenRequest)); | ||
| } | ||
|
|
||
| IIntrospectTokenBodyParameters body = introspectTokenRequest; | ||
| IBasicAuthenticationParameters basicAuth = introspectTokenRequest; | ||
|
|
||
| if (string.IsNullOrWhiteSpace(body.Token)) | ||
| { | ||
| throw new ArgumentException("Token must be provided.", nameof(body.Token)); | ||
| } | ||
|
|
||
| BasicAuthParamValidator.Validate(basicAuth); | ||
|
|
||
| return await _client.PostAsync<IntrospectTokenResponse>( | ||
| ApiEndpoints.AuthenticationUrls.IntrospectToken(), | ||
| body, | ||
| basicAuthenticationParameters: basicAuth, | ||
| cancellationToken: cancellationToken | ||
| ); | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| using Newtonsoft.Json; | ||
|
|
||
| namespace Notion.Client | ||
| { | ||
| public interface IIntrospectTokenBodyParameters | ||
| { | ||
| /// <summary> | ||
| /// The access token | ||
| /// </summary> | ||
| [JsonProperty(PropertyName = "token")] | ||
| public string Token { get; } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| namespace Notion.Client | ||
| { | ||
| public class IntrospectTokenRequest : IIntrospectTokenBodyParameters, IBasicAuthenticationParameters | ||
| { | ||
| public string Token { get; set; } | ||
|
|
||
| public string ClientId { get; set; } | ||
|
|
||
| public string ClientSecret { get; set; } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| using Newtonsoft.Json; | ||
|
|
||
| namespace Notion.Client | ||
| { | ||
| public class IntrospectTokenResponse | ||
| { | ||
| [JsonProperty("active")] | ||
| public bool IsActive { get; set; } | ||
|
|
||
| [JsonProperty("scope")] | ||
| public string Scope { get; set; } | ||
|
|
||
| [JsonProperty("iat")] | ||
| public long Iat { get; set; } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,133 @@ | ||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.Threading; | ||
| using System.Threading.Tasks; | ||
| using Moq; | ||
| using Moq.AutoMock; | ||
| using Newtonsoft.Json; | ||
| using Notion.Client; | ||
| using Xunit; | ||
|
|
||
| namespace Notion.UnitTests; | ||
|
|
||
| public class AuthenticationClientTests | ||
| { | ||
| private readonly AutoMocker _mocker = new(); | ||
| private readonly Mock<IRestClient> _restClientMock; | ||
| private readonly AuthenticationClient _authenticationClient; | ||
|
|
||
| public AuthenticationClientTests() | ||
| { | ||
| _restClientMock = _mocker.GetMock<IRestClient>(); | ||
| _authenticationClient = _mocker.CreateInstance<AuthenticationClient>(); | ||
| } | ||
|
|
||
| [Fact] | ||
| public async Task IntrospectTokenAsync_ThrowsArgumentNullException_WhenRequestIsNull() | ||
| { | ||
| // Act & Assert | ||
| var exception = await Assert.ThrowsAsync<ArgumentNullException>(() => _authenticationClient.IntrospectTokenAsync(null)); | ||
| Assert.Equal("introspectTokenRequest", exception.ParamName); | ||
| Assert.Equal("Value cannot be null. (Parameter 'introspectTokenRequest')", exception.Message); | ||
| } | ||
|
|
||
| [Theory] | ||
| [InlineData(null)] | ||
| [InlineData("")] | ||
| [InlineData(" ")] | ||
| public async Task IntrospectTokenAsync_ThrowsArgumentException_WhenTokenIsNullOrEmpty(string token) | ||
|
Check warning on line 38 in Test/Notion.UnitTests/AuthenticationClientTests.cs
|
||
| { | ||
| // Arrange | ||
| var request = new IntrospectTokenRequest | ||
| { | ||
| Token = null, | ||
| ClientId = "validClientId", | ||
| ClientSecret = "validClientSecret" | ||
| }; | ||
|
|
||
| // Act & Assert | ||
| var exception = await Assert.ThrowsAsync<ArgumentException>(() => _authenticationClient.IntrospectTokenAsync(request)); | ||
| Assert.Equal("Token", exception.ParamName); | ||
| Assert.Equal("Token must be provided. (Parameter 'Token')", exception.Message); | ||
| } | ||
|
|
||
| [Theory] | ||
| [InlineData(null)] | ||
| [InlineData("")] | ||
| [InlineData(" ")] | ||
| public async Task IntrospectTokenAsync_ThrowsArgumentException_WhenClientIdIsNullOrEmpty(string clientId) | ||
| { | ||
| // Arrange | ||
| var request = new IntrospectTokenRequest | ||
| { | ||
| Token = "validToken", | ||
| ClientId = clientId, | ||
| ClientSecret = "validClientSecret" | ||
| }; | ||
|
Comment on lines
+61
to
+66
|
||
|
|
||
| // Act & Assert | ||
| var exception = await Assert.ThrowsAsync<ArgumentException>(() => _authenticationClient.IntrospectTokenAsync(request)); | ||
| Assert.Equal("ClientId", exception.ParamName); | ||
| Assert.Equal("ClientId must be provided. (Parameter 'ClientId')", exception.Message); | ||
| } | ||
|
|
||
| [Theory] | ||
| [InlineData(null)] | ||
| [InlineData("")] | ||
| [InlineData(" ")] | ||
| public async Task IntrospectTokenAsync_ThrowsArgumentException_WhenClientSecretIsNullOrEmpty(string clientSecret) | ||
| { | ||
| // Arrange | ||
| var request = new IntrospectTokenRequest | ||
| { | ||
| Token = "validToken", | ||
| ClientId = "validClientId", | ||
| ClientSecret = clientSecret | ||
| }; | ||
|
|
||
| // Act & Assert | ||
| var exception = await Assert.ThrowsAsync<ArgumentException>(() => _authenticationClient.IntrospectTokenAsync(request)); | ||
| Assert.Equal("ClientSecret", exception.ParamName); | ||
| Assert.Equal("ClientSecret must be provided. (Parameter 'ClientSecret')", exception.Message); | ||
| } | ||
|
|
||
| [Fact] | ||
| public async Task IntrospectTokenAsync_CallsPostAsync_WithCorrectParameters() | ||
| { | ||
| // Arrange | ||
| var introspectTokenRequest = new IntrospectTokenRequest | ||
| { | ||
| Token = "validToken", | ||
| ClientId = "validClientId", | ||
| ClientSecret = "validClientSecret" | ||
| }; | ||
|
|
||
| var expectedResponse = new IntrospectTokenResponse | ||
| { | ||
| IsActive = true, | ||
| Scope = "read write", | ||
| Iat = DateTimeOffset.UtcNow.ToUnixTimeSeconds() | ||
| }; | ||
|
|
||
| _restClientMock | ||
| .Setup(client => client.PostAsync<IntrospectTokenResponse>( | ||
| It.Is<string>(url => url == ApiEndpoints.AuthenticationUrls.IntrospectToken()), | ||
| It.IsAny<IIntrospectTokenBodyParameters>(), | ||
| It.IsAny<IEnumerable<KeyValuePair<string, string>>>(), | ||
| It.IsAny<IDictionary<string, string>>(), | ||
| It.IsAny<JsonSerializerSettings>(), | ||
| It.IsAny<IBasicAuthenticationParameters>(), | ||
| It.IsAny<CancellationToken>())) | ||
| .ReturnsAsync(expectedResponse); | ||
|
|
||
| // Act | ||
| var response = await _authenticationClient.IntrospectTokenAsync(introspectTokenRequest); | ||
|
|
||
| // Assert | ||
| Assert.NotNull(response); | ||
| Assert.Equal(expectedResponse.IsActive, response.IsActive); | ||
| Assert.Equal(expectedResponse.Scope, response.Scope); | ||
| Assert.Equal(expectedResponse.Iat, response.Iat); | ||
| _restClientMock.VerifyAll(); | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.