Open Closed

Authorization error when attempting to register using custom registration page #9750


User avatar
0
cstobler created

I have an open ticket for this (#9614), but I haven't received help so I am opening another to see if I can get help here and maybe get refunded for that ticket.

I created a custom registration page, which I am planning on using to allow customers to sign up themselves, creating a tenant and signing up for an edition at the same time.

Here is my page model for my registration page that will handle creating the tenant and subscribing the edition to that tenant:

[AllowAnonymous]
public class RegisterModel : ArmadaPageModel
{
    private readonly ITenantAppService _tenantAppService;
    private readonly IMultiTenancyAppService _multiTenancyAppService;
    private readonly IEditionAppService _editionAppService;
    private readonly ISubscriptionAppService _subscriptionAppService;
    private readonly ICurrentTenant _currentTenant;

    public RegisterModel(
        ITenantAppService tenantAppService,
        IMultiTenancyAppService multiTenancyAppService,
        IEditionAppService editionAppService,
        ISubscriptionAppService subscriptionAppService,
        ICurrentTenant currentTenant)
    {
        _tenantAppService = tenantAppService;
        _multiTenancyAppService = multiTenancyAppService;
        _editionAppService = editionAppService;
        _subscriptionAppService = subscriptionAppService;
        _currentTenant = currentTenant;
    }

    [BindProperty]
    public RegisterTenantViewModel Input { get; set; } = new();

    public async Task OnGetAsync()
    {
        List<EditionDto> editions = await _multiTenancyAppService.GetEditionsAsync();
        List<SelectListItem> editionSelectItems = editions.Select(e => new SelectListItem
        {
            Text = e.DisplayName,
            Value = e.Id.ToString()
        }).ToList();

        Input.AvailableEditions = editionSelectItems;
    }

    public async Task<IActionResult> OnPostAsync()
    {
        if (!ModelState.IsValid)
        {
            await OnGetAsync(); // reload editions
            return Page();
        }

        // Confirm we are NOT in tenant context
        if (_currentTenant.Id != null)
        {
            throw new Exception("Cannot register a tenant while already in a tenant context.");
            //return Forbid(); // Registration should only be done as host
        }

        //var tenantDto = await _tenantAppService.CreateAsync(new SaasTenantCreateDto
        //{
        //    Name = Input.TenantName,
        //    AdminEmailAddress = Input.Email,
        //    AdminPassword = Input.Password,
        //    EditionId = Input.EditionId
        //});

        var tenantDto = await _multiTenancyAppService.CreateTenantAsync(new SaasTenantCreateDto
        {
            Name = Input.TenantName,
            AdminEmailAddress = Input.Email,
            AdminPassword = Input.Password,
            EditionId = Input.EditionId
        });

        // 2. Manually switch to new tenant context
        using (_currentTenant.Change(tenantDto.Id))
        {
            // 3. Create subscription with edition ID
            var subscription = await _subscriptionAppService.CreateSubscriptionAsync(
                Input.EditionId,
                tenantDto.Id
            );

            // 4. Redirect to Stripe or confirmation
            //return Redirect(subscription.PaymentUrl);
        }

        return Page();
    }
}

I also created an appservice called MultiTenancyAppService which helps me populate the editions dropdown, as well as attempt to create a tenant (since calling tenantAppService.CreateAsync() was throwing an authorization error):

[AllowAnonymous]
public class MultiTenancyAppService : ArmadaIOAppService, IMultiTenancyAppService
{
    private readonly IRepository<Edition, Guid> _editionRepository;
    private readonly ITenantAppService _tenantAppService;
    private readonly IRepository<Tenant, Guid> _tenantRepository;

    public MultiTenancyAppService(IRepository<Edition, Guid> editionRepository, ITenantAppService tenantAppService, IRepository<Tenant, Guid> tenantRepository)
    {
        _editionRepository = editionRepository;
        _tenantAppService = tenantAppService;
        _tenantRepository = tenantRepository;
    }

    public async Task<List<EditionDto>> GetEditionsAsync()
    {
        List<Edition> editions = await _editionRepository.GetListAsync();
        List<EditionDto> dtos = ObjectMapper.Map<List<Edition>, List<EditionDto>>(editions);
        return dtos;
    }

    public async Task<SaasTenantDto> CreateTenantAsync(SaasTenantCreateDto input)
    {
        if (string.IsNullOrWhiteSpace(input.Name) || string.IsNullOrWhiteSpace(input.AdminEmailAddress) || string.IsNullOrWhiteSpace(input.AdminPassword))
        {
            throw new UserFriendlyException("Please fill all required fields before submission");
        }
        
        return await _tenantAppService.CreateAsync(input);
    }
}

It is still throwing a "Exception of type 'Volo.Abp.Authorization.AbpAuthorizationException' was thrown" error on the "await _tenantAppService.CreateAsync(input);" line when trying to create a tenant, even though I have the [AllowAnonymous] decorator on the page model and the app service. I am under the assumption that by default, a register page is creating a tenant when in host mode, since a tenant isn't logged in, so this seems like unintuitive functionality. I'm assuming part of this is because the page itself is accessible to non-tenant users, but that is necessary figuring the only people registering will be those who are not yet tenants.

Please advise me on a potential solution to this.

Charlie


3 Answer(s)
  • User Avatar
    0
    AI-Bot created
    Support Team ABP Agent

    To enable anonymous users to register tenants (self-service tenant registration) in ABP, you must explicitly allow anonymous access to the tenant creation endpoint. By default, ABP's SaasTenantAppService (and related endpoints) require authorization, even if you decorate your own app service or page with [AllowAnonymous]. The underlying service still enforces authorization.

    Solution

    1. Override Authorization for Tenant Creation

      You need to override the authorization for the CreateAsync method in your own application layer. You can do this by creating a custom application service that exposes tenant creation and is decorated with [AllowAnonymous], and then route your registration page to use this service.

      Example:

    [AllowAnonymous]
       public class PublicTenantRegistrationAppService : ApplicationService
       {
           private readonly ITenantAppService _tenantAppService;
    
           public PublicTenantRegistrationAppService(ITenantAppService tenantAppService)
           {
               _tenantAppService = tenantAppService;
           }
    
           public async Task<SaasTenantDto> RegisterTenantAsync(SaasTenantCreateDto input)
           {
               // Optionally add your own validation here
               return await _tenantAppService.CreateAsync(input);
           }
       }
    

    Then, in your registration page, call this new service instead of the default ITenantAppService.

    1. Expose the Endpoint in Your Module

      Make sure your new service is exposed as an API endpoint (if using API), or is accessible from your MVC page.

    2. Check Permission Policies

      If you are still getting authorization errors, check if there are permission policies applied to the method or controller. You may need to override or remove them for this endpoint.

    3. ABP Version Note

      In ABP v1.x, the built-in tenant registration UI and APIs are not as flexible as in later versions. If possible, consider upgrading to a newer ABP version, which has improved support for tenant self-registration.

    4. Reference Working Example

      A similar approach and discussion is available in the following thread, which demonstrates how to bypass the default authorization for tenant creation and allow anonymous registration:

      • https://abp.io/support/questions/7847

    Summary:

    • The [AllowAnonymous] attribute on your own app service or page does not override the authorization on the underlying ABP SaasTenantAppService.
    • Create a new application service with [AllowAnonymous] that wraps tenant creation and use it in your registration flow.
    • Ensure no permission policies block anonymous access to this endpoint.

    Sources:

    • https://abp.io/support/questions/7847 (validated)

    This response was generated by AI. If it didn’t solve your problem, feel free to post a new answer — our support team will jump in to help you soon.

  • User Avatar
    0
    cstobler created

    The AI generated response is how I thought this functioned, which is why (as indicated in my OP) I created an app service that is decorated with [AllowAnonymous] and calls _tenantAppService.CreateAsync(input).

    Somehow, this is still throwing an authorization error. I have emailed a Google Drive link to my project files in case you are able to find a bug or some miconfiguration.

    Charlie

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    These appservices will check the current authentication.

    private readonly ITenantAppService _tenantAppService;
    private readonly IEditionAppService _editionAppService;
    private readonly ISubscriptionAppService _subscriptionAppService;
    

    If you want to skip, you can inject the IEditionRepository, EditionManager, ITenantManager, ITenantRepository.

    Thanks.

Boost Your Development
ABP Live Training
Packages
See Trainings
Mastering ABP Framework Book
The Official Guide
Mastering
ABP Framework
Learn More
Mastering ABP Framework Book
Made with ❤️ on ABP v10.0.0-preview. Updated on September 01, 2025, 08:37