Skip to content
This repository was archived by the owner on Jul 10, 2024. It is now read-only.

Commit 56e2655

Browse files
davidfowlDamianEdwards
authored andcommitted
Half updated section 4
1 parent 9926b70 commit 56e2655

File tree

1 file changed

+24
-68
lines changed

1 file changed

+24
-68
lines changed

docs/4. Add auth features.md

Lines changed: 24 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -107,48 +107,47 @@ In this module we're going to add the capability for users to register and sign-
107107
```
108108

109109
### Allow creation of an admin user
110-
> Let's make it so the site allows creation of an admin user when there isn't one already, but only if the user also has a special single-use creation key. That way, we can easily create an admin user without access to the database when we first run the app in any environment.
110+
> Let's make it so the site allows creation of an admin user when there isn't one already. The first user to access the site will be deemed the administrator.
111111

112112
1. Create a new class `AdminService` in the `Services` folder. This class will be responsible for managing the creation key generation and tracking whether the site should allow creating admin users.
113113
1. Add code to the class that will create an appropriately long creation key and expose it via a property:
114114
``` c#
115-
private readonly Lazy<long> _creationKey = new Lazy<long>(() => BitConverter.ToInt64(Guid.NewGuid().ToByteArray(), 7));
116-
private readonly IdentityDbContext _dbContext;
117-
private bool _adminExists;
118-
119-
public AdminService(IdentityDbContext dbContext)
115+
public class AdminService
120116
{
121-
_dbContext = dbContext;
122-
}
117+
private readonly IdentityDbContext _dbContext;
123118

124-
public long CreationKey => _creationKey.Value;
119+
private bool _adminExists;
125120

126-
public async Task<bool> AllowAdminUserCreationAsync()
127-
{
128-
if (_adminExists)
121+
public AdminService(IdentityDbContext dbContext)
129122
{
130-
return false;
123+
_dbContext = dbContext;
131124
}
132-
else
125+
126+
public async Task<bool> AllowAdminUserCreationAsync()
133127
{
134-
if (await _dbContext.Users.AnyAsync(user => user.IsAdmin))
128+
if (_adminExists)
135129
{
136-
// There are already admin users so disable admin creation
137-
_adminExists = true;
138130
return false;
139131
}
132+
else
133+
{
134+
if (await _dbContext.Users.AnyAsync(user => user.IsAdmin))
135+
{
136+
// There are already admin users so disable admin creation
137+
_adminExists = true;
138+
return false;
139+
}
140140

141-
// There are no admin users so enable admin creation
142-
return true;
141+
// There are no admin users so enable admin creation
142+
return true;
143+
}
143144
}
144145
}
145146
```
146147
1. Extract an interface from the class and call it `IAdminService`
147148
``` c#
148149
public interface IAdminService
149150
{
150-
long CreationKey { get; }
151-
152151
Task<bool> AllowAdminUserCreationAsync();
153152
}
154153
```
@@ -164,7 +163,7 @@ In this module we're going to add the capability for users to register and sign-
164163
```
165164
dotnet aspnet-codegenerator identity --dbContext FrontEnd.Data.IdentityDbContext --files Account.Register
166165
```
167-
1. Update the `RegisterModel` class in the `Register.cshtml.cs` file to accept `IAdminService` and `IdentityDbContext` parameters and save them to local members:
166+
1. Update the `RegisterModel` class in the `Register.cshtml.cs` file to accept `IAdminService` as a parameter and it them to a local member:
168167
``` c#
169168
[AllowAnonymous]
170169
public class RegisterModel : PageModel
@@ -174,52 +173,30 @@ In this module we're going to add the capability for users to register and sign-
174173
private readonly ILogger<RegisterModel> _logger;
175174
private readonly IEmailSender _emailSender;
176175
private readonly IAdminService _adminService;
177-
private readonly IdentityDbContext _dbContext;
178176

179177
public RegisterModel(
180178
UserManager<User> userManager,
181179
SignInManager<User> signInManager,
182180
ILogger<RegisterModel> logger,
183181
IEmailSender emailSender,
184-
IAdminService adminService,
185-
IdentityDbContext dbContext)
182+
IAdminService adminService)
186183
{
187184
_userManager = userManager;
188185
_signInManager = signInManager;
189186
_logger = logger;
190187
_emailSender = emailSender;
191188
_adminService = adminService;
192-
_dbContext = dbContext;
193189
}
194190

195191
...
196192
```
197-
1. Add a `bool` property to the page model to indicate to the page whether admin creation is currently allowed:
198-
``` c#
199-
public bool AllowAdminCreation { get; set; }
200-
```
201-
1. Add an `AdminCreationKey` property to to the page's `InputModel` class to capture the submitted key for creating the admin user:
202-
``` c#
203-
[DataType(DataType.Password)]
204-
[Display(Name = "Admin creation key")]
205-
public long? AdminCreationKey { get; set; }
206-
```
207-
1. Add code to the `OnGet` method to use the `IAdminService` to see if admin creation is enabled and log the creation key if so. You'll also need to change the method to be async by updating the method signature to the following: `public async Task OnGetAsync(string returnUrl = null)`
193+
194+
1. Add code to the `OnPostAsync` that marks the new user as an admin if the `IAdminService.AllowAdminUserCreationAsync` returns true before creating the user:
208195
``` c#
209196
if (await _adminService.AllowAdminUserCreationAsync())
210-
{
211-
AllowAdminCreation = true;
212-
_logger.LogInformation("Admin creation is enabled. Use the following key to create an admin user: {adminKey}", _adminService.CreationKey);
213-
}
214-
```
215-
1. Add code to the `OnPostAsync` that marks the new user as an admin if the admin creation key was submitted and matches the in the `IAdminService`, before creating the user:
216-
``` c#
217-
if (await _adminService.AllowAdminUserCreationAsync() && Input.AdminCreationKey == _adminService.CreationKey)
218197
{
219198
// Set as admin user
220199
user.IsAdmin = true;
221-
// In the event user creation fails in the next few lines, set this so the admin key box still shows up on the retry page
222-
AllowAdminCreation = true;
223200
}
224201

225202
var result = await _userManager.CreateAsync(user, Input.Password);
@@ -235,34 +212,13 @@ In this module we're going to add the capability for users to register and sign-
235212
_logger.LogInformation("User created a new account with password.");
236213
}
237214
```
238-
1. Update the registration form to allow entering the admin creation key by adding a text input to the end of `Register.cshtml`:
239-
``` html
240-
...
241-
<div class="form-group">
242-
<label asp-for="Input.ConfirmPassword"></label>
243-
<input asp-for="Input.ConfirmPassword" class="form-control" />
244-
<span asp-validation-for="Input.ConfirmPassword" class="text-danger"></span>
245-
</div>
246-
@if (Model.AllowAdminCreation)
247-
{
248-
<div class="form-group">
249-
<label asp-for="Input.AdminCreationKey"></label>
250-
<input asp-for="Input.AdminCreationKey" class="form-control" />
251-
<span asp-validation-for="Input.AdminCreationKey" class="text-danger"></span>
252-
</div>
253-
}
254-
<button type="submit" class="btn btn-primary">Register</button>
255-
</form>
256-
...
257-
```
258215

259216
> If you run the app at this point, you'll see an exception stating that you can't inject a scoped type into a type registered as a singleton. This is the DI system protecting you from a common anti-pattern that can arise when using IoC containers. Let's fix the `AdminService` to use the scoped `IdentityDbContext` correctly.
260217

261218
1. Open the `AdminService.cs` file and change the code to accept an `IServiceProvider` instead of the `IdentityDbContext` in its constructor:
262219
``` csharp
263220
public class AdminService : IAdminService
264221
{
265-
private readonly Lazy<long> _creationKey = new Lazy<long>(() => BitConverter.ToInt64(Guid.NewGuid().ToByteArray(), 7));
266222
private readonly IServiceProvider _serviceProvider;
267223

268224
private bool _adminExists;

0 commit comments

Comments
 (0)