Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
73 commits
Select commit Hold shift + click to select a range
654d6a1
feat: remove IsDefault property from TenantEntity
ryceg Apr 20, 2026
74dd8ac
chore: remove DefaultTenantSlug from configuration
ryceg Apr 20, 2026
52d549e
chore: remove DefaultTenantSeeder
ryceg Apr 20, 2026
3bec8a6
chore: remove RecoveryModeState, RecoveryModeCheckService, and Recove…
ryceg Apr 20, 2026
4b2446d
feat: expand reserved tenant slugs to ~60 entries
ryceg Apr 20, 2026
087ac18
feat: remove IsDefault from TenantService, DTOs, and controllers
ryceg Apr 20, 2026
702baff
feat: simplify auth status — remove single/multi-tenant forking and R…
ryceg Apr 20, 2026
c4c36ae
fix: remove IsDefault from DirectGrantTokenHandlerTests
ryceg Apr 20, 2026
4fca789
feat: rewrite tenant resolution — single-tenant mode, setup detection…
ryceg Apr 20, 2026
d8ec9f9
feat: fail fast on startup if >1 tenant without BaseDomain
ryceg Apr 20, 2026
0c377ca
feat: TenantSetupMiddleware works in both single and multi-tenant modes
ryceg Apr 20, 2026
b4cd2d0
feat: add two-step setup endpoints (POST /api/v4/setup/tenant and /ap…
ryceg Apr 20, 2026
8e107ad
feat: add /api/v4/platform/ cross-tenant endpoints
ryceg Apr 20, 2026
6b7676f
feat: revoke all refresh tokens when BaseDomain is configured
ryceg Apr 20, 2026
d2ef31a
chore: remove all remaining IsDefault and RecoveryModeMiddleware refe…
ryceg Apr 20, 2026
0e7985f
feat: add migration to drop is_default column from tenants
ryceg Apr 20, 2026
67fb67f
feat: remove isDefault references and hostname-sniffing from frontend
ryceg Apr 20, 2026
3efe210
feat: two-step setup flow — slug/display name first, then passkey
ryceg Apr 20, 2026
f9a4011
feat: show BaseDomain transition notice on apex dashboard and tenant …
ryceg Apr 20, 2026
69c3ed0
feat: add coach mark state persistence layer
ryceg Apr 22, 2026
1147e5b
fix: add XML docs and fix style in coach mark files
ryceg Apr 22, 2026
6d3afa6
feat: add migration for coach_mark_states table
ryceg Apr 22, 2026
ef7d7e6
feat: add CoachMarkService with tests
ryceg Apr 22, 2026
8ebad0a
feat: add CoachMarkController with GET and PATCH endpoints
ryceg Apr 22, 2026
9cc553b
chore: regenerate API client with coach mark endpoints
ryceg Apr 22, 2026
635a108
feat: scaffold @nocturne/coach package
ryceg Apr 22, 2026
64788d5
feat: add @nocturne/coach type definitions
ryceg Apr 22, 2026
056e118
feat: add coach mark sequencing engine
ryceg Apr 22, 2026
28558b8
feat: add CoachMarkProvider and context
ryceg Apr 22, 2026
9865f22
feat: add coachmark attachment
ryceg Apr 22, 2026
bea05a3
feat: add coach mark popover with Floating UI positioning
ryceg Apr 22, 2026
13dbb4c
feat: add coach mark default theme with custom properties
ryceg Apr 22, 2026
426afe5
feat: add coach mark state adapter
ryceg Apr 22, 2026
da7fe7a
feat: add onboarding coach mark sequence config
ryceg Apr 22, 2026
2bf7185
feat: wire CoachMarkProvider into authenticated layout
ryceg Apr 22, 2026
9064464
feat: add OnboardingProgress dashboard widget
ryceg Apr 22, 2026
2366ffd
feat: place onboarding coach marks on settings pages
ryceg Apr 22, 2026
e10081c
refactor: replace SetupCard with coach mark system
ryceg Apr 22, 2026
7389383
feat(coach): scoped hotspot visibility, completedWhen/completeOn, the…
ryceg Apr 22, 2026
9f31568
fix(coach): non-null assertion for ctx in nested closure
ryceg Apr 22, 2026
8e7aed2
feat: map coach mark theme tokens to shadcn design system
ryceg Apr 22, 2026
632df9c
feat: add dashboard-discovery, feature-intro, and power-user sequences
ryceg Apr 22, 2026
af0e465
refactor: migrate onboarding marks from completed boolean to complete…
ryceg Apr 22, 2026
bea6d99
feat: place dashboard-discovery coach marks on dashboard, sidebar, an…
ryceg Apr 22, 2026
d3f67af
feat: place feature-intro coach marks on calendar, meals, food, and a…
ryceg Apr 22, 2026
21f71f1
feat: place power-user coach marks on trackers, connectors, alerts, a…
ryceg Apr 22, 2026
1ee1b6a
fix(coach): address code review findings
ryceg Apr 22, 2026
e8d6097
Merge branch 'main' into feature/coach-mark-system
ryceg Apr 23, 2026
e013f24
fix(aspire): bind gateway to port 443 in worktrees when custom domain…
ryceg Apr 23, 2026
05f4236
Revert "fix(aspire): bind gateway to port 443 in worktrees when custo…
ryceg Apr 23, 2026
abc6bb3
wip: coach mark popover, onboarding progress, and locale strings
ryceg Apr 23, 2026
b9b86a9
Merge branch 'feat/multitenancy-redesign'
ryceg Apr 23, 2026
cc1ed38
Merge branch 'main' into feature/coach-mark-system
ryceg Apr 23, 2026
3b5cc72
fix: resolve frontend type errors and merge multitenancy changes
ryceg Apr 23, 2026
0433d30
feat(auth): add setup OIDC interface methods to IOidcAuthService
ryceg Apr 23, 2026
87c5165
feat(auth): implement setup OIDC authorization and callback in OidcAu…
ryceg Apr 23, 2026
bb70a09
feat(setup): add OIDC owner creation endpoints to SetupController
ryceg Apr 23, 2026
c29e897
fix(setup): handle OIDC provider errors in callback, sync cookie expi…
ryceg Apr 23, 2026
0c5e018
test(setup): add unit tests for OIDC owner creation guard logic
ryceg Apr 23, 2026
4030fe7
feat(web): regenerate API client with setup OIDC endpoint
ryceg Apr 23, 2026
23f1dee
feat(web): refactor AccountCreation to share form fields between pass…
ryceg Apr 24, 2026
85fa654
feat(web): add AAPS therapy event icons replacing Lucide placeholders
ryceg Apr 24, 2026
fae4de7
feat(eversense): register ConnectSource, DataSources, and ServiceName…
ryceg Apr 24, 2026
5210030
feat(eversense): add project, models, and constants
ryceg Apr 24, 2026
fba9029
feat(eversense): add connector configuration with registration attrib…
ryceg Apr 24, 2026
5284804
test(eversense): add sensor glucose mapper tests
ryceg Apr 24, 2026
c660d26
feat(eversense): implement sensor glucose mapper
ryceg Apr 24, 2026
7a289f0
feat(eversense): implement auth token provider with OAuth2 password g…
ryceg Apr 24, 2026
148f229
test(eversense): add connector service patient selection tests
ryceg Apr 24, 2026
3c634e7
feat(eversense): implement connector service with patient selection
ryceg Apr 24, 2026
174df0c
feat(eversense): add connector installer and sync executor
ryceg Apr 24, 2026
8d5409e
fix(eversense): address code review findings (per-request auth, doubl…
ryceg Apr 24, 2026
a27294d
test(eversense): add integration-style service tests with mocked HTTP
ryceg Apr 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions nocturne.sln
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{15AD23A6-2AE
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NightscoutFoundation.Nocturne", "sdk\csharp\out\src\NightscoutFoundation.Nocturne\NightscoutFoundation.Nocturne.csproj", "{4E7462A7-89D6-44FD-8B40-4DE8BDD12543}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nocturne.Connectors.Eversense.Tests", "tests\Unit\Nocturne.Connectors.Eversense.Tests\Nocturne.Connectors.Eversense.Tests.csproj", "{66546F32-B3E7-4FEB-B3B3-979C2F14CC65}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nocturne.Connectors.Eversense", "src\Connectors\Nocturne.Connectors.Eversense\Nocturne.Connectors.Eversense.csproj", "{A79F4737-785F-4D54-A4C3-73CFA477C9D8}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -660,6 +664,30 @@ Global
{4E7462A7-89D6-44FD-8B40-4DE8BDD12543}.Release|x64.Build.0 = Release|Any CPU
{4E7462A7-89D6-44FD-8B40-4DE8BDD12543}.Release|x86.ActiveCfg = Release|Any CPU
{4E7462A7-89D6-44FD-8B40-4DE8BDD12543}.Release|x86.Build.0 = Release|Any CPU
{66546F32-B3E7-4FEB-B3B3-979C2F14CC65}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{66546F32-B3E7-4FEB-B3B3-979C2F14CC65}.Debug|Any CPU.Build.0 = Debug|Any CPU
{66546F32-B3E7-4FEB-B3B3-979C2F14CC65}.Debug|x64.ActiveCfg = Debug|Any CPU
{66546F32-B3E7-4FEB-B3B3-979C2F14CC65}.Debug|x64.Build.0 = Debug|Any CPU
{66546F32-B3E7-4FEB-B3B3-979C2F14CC65}.Debug|x86.ActiveCfg = Debug|Any CPU
{66546F32-B3E7-4FEB-B3B3-979C2F14CC65}.Debug|x86.Build.0 = Debug|Any CPU
{66546F32-B3E7-4FEB-B3B3-979C2F14CC65}.Release|Any CPU.ActiveCfg = Release|Any CPU
{66546F32-B3E7-4FEB-B3B3-979C2F14CC65}.Release|Any CPU.Build.0 = Release|Any CPU
{66546F32-B3E7-4FEB-B3B3-979C2F14CC65}.Release|x64.ActiveCfg = Release|Any CPU
{66546F32-B3E7-4FEB-B3B3-979C2F14CC65}.Release|x64.Build.0 = Release|Any CPU
{66546F32-B3E7-4FEB-B3B3-979C2F14CC65}.Release|x86.ActiveCfg = Release|Any CPU
{66546F32-B3E7-4FEB-B3B3-979C2F14CC65}.Release|x86.Build.0 = Release|Any CPU
{A79F4737-785F-4D54-A4C3-73CFA477C9D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A79F4737-785F-4D54-A4C3-73CFA477C9D8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A79F4737-785F-4D54-A4C3-73CFA477C9D8}.Debug|x64.ActiveCfg = Debug|Any CPU
{A79F4737-785F-4D54-A4C3-73CFA477C9D8}.Debug|x64.Build.0 = Debug|Any CPU
{A79F4737-785F-4D54-A4C3-73CFA477C9D8}.Debug|x86.ActiveCfg = Debug|Any CPU
{A79F4737-785F-4D54-A4C3-73CFA477C9D8}.Debug|x86.Build.0 = Debug|Any CPU
{A79F4737-785F-4D54-A4C3-73CFA477C9D8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A79F4737-785F-4D54-A4C3-73CFA477C9D8}.Release|Any CPU.Build.0 = Release|Any CPU
{A79F4737-785F-4D54-A4C3-73CFA477C9D8}.Release|x64.ActiveCfg = Release|Any CPU
{A79F4737-785F-4D54-A4C3-73CFA477C9D8}.Release|x64.Build.0 = Release|Any CPU
{A79F4737-785F-4D54-A4C3-73CFA477C9D8}.Release|x86.ActiveCfg = Release|Any CPU
{A79F4737-785F-4D54-A4C3-73CFA477C9D8}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -721,5 +749,7 @@ Global
{DEFF1846-EDA4-2FF5-6B67-8F8E44E83984} = {455910FC-CC9B-244E-E3A8-78E8113461FC}
{15AD23A6-2AE8-6CCD-98A8-B9F72FE1F2E0} = {DEFF1846-EDA4-2FF5-6B67-8F8E44E83984}
{4E7462A7-89D6-44FD-8B40-4DE8BDD12543} = {15AD23A6-2AE8-6CCD-98A8-B9F72FE1F2E0}
{66546F32-B3E7-4FEB-B3B3-979C2F14CC65} = {F25A9213-C080-46DC-93A8-09D96E2C55A1}
{A79F4737-785F-4D54-A4C3-73CFA477C9D8} = {237CDB4C-E7C4-795B-969A-4CCB7C8B335B}
EndGlobalSection
EndGlobal
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@ namespace Nocturne.API.Authorization;

/// <summary>
/// Marks a controller or action as safe to invoke while the current tenant has not
/// yet completed first-time setup (no passkey credentials) or is in recovery mode.
/// yet completed first-time setup (no passkey credentials).
/// <para>
/// <see cref="Middleware.TenantSetupMiddleware"/> and
/// <see cref="Middleware.RecoveryModeMiddleware"/> use endpoint metadata to
/// <see cref="Middleware.TenantSetupMiddleware"/> uses endpoint metadata to
/// short-circuit blocking behavior when this attribute is present.
/// </para>
/// <para>
Expand Down
158 changes: 37 additions & 121 deletions src/API/Nocturne.API/Controllers/Authentication/PasskeyController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -471,20 +471,25 @@
}

/// <summary>
/// Returns whether the current tenant is in recovery mode.
/// In multi-tenant mode, queries the database for orphaned subjects.
/// In single-tenant mode, reads from the global RecoveryModeState.
/// Returns tenant auth status: whether setup is required or recovery mode is active.
/// Queries the database for passkey credentials and orphaned subjects.
/// </summary>
[HttpGet("recovery-mode-status")]
[HttpGet("status")]
[AllowAnonymous]
[RemoteQuery]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<IActionResult> GetRecoveryModeStatus([FromServices] RecoveryModeState state)
[ProducesResponseType(typeof(AuthStatusResponse), StatusCodes.Status200OK)]
public async Task<IActionResult> GetAuthStatus()
{
var tenantId = _tenantAccessor.TenantId;

var hasCredentials = await _dbContext.TenantMembers
.Where(m => m.TenantId == tenantId)
.AnyAsync(m => _dbContext.PasskeyCredentials.Any(c => c.SubjectId == m.SubjectId));
var setupRequired = !hasCredentials;

bool recoveryMode;
if (_tenantAccessor.IsResolved)
if (hasCredentials)
{
var tenantId = _tenantAccessor.TenantId;
recoveryMode = await _dbContext.TenantMembers
.Where(tm => tm.TenantId == tenantId)
.Join(
Expand All @@ -499,61 +504,10 @@
}
else
{
recoveryMode = state.IsEnabled;
}

return Ok(new { recoveryMode });
}

/// <summary>
/// Returns tenant auth status: whether setup is required or recovery mode is active.
/// In multi-tenant mode, queries the database. In single-tenant mode, reads global state.
/// </summary>
[HttpGet("status")]
[AllowAnonymous]
[RemoteQuery]
[ProducesResponseType(typeof(AuthStatusResponse), StatusCodes.Status200OK)]
public async Task<IActionResult> GetAuthStatus([FromServices] RecoveryModeState state)
{
bool setupRequired;
bool recoveryMode;

if (_tenantAccessor.IsResolved)
{
var tenantId = _tenantAccessor.TenantId;
var hasCredentials = await _dbContext.TenantMembers
.Where(m => m.TenantId == tenantId)
.AnyAsync(m => _dbContext.PasskeyCredentials.Any(c => c.SubjectId == m.SubjectId));
setupRequired = !hasCredentials;

if (hasCredentials)
{
recoveryMode = await _dbContext.TenantMembers
.Where(tm => tm.TenantId == tenantId)
.Join(
_dbContext.Subjects.Where(s => s.IsActive && !s.IsSystemSubject),
tm => tm.SubjectId,
s => s.Id,
(tm, s) => s)
.Where(s =>
!_dbContext.SubjectOidcIdentities.Any(i => i.SubjectId == s.Id) &&
!_dbContext.PasskeyCredentials.Any(p => p.SubjectId == s.Id))
.AnyAsync();
}
else
{
recoveryMode = false;
}
}
else
{
setupRequired = state.IsSetupRequired;
recoveryMode = state.IsEnabled;
recoveryMode = false;
}

var tenant = _tenantAccessor.IsResolved
? await _dbContext.Tenants.FirstOrDefaultAsync(t => t.Id == _tenantAccessor.TenantId)
: await _dbContext.Tenants.IgnoreQueryFilters().FirstOrDefaultAsync(t => t.IsDefault);
var tenant = await _dbContext.Tenants.FirstOrDefaultAsync(t => t.Id == tenantId);

return Ok(new AuthStatusResponse
{
Expand All @@ -575,29 +529,15 @@
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status403Forbidden)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)]
public async Task<ActionResult<PasskeyOptionsResponse>> SetupOptions(
[FromBody] SetupOptionsRequest request,
[FromServices] RecoveryModeState state)
[FromBody] SetupOptionsRequest request)
{
// Use the resolved tenant (multi-tenant) or fall back to the default tenant (single-tenant)
var tenantId = _tenantAccessor.IsResolved
? _tenantAccessor.TenantId
: (await _dbContext.Tenants.IgnoreQueryFilters().FirstOrDefaultAsync(t => t.IsDefault))?.Id
?? Guid.Empty;

if (tenantId == Guid.Empty)
{
return Problem(detail: "Tenant not found — restart the application", statusCode: 500, title: "Server Error");
}
var tenantId = _tenantAccessor.TenantId;

// Single-tenant: RecoveryModeState.IsSetupRequired is set at startup
// Multi-tenant: no tenant members have passkey credentials yet
var tenantHasPasskeys = _tenantAccessor.IsResolved &&
await _dbContext.TenantMembers
.Where(m => m.TenantId == tenantId)
.AnyAsync(m => _dbContext.PasskeyCredentials.Any(c => c.SubjectId == m.SubjectId));
var tenantNeedsSetup = state.IsSetupRequired ||
(_tenantAccessor.IsResolved && !tenantHasPasskeys);
if (!tenantNeedsSetup)
// Check whether any tenant member already has a passkey credential
var tenantHasPasskeys = await _dbContext.TenantMembers
.Where(m => m.TenantId == tenantId)
.AnyAsync(m => _dbContext.PasskeyCredentials.Any(c => c.SubjectId == m.SubjectId));
if (tenantHasPasskeys)
{
return Problem(detail: "Setup mode is not active", statusCode: 403, title: "Forbidden");
}
Expand Down Expand Up @@ -675,26 +615,15 @@
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status403Forbidden)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)]
public async Task<ActionResult<SetupCompleteResponse>> SetupComplete(
[FromBody] SetupCompleteRequest request,
[FromServices] RecoveryModeState state)
[FromBody] SetupCompleteRequest request)
{
var tenantId = _tenantAccessor.IsResolved
? _tenantAccessor.TenantId
: (await _dbContext.Tenants.IgnoreQueryFilters().FirstOrDefaultAsync(t => t.IsDefault))?.Id
?? Guid.Empty;

if (tenantId == Guid.Empty)
{
return Problem(detail: "Tenant not found", statusCode: 500, title: "Server Error");
}
var tenantId = _tenantAccessor.TenantId;

var tenantHasPasskeys = _tenantAccessor.IsResolved &&
await _dbContext.TenantMembers
.Where(m => m.TenantId == tenantId)
.AnyAsync(m => _dbContext.PasskeyCredentials.Any(c => c.SubjectId == m.SubjectId));
var tenantNeedsSetup = state.IsSetupRequired ||
(_tenantAccessor.IsResolved && !tenantHasPasskeys);
if (!tenantNeedsSetup)
// Check whether any tenant member already has a passkey credential
var tenantHasPasskeys = await _dbContext.TenantMembers
.Where(m => m.TenantId == tenantId)
.AnyAsync(m => _dbContext.PasskeyCredentials.Any(c => c.SubjectId == m.SubjectId));
if (tenantHasPasskeys)
{
return Problem(detail: "Setup mode is not active", statusCode: 403, title: "Forbidden");
}
Expand All @@ -720,7 +649,7 @@
}

var roles = await _subjectService.GetSubjectRolesAsync(credResult.SubjectId);
var permissions = await _subjectService.GetSubjectPermissionsAsync(credResult.SubjectId);

Check warning

Code scanning / CodeQL

Log entries created from user input Medium

This log entry depends on a
user-provided value
.

// Issue session
var subjectInfo = new SubjectInfo
Expand All @@ -740,9 +669,6 @@

SetSessionCookies(accessToken, refreshToken);

// Exit setup mode
state.IsSetupRequired = false;

_logger.LogInformation(
"Setup complete: first user {SubjectId} registered with passkey",
credResult.SubjectId);
Expand Down Expand Up @@ -780,9 +706,9 @@
public async Task<ActionResult<PasskeyOptionsResponse>> AccessRequestOptions(
[FromBody] AccessRequestOptionsRequest request)
{
var tenantId = _tenantAccessor.TenantId;
var tenant = await _dbContext.Tenants
.IgnoreQueryFilters()
.FirstOrDefaultAsync(t => t.IsDefault);
.FirstOrDefaultAsync(t => t.Id == tenantId);

if (tenant == null || !tenant.AllowAccessRequests)
return NotFound();
Expand Down Expand Up @@ -847,9 +773,9 @@
[FromBody] AccessRequestCompleteRequest request,
[FromServices] IInAppNotificationService notificationService)
{
var tenantId = _tenantAccessor.TenantId;
var tenant = await _dbContext.Tenants
.IgnoreQueryFilters()
.FirstOrDefaultAsync(t => t.IsDefault);
.FirstOrDefaultAsync(t => t.Id == tenantId);

if (tenant == null || !tenant.AllowAccessRequests)
return NotFound();
Expand Down Expand Up @@ -929,12 +855,7 @@
if (invite == null || !invite.IsValid)
return NotFound();

var tenant = await _dbContext.Tenants
.IgnoreQueryFilters()
.FirstOrDefaultAsync(t => t.IsDefault);

if (tenant == null)
return Problem(detail: "Default tenant not found", statusCode: 500, title: "Server Error");
var tenantId = _tenantAccessor.TenantId;

// Create the subject
var subjectId = Guid.CreateVersion7();
Expand All @@ -957,7 +878,7 @@

// Generate passkey registration options
var result = await _passkeyService.GenerateRegistrationOptionsAsync(
subjectId, username, tenant.Id);
subjectId, username, tenantId);

return Ok(new PasskeyOptionsResponse
{
Expand Down Expand Up @@ -985,17 +906,12 @@
return Problem(detail: "Challenge token and invite token are required", statusCode: 400, title: "Bad Request");
}

var tenant = await _dbContext.Tenants
.IgnoreQueryFilters()
.FirstOrDefaultAsync(t => t.IsDefault);

if (tenant == null)
return Problem(detail: "Default tenant not found", statusCode: 500, title: "Server Error");
var tenantId = _tenantAccessor.TenantId;

try
{
var credResult = await _passkeyService.CompleteRegistrationAsync(
request.AttestationResponseJson, request.ChallengeToken, tenant.Id);
request.AttestationResponseJson, request.ChallengeToken, tenantId);

// Accept the invite
var acceptResult = await memberInviteService.AcceptInviteAsync(request.Token, credResult.SubjectId);
Expand Down
8 changes: 1 addition & 7 deletions src/API/Nocturne.API/Controllers/MetadataController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -195,8 +195,7 @@ public ActionResult<MultitenancyInfo> GetMultitenancyInfo(
return Ok(new MultitenancyInfo
{
BaseDomain = config.Value.BaseDomain,
DefaultTenantSlug = config.Value.DefaultTenantSlug,
SubdomainResolution = !string.IsNullOrEmpty(config.Value.BaseDomain),
SubdomainResolution = true,
AllowSelfServiceCreation = config.Value.AllowSelfServiceCreation,
CurrentTenantSlug = tenantContext?.Slug,
CurrentTenantId = tenantContext?.TenantId,
Expand Down Expand Up @@ -571,11 +570,6 @@ public class MultitenancyInfo
/// </summary>
public string? BaseDomain { get; set; }

/// <summary>
/// Slug of the auto-created default tenant
/// </summary>
public string DefaultTenantSlug { get; set; } = "default";

/// <summary>
/// Whether subdomain-based tenant resolution is active
/// </summary>
Expand Down
Loading
Loading