/* This file is part of the Notesnook Sync Server project (https://notesnook.com/) Copyright (C) 2023 Streetwriters (Private) Limited This program is free software: you can redistribute it and/or modify it under the terms of the Affero GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the Affero GNU General Public License for more details. You should have received a copy of the Affero GNU General Public License along with this program. If not, see . */ using System; using System.Collections.Generic; using System.Linq; using System.Security.Claims; using System.Threading.Tasks; using AspNetCore.Identity.Mongo.Model; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using Streetwriters.Common; using Streetwriters.Common.Models; using Streetwriters.Identity.Enums; using Streetwriters.Identity.Interfaces; using Streetwriters.Identity.Models; using Streetwriters.Identity.Services; namespace Streetwriters.Identity.Controllers { [ApiController] [Route("signup")] public class SignupController : IdentityControllerBase { public SignupController(UserManager _userManager, IEmailSender _emailSender, SignInManager _signInManager, RoleManager _roleManager, IMFAService _mfaService) : base(_userManager, _emailSender, _signInManager, _roleManager, _mfaService) { } private async Task AddClientRoleAsync(string clientId) { if (await RoleManager.FindByNameAsync(clientId) == null) await RoleManager.CreateAsync(new MongoRole(clientId)); } [HttpPost] [AllowAnonymous] public async Task Signup([FromForm] SignupForm form) { var client = Clients.FindClientById(form.ClientId); if (client == null) return BadRequest(new string[] { "Invalid client id." }); await AddClientRoleAsync(client.Id); // email addresses must be case-insensitive form.Email = form.Email.ToLowerInvariant(); form.Username = form.Username?.ToLowerInvariant(); if (!await EmailAddressValidator.IsEmailAddressValidAsync(form.Email)) return BadRequest(new string[] { "Invalid email address." }); var result = await UserManager.CreateAsync(new User { Email = form.Email, EmailConfirmed = false, UserName = form.Username ?? form.Email, }, form.Password); if (result.Errors.Any((e) => e.Code == "DuplicateEmail")) { var user = await UserManager.FindByEmailAsync(form.Email); if (!await UserManager.IsInRoleAsync(user, client.Id)) { if (!await UserManager.CheckPasswordAsync(user, form.Password)) { // TODO await UserManager.RemovePasswordAsync(user); await UserManager.AddPasswordAsync(user, form.Password); } await UserManager.AddToRoleAsync(user, client.Id); } else { return BadRequest(new string[] { "Invalid email address." }); } return Ok(new { userId = user.Id.ToString() }); } if (result.Succeeded) { var user = await UserManager.FindByEmailAsync(form.Email); await UserManager.AddToRoleAsync(user, client.Id); if (Constants.IS_SELF_HOSTED) await UserManager.AddClaimAsync(user, UserService.SubscriptionTypeToClaim(client.Id, Common.Enums.SubscriptionType.PREMIUM)); var code = await UserManager.GenerateEmailConfirmationTokenAsync(user); var callbackUrl = Url.TokenLink(user.Id.ToString(), code, client.Id, TokenType.CONFRIM_EMAIL, Request.Scheme); await EmailSender.SendConfirmationEmailAsync(user.Email, callbackUrl, client); return Ok(new { userId = user.Id.ToString() }); } return BadRequest(result.Errors.ToErrors()); } } }