#StackBounty: #javascript #security #authentication #typescript #angular-2+ Angular Frontend login logic

Bounty: 50

This is a follow-up to this question Node.js backend login logic. I wrote the following login Angular frontend logic for my Node.js Backend (see the previous question above). Is it any good in terms of security, efficiency, building, async/sync, logging? SECURITY is my main concern.
On in a prettier format the question would be:

  • SECURITY: Is my website secure in any way, shape, or form? I’m wondering if I could implement any security measures other than the ones that are built in to the methods provided by Angular. Isn’t the transmission of the password in plaintext a security issue? What about XSS and similar troubles? Can’t my login just simply be circumvented? That would be a critical mistake.
  • EFFICIENCY: Is how I’m checking usernames and password efficient? Is there any better way to do this?
  • BUILDING: Is how I loaded my website acceptable?
  • ASYNC/SYNC: I know I preform async and sync calls at the same time. Is there any problem to this?
  • LOGGING: I log all connections to the server, and all login attempts. Is this a good practice, or am I overdoing what logging is supposed to accomplish?
  • MISC: Are there any mistakes in the play between the backend and frontend? If I forgot some other important points about the code I would be glad if you mentioned them as well
    (Source: Login Server with Node.js)

My code:

authentication.service.ts:

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { BehaviorSubject, Observable } from 'rxjs';
import { map } from 'rxjs/operators';

import { environment } from '../../environments/environment';
import { User } from '../models/user.model';
import { Router } from '@angular/router';
import { GlobalDataService } from './global-data.service';

@Injectable({ providedIn: 'root' })
export class AuthenticationService {
    constructor(private http: HttpClient,
                private router: Router, public DataService: GlobalDataService) {
        this.currentUserSubject = new BehaviorSubject<User>(JSON.parse(localStorage.getItem('currentUser')));
        this.currentUser = this.currentUserSubject.asObservable();
        this.LoggedIn = true;
    }
    public LoggedIn = true;
    public get currentUserValue(): User {
        return this.currentUserSubject.value;
    }
    private currentUserSubject: BehaviorSubject<User>;
    public currentUser: Observable<User>;
  getRedirectUrl() {
    throw new Error('Method not implemented.');
  }
  isUserLoggedIn() {
    throw new Error('Method not implemented.');
  }

  login(email: string, password: string) {
    return this.http.post<any>(`${environment.apiUrl}/api/login`, { email, password }, {withCredentials: true})
        .pipe(map(user => {
            // login successful if there's a jwt token in the response
            if (user && user.token) {

                // store user details and jwt token in local storage to keep user logged in between page refreshes
                // https://dev.to/rdegges/please-stop-using-local-storage-1i04
                localStorage.setItem('currentUserToken', JSON.stringify(user));
                this.currentUserSubject.next(user);
            }
            // set firstname & email of loggedin user
            this.DataService.loggedinfirstname = user['firstname'];
            this.DataService.loggedinemail = user['eMail'];
            this.redirtoDashboard();
            this.Toolbar();
            this.DataService.prefillSenderData();
            return user;
        }));
  }

  redirtoDashboard() {
      this.router.navigate(['order']);
  }

  Toolbar() {
      this.LoggedIn = !this.LoggedIn;
  }
}

login.component.ts:

import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { first } from 'rxjs/operators';

import { AuthenticationService } from '../services/authentication.service';

@Component({
  selector: 'app-login',
  templateUrl: './login.component.html',
  styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {

  returnUrl: string;
  loginForm: FormGroup;
  submitted = false;
  error = '';
  loading = false;
  public errorMsg = 'Please login to continue.';
  public redirected: boolean;
  public utm_source: string;

  constructor(private router: Router, private formBuilder: FormBuilder,
              private authenticationService: AuthenticationService, private activatedRoute: ActivatedRoute) {
      if (this.authenticationService.currentUserValue) {
        this.router.navigate(['order']);
    }
      this.activatedRoute.queryParams.subscribe(params => {
      const param = params['utm_source'];

      if (param === 'order' || param === 'work-document' || param === 'profile') {
        this.redirected = true;
        this.utm_source = param;
      } else {
        this.redirected = false;
      }
  });
  }

  ngOnInit(): void {
    this.loginForm = this.formBuilder.group({
      email: ['', [Validators.required, Validators.email]],
      password: ['', [Validators.required, Validators.minLength(6)]]
  });
  }

// convenience getter for easy access to form fields
get f() { return this.loginForm.controls; }

  onSubmit(loginsubmit) {
    this.submitted = true;
    // stop here if form is invalid
    if (this.loginForm.invalid) {
        return console.log('LoginForm Invalid');
    }
    this.loading = true;
    this.authenticationService.login(this.f.email.value, this.f.password.value)
        .pipe(first())
        .subscribe(
            data => {
                if (this.redirected) {
                  this.router.navigate([this.utm_source]);
                } else {
                  this.router.navigate(['order']);
                }

            },
            error => {
                console.log('Login->authservice->err: ', error);
                this.error = error;
                this.loading = false;
            });
}

}

login.component.html:

<div class="container">
  <div class="row">
    <div class="col-sm-9 col-md-7 col-lg-5 mx-auto">
      <div class="card card-signin my-5">
        <div class="card-body">
          <h5 class="card-title text-center">Login</h5>
          <br>
            <form [formGroup]="loginForm" class="form-signin" (ngSubmit)="onSubmit(this.loginForm.value)">
              <div class="form-label-group">
                <input #userName formControlName="email" type="text" id="inputUser" class="form-control" placeholder="E-Mail" required autofocus [ngClass]="{ 'is-invalid': submitted && f.email.errors }">
                  <div *ngIf="submitted && f['email'].errors" class="invalid-feedback">
                    <div *ngIf="f['email'].errors.required">E-Mail is required</div>
                  </div>
                </div>
                <br>
                  <div class="form-label-group">
                    <input #password type="password" formControlName="password" id="inputPassword" class="form-control" placeholder="Password" required [ngClass]="{ 'is-invalid': submitted && f.password.errors }">
                      <div *ngIf="submitted && f['password'].errors" class="invalid-feedback">
                        <div *ngIf="f['password'].errors.required">Password is required</div>
                      </div>
                    </div>
                    <br>
                      <div *ngIf="redirected">
                        <mat-error>
                          <p class="alert alert-danger">
                            {{errorMsg}}
                          </p>
                        </mat-error>
                      </div>
                      <button [disabled]="!loginForm.valid" class="btn btn-dark btn-block" id="loginSubmit" type="submit">Login</button>
                      <div class="forgot-password-link">
                        <a routerLink="/forgot-password">Forgot password</a>
                      </div>
                    </form>
                  </div>
                </div>
              </div>
            </div>
          </div>

```


Get this bounty!!!

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.