Comp-490-SeniorProject / site

MIT License
0 stars 1 forks source link

registration form component issue 33 #43

Closed alinaah closed 2 years ago

alinaah commented 2 years ago

In "AuthService" I created the constructor, and noticed that the singIn implementation was not done. I have added the http request to the backend and processed the response for login case. Let me know if it conflicts with your code/plans.

public async signIn(userData: User) { const response = await this.http .post(${environment.serverURL}/sign-in, userData) .toPromise()

    if (!response?.access_token) {
        throw new Error("Invalid response body!")
    }

    localStorage.setItem("ACCESS_TOKEN", response.access_token)
    this.stateChanges.next(true)
}
MarkKoz commented 2 years ago

The registration endpoint will be at /api/accounts/register/. A POST request has to be made; it expects data to be JSON:

{
    "password": "..",
    "username": "...",
    "password_confirm": "..."
}

So, the front end needs to ask for the password to be repeated. The registration may fail if the passwords don't match or if the password is "too common". It will probably also fail if the username is already taken. I don't remember what the exact HTTP status codes are for those responses, but I will get back to you with that information later. However, I do know that on success the status code is 201.

By default, I don't think an e-mail can be given to the API, but I will be researching how to configure that.

Registration will not log in the user. For that, there is a separate login endpoint. I believe it is /api/accounts/login/. I haven't done enough research into that endpoint, and plan to do that research next. Then I will let you know how authorization needs to be handled.

alinaah commented 2 years ago
  1. I will update registration endpoint shortly in my Pull Request. Still I need the server url, under what domain the backend is deployed, now I put it “https://exmaple.com

  2. About confirm password - I covered this via Angular ngIf directive- attribute which works essentially as an “if” statement for HTML. You can see it in sign-up.component.html

    
    `<p
      [ngClass]=“{
        ‘has-error’: isSubmitted && formControls?.confirmPassword?.errors
      }”
    >
    <div
      *ngIf=“isSubmitted && formControls?.confirmPassword?.errors”
      class=“help-block”
    >
      <div *ngIf=“formControls?.confirmPassword?.errors?.required”>
        Confirm your password
      </div>
      <div *ngIf=“formControls?.confirmPassword?.errors?.invalid”>
        Not matching
      </div>
    </div>`

3. Registration POST request logic: 
 IMO(In My Opinion) registration creates a new user in the backend, generates a token for it, and returns already logged-in user data to the frontend. There is a separate login endpoint for another user case when user is already logged in, comes to the site e.g. second time, and taps on the Login button directly from the UI.
It means when I do a registration request, I do not have to do another login request, login should be and required to be included in the registration. 
Registration = create new user + login + return user data
Login = find user by email and password + return user data
MarkKoz commented 2 years ago

IMO(In My Opinion) registration creates a new user in the backend, generates a token for it, and returns already logged-in user data to the frontend. There is a separate login endpoint for another user case when user is already logged in, comes to the site e.g. second time, and taps on the Login button directly from the UI. It means when I do a registration request, I do not have to do another login request, login should be and required to be included in the registration.

I think the reason django-rest-registration does not do it this way is because it needs to suppose intermediary steps like e-mail verification (if we enable it). I'm not sure if it's feasible to modify the API to log in upon registration. If it is a problem to just make a separate request to the login endpoint, then I can look into how to login upon registration from the back end.

MarkKoz commented 2 years ago

For the registration endpoint, there are some optional fields. You could still add fields to the form for these, but leave them as optional rather than required:

Still I need the server url, under what domain the backend is deployed, now I put it https://exmaple.com

We don't know yet. Are you not able to just use a relative URL, so that the current host is prepended?

MarkKoz commented 2 years ago

Logging In

The login API is fairly simple. You send a POST request to /api/accounts/login/. The data should be JSON, which contains login and password keys. For login, currently only the username is allowed; users cannot log in using their email, but this can be changed later if we want.

If login is successful, it will return a 200 status code. It will also set two cookies: csrftoken and sessionid. The CSRF token will be important for making authenticated requests later.

Example Request

Here is the code I pasted into my browser's console to try out the endpoint. Note that this uses the JS fetch API, which is part of the language. Angular probably has its own way of doing this, so just take this as an example rather than thinking you need to do it exactly like this.

(async () => {
    let response = await fetch(
        "/api/accounts/login/",
        {
            method: "POST",
            headers: {
                'Accept': 'application/json',
                'Content-Type': 'application/json',
            },
            body: JSON.stringify({
                login: "user2",
                password: "9023fj32",
            }),
        }
    );
    let content = await response.json();
    console.log(content);
})();

Logging Out

Logging out is a bit more complex, since it is an authenticated endpoint. Authentication works by providing the CSRF token in the request's header. The CSRF token comes from the cookie described earlier, which was generated by logging in.

Django's documentation describes how to get the token and how to pass it to the request. Note that Angular may have an easier way to retrieve cookies, so look into that. If not, maybe it's worth using the cookie library that Django's documentation recommends.

For now, Django is configured with the default, which is to store the CSRF token in a cookie. Maybe later I will investigate the alternative of using a session instead. However, that isn't a priority, so you can be safe to assume it's a cookie for the time being.

Example Request

function getCookie(name) {
    let cookieValue = null;
    if (document.cookie && document.cookie !== '') {
        const cookies = document.cookie.split(';');
        for (let i = 0; i < cookies.length; i++) {
            const cookie = cookies[i].trim();
            // Does this cookie string begin with the name we want?
            if (cookie.substring(0, name.length + 1) === (name + '=')) {
                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                break;
            }
        }
    }
    return cookieValue;
}

(async () => {
    let response = await fetch(
        "/api/accounts/logout/",
        {
            method: "POST",
            headers: {
                'Accept': 'application/json',
                'X-CSRFToken': getCookie('csrftoken'),
            }
        }
    );
    let content = await response.json();
    console.log(content);
})();
MarkKoz commented 2 years ago

Note that in #45 I have also added support for logging in with email. Thus, the user can log in either with an email or with their username.

MarkKoz commented 2 years ago

Superseded by #63