Okay.
What we're going to take a look is creating our own register endpoint.
And why do we have to do this?
Well, because I want to send up the username, which actually in this case I would need to change to
at Test.com for this particular user because of the way that the login endpoint works where we send
up an email, but really that gets compared against the username property in our database.
So if we take a look at the documentation for the register endpoint, which is the one above here,
then this only takes an email and a password and it doesn't set the username for the registered user.
So because of the way that the weirdness, in my opinion, the way that this has been set up where we
use the email to Register a user, but then this email address isn't going to work with the login endpoint.
It has to be a username.
Then we're just going to create our own register endpoint.
In this case anyway, the register endpoint doesn't log in a user.
It doesn't send back a cookie.
It doesn't send back anything else.
It simply creates a user in a database.
And we can just go ahead and create that ourselves.
And we do need to generate a few other endpoints of our own to make our login and authentication system
work how we want it in our application.
So let's head back to VS code and we will create a new controller in our application.
And I'll go to the controllers folder I'll say new file.
And this is going to be a class.
And I'm going to call it account controller and press return.
And this is going to derive from our base API controller.
And we're going to create an endpoint in here that's going to be an HTTP post and we're going to call
it Register and we'll say public async task.
And we're just going to return an action result from this without a type, as we're not going to return
anything from this and we'll call it Register user.
Well I say we're not going to return anything from this.
Every HTTP request demands that we send back an HTTP response, but we're not going to return any user
data with this.
So we don't need to specify a type here.
And what are we going to send up as the arguments though for the register user we need to know the username,
the email and the password.
And in fact we don't need the username if we're going to ask them to, or if we're only going to store
the email as the username, we don't even need the username to be sent up.
So I'll adjust that in postman soon.
But for the register, let's go to our Dtos CTOs folder and we'll create a class and I'm just going
to call it Register DTO, which is going to allow us to receive the properties in the body of the request.
So I'm going to have a property of string that's going to be an email.
And I'm also going to have a prop that's a string that's going to be for the password.
Now with this one I would like validation to take place.
I would like the passwords to be validated by identity because it's going to be a complex password.
I would also like the email to be required.
But if I use required here and well let's just do both and we'll just do a quick example of what's going
to happen using this approach to saying that we want these to be required.
It's not going to have quite the effect that I want in this case, but we'll do this temporarily.
Then we'll come back and take a look at a different approach.
So inside the account controller now we've got our registered DTO.
We can use this for our argument.
So I'll say register DTO and I'll call it Register DTO.
And inside this method we'll create the code to create a new user.
So I'll say var user equals and new user which we get from API entities.
And we'll have our username which is going to equal the register DTO dot email and the email which equals
register dto dot email.
Okay, I can say still a bit weird, but that's what we're going to go with.
And then we need to create our new user in the database.
And this time we'll inject something in here.
But we won't inject the user manager in this time we'll actually inject the sign in manager for something
we're going to do a bit later.
But the sign in manager also gives us access to the user manager as well.
Now the sign in manager does what you think it's going to do.
It's going to take in a username password and then compare that password or the hashed version of that
password in our database.
But for a short term we're just going to use this as a user manager.
And I'll show you what I mean very soon.
So I'm going to give this the user as its type.
And I'll call it sign in manager.
And we're going to create a user just like we did on our seed data using the user manager.
So I'm going to specify var results equals await.
And we need the sign in manager.
And from the sign in manager we can get access to our user manager.
And then we can use the create async method.
And we can pass in the user and the register DTO dot password.
And we can check to see if the results and one of the properties we get back from the results we get
back from our user manager is whether or not it succeeded, and we want to check to see it's not succeeded
in this case.
So I'll add the Not operator.
And we want to effectively return the validation errors from this because our result objects will also
contain an errors object with each of the different problems.
So I'm going to loop over that object and see for each error.
VAR error in results dot errors.
Then we're going to use the model state, which we took a look at a bit earlier when we were taking
a look at error handling.
And I'm going to add the model error and say error code and error dot description.
And from here I'm going to return a validation problem.
Now in theory that should give us the email is required passwords not complex enough.
And the different password validation errors we get from this.
I don't think we're going to get that.
I'm doing this just as a quick test really.
Following this, I'm going to use the await sign in manager.
Again use the user manager and we'll use the add to role async.
And we'll put our user in the member role when they do sign up and we will return.
Okay.
So that's our account controller.
And let's see we'll restart our API server.
So I'm just going to use control C and dotnet watch to restart the API server.
And then we'll go back to postman and take a look at a quick test to see how we're doing.
So inside here we don't need the username I'm going to remove that from this request.
So you should not see that on your one.
Going to create a user called Tom at Test.com with the standard password that I have been using.
And just as a quick test, I'm just going to take out the email and I'm going to take out the password
from this request and send this just as it is.
I want to see what we get back.
And okay, so the password is fine.
We've got the errors in there for the password.
We don't see the email one though.
And actually this is not as bad as I thought it would be.
I thought this would be a bit worse.
So we got our password validators.
That's fine.
If I do put in a password that's too weak and click send, what I should see is less validation problems,
but still validation problems, so that's fine as well.
But I'm not seeing the detail about the email is empty.
And what if I did have a complex password but an empty email?
Let's just check this one and click send.
Okay.
So we do get some detail here.
So possibly that's an improvement in dotnet nine.
Do I like this invalid email and invalid username.
Mm.
Let me just demonstrate the way that I was planning to demonstrate this in the first place when it comes
to validating things.
My original plan, and this might well be the plan that I continue with, is to use a data annotation
inside here and specify required for email, with the required coming from data annotations.
Not use the required modifier, but give this an initial value of String.empty and leave data annotation
validation to actually validate the email has been provided, so I'll just restart the API if needed
and doesn't look like it does need to be restarted.
So let's give that a go again.
And yeah, I'll just restart the API because I was expecting a different result than that.
And okay, let's give that another go and click send.
And this is probably my preference.
So where we get an error for the email we just get the email field is required.
And we don't see the stuff about the username for something that I'm sending up just as an email address
here.
So I think that's the approach I'm going to stick with.
But let's make sure that we can register a new user.
So I'm going to put Tom at Test.com back in and I'll click send and we get the 200 okay.
Which means Tom has been created in our database.
And if we go back and try and log in again as this time Tom instead of Bob, then we should get success
and we get the 200.
Okay.
And that cookie will be for the Tom user rather than the Bob user.
Now there are a couple of other endpoints that we need to create.
We surprisingly don't get one to log out, which is designed to remove the cookie from the user's browser.
And we also need one, because we're not going to be able to return a user using this approach.
Using the cookies, we'll need an endpoint so that we can get some user info about the user that has
logged in, and we'll take a look at those next.