IdentityShroud/IdentityShroud.Api.Tests/Apis/OpenIdApiTests.cs

123 lines
4.2 KiB
C#
Raw Normal View History

using System.Net;
using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text.Json.Serialization;
using IdentityShroud.Api.Apis;
using IdentityShroud.Core.EFCore;
using IdentityShroud.Core.Tests.Fixtures;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Shouldly;
namespace IdentityShroud.Api.Tests.Apis;
public class OpenIdApiTests : IClassFixture<ApplicationFactory>
{
private readonly ApplicationFactory _factory;
public OpenIdApiTests(ApplicationFactory factory)
{
_factory = factory;
using var scope = _factory.Services.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<Db>();
if (!db.Database.EnsureCreated())
{
db.Database.ExecuteSqlRaw("TRUNCATE realm CASCADE;");
}
}
[Theory]
[InlineData(true)]
[InlineData(false)]
public async Task ClientCredentialsFlow(bool useAuthenticationHeader)
{
var client = _factory.CreateClient();
var createRealmResponse = await client.PostAsync("/api/v1/realms", JsonContent.Create(new
{
Slug = "foo",
Name = "Test'",
}),
TestContext.Current.CancellationToken);
createRealmResponse.StatusCode.ShouldBe(HttpStatusCode.Created);
var realm = await createRealmResponse.Content.ReadFromJsonAsync<RealmRepresentation>(
cancellationToken: TestContext.Current.CancellationToken);
realm.ShouldNotBeNull();
realm.Id.ShouldNotBe(Guid.Empty);
var createClientResponse = await client.PostAsync(
$"/api/v1/realms/{realm.Id}/clients",
JsonContent.Create(new
{
ClientId = "myclient",
Name = "New Client",
Confidential = true,
AllowClientCredentialsFlow = true,
GenerateSecret = true,
}),
TestContext.Current.CancellationToken);
createClientResponse.StatusCode.ShouldBe(HttpStatusCode.Created);
// Act
const string clientId = "myclient";
var data = new[]
{
new KeyValuePair<string, string>("client_id", clientId),
new KeyValuePair<string, string>("client_secret", "secret"),
new KeyValuePair<string, string>("response_type", "token"),
new KeyValuePair<string, string>("grant_type", "client_credentials"),
};
if (useAuthenticationHeader)
{
// client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("basic",
// Convert.ToBase64String($"{clientId}:{clientSecret}"))
}
var content = new FormUrlEncodedContent(data);
var response = await client.PostAsync(
"/auth/realms/foo/openid-connect/token",
content,
TestContext.Current.CancellationToken);
// Verify
// var responseJson = await response.Content.ReadAsStringAsync(
// TestContext.Current.CancellationToken);
// Console.WriteLine($"Response: {responseJson}");
response.StatusCode.ShouldBe(HttpStatusCode.OK);
// Cache-Control: no-store
response.Headers.CacheControl.ShouldNotBeNull()
.NoStore.ShouldBe(true);
// Pragma: no-cache
response.Headers.Pragma.ShouldNotBeNull()
.ShouldContain(new NameValueHeaderValue("no-cache"));
var payload = await response.Content.ReadFromJsonAsync<TokenResponse>();
payload.ShouldNotBeNull();
Assert.Multiple(
() => payload.AccessToken.ShouldNotBeNull(),
() => payload.TokenType.ShouldBe("bearer"),
() => payload.ExpiresIn.ShouldBe(3600));
// - refresh_token OPTIONAL
// - scope OPTIONAL when identical to request otherwise REQUIRED
}
internal class TokenResponse
{
[JsonPropertyName("access_token")]
public string? AccessToken { get; set; }
[JsonPropertyName("token_type")]
public string? TokenType { get; set; }
[JsonPropertyName("expires_in")]
public int? ExpiresIn { get; set; }
}
}