Receive user data before proceeding with code execution. ANGULAR

Asked

Viewed 421 times

-1

I’m trying to assign a user to a service, so I can use user information wherever it is needed, just calling the service that will have user information. This information is picked up by http requests.

Problem is because it’s my first time dealing with angular, I’m having trouble with asynchronous executions, so I don’t know what I need to do to solve the problem. Follows code below

Another information: The login is done by Openid, so I check if the user is logged in checking if he has an access token by Session Storage and with the token, I make a request to the backend requesting the user’s data.

The code below is the service that receives the user data and make it available, so that when I inject it in a Component, I can get the user data that is logged.

(Already modified, with the suggestion of the comments).



import { User } from './../../app/modules/pages/loginScreen/models/user.model';
import { Observable, } from 'rxjs';
import { LoginService } from './../../app/core/loginService/login.service';
import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class GlobalEnvironment {

  user: any;

    constructor(private loginService: LoginService
  ){
    this.user = new Observable((observer) => {
      observer.next({

        //var que deve contem o perfil inicial
        //de forma modular e não fixada
        role: [""],

        name: "User",
      })
      this.loginService.login().subscribe(res => {
        observer.next(res)
      })
    });


   }

changeUser(user: User){
  this.user = new Observable((observer) => {
    observer.next(user);
  });

}

}



The problem is that because the angular is asynchronous, when I use the Globalenvironment service in a Component, the user object in the service is still empty(Undefined), it does not wait for the constructor to assign the login service response that brings the user to assign the service user variable, when it assigns the Component where injected already executed all operations with the Undefined user.

How do I make the components in which I inject this service await the full execution of the service Globalenvironment, so that when I use the service, the attribute user of the service is already with the user data logged in.

The code below is from the Header System, where I use the service to take the user profile and define the buttons that appeared to it and also use to take the name of the logged-in user and put in the Header with a Welcome text: nameUser;

But when I use the service it returns me Undefined for any variable, it does not wait for the assignment of the user in the service.

import { GlobalEnvironment } from './../../../../environments/constant/global.environment';
import { LoginService } from './../../../core/loginService/login.service';
import { environment } from 'src/environments/environment';
import { OAuthService } from 'angular-oauth2-oidc';
import { User } from './../../../modules/pages/loginScreen/models/user.model';
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';

@Component({
  selector: 'app-header',
  templateUrl: './header.component.html',
  styleUrls: ['./header.component.css']
})
export class HeaderComponent implements OnInit {
  /*Var user name*/

  name: string;

  profiles: string[];

  constructor(
    private router: Router,
    private oauthService: OAuthService,
    private globalConst: GlobalEnvironment
  ) {

  }

  ngOnInit() {

    this.name = this.globalConst.user.name;


    // Array profiles
    this.profiles = this.globalConst.user.roles;
  }

}

Part of the routing control that is in trouble, in this guard I check if the user is logged in and if his profile is the necessary to access the route, but as the observable has an initial value, it always ends up comparing with the initial value of the observable:

import { GlobalEnvironment } from './../../../environments/constant/global.environment';
import { LoginService } from './../loginService/login.service';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';


import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router';
import { OAuthService } from 'angular-oauth2-oidc';
import { authCodeFlowConfig } from 'src/app/shared/loginConfig/authConfig';

@Injectable({
  providedIn: 'root'
})
export class AuthGuardAdminService implements CanActivate {

   profiles: string[];

  constructor(private oauthService: OAuthService,
              private router: Router,
              private globalConst: GlobalEnvironment) {

                this.globalConst.user.subscribe(res => {

                  this.profiles = res.role;
               }) ;

               }

  canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): Observable<boolean> | boolean {


      if (this.oauthService.getAccessToken() != null && this.profiles.includes("ADMIN")) {

      return true;
      }

    this.router.navigate([authCodeFlowConfig.issuer]);
    return false;
  }




}
  • 1

    There are many ways to do it, depending on your context. But have you tried after logging in, retrieving data, saving to a Store localor a shared service user information? And another, if you do not have logged in user information yet, I believe, that internal system routes should not be shown. If not shown, then the Headercomponent is not loaded.

  • I’m trying to approach the shared service solution. The route control is being done, it shows nothing if you do not have an active session and the session is created by the openid screen, where the user is redirected if you are not logged in, Did you understand? The problem is that I don’t know how to do this service in order to always leave him with the updated information, I was seeing something about using an observable in the variable user of the Globalenviroments service and watching this variable in the other components. It would be something like that?

  • You can use observable too, it’ll work. In my case, after login, I call a service that stores the necessary information in a localStorage/sessionStorage and on the screens that I need this information, I only search for this service, a method that searches for the localStorage/sessionStorage. But the information comes from the login method, that is, when logging in, the answer comes the data I need. But in your case you use Openid, what you can do is, after Openid result, call another method of yours, in your Backend and search for this information.

  • That’s right, after the openid login is done in Openid, the user is redirecting the Home screen where some buttons are displayed according to her profile. The profile picked up by a service that accesses the Backend, collects the user’s information and returns me, so I store them in service with an observable like I had commented. Now the big problem is that in my Canactive, when I take the if I try to validate if the user is logged in and if his profile is what is needed to access the route, it is returning false even with the user having the profile.

  • The point is that it does not wait for the observable to receive the user’s data and validates executes the if with the default values that defined in the observable and if I fail to define an incial pattern the validate returns me null, for not having profile to compare. I will add these classes to the post to be able to understand.

  • Yes, I did. But by the time it redirects to the Home screen, it already runs Canactive. Ideal would be to change the route, after having the user information, soon, will have value the variable and there changes the route, thus passing through Canactive.

Show 1 more comment

1 answer

0


I’ll show you a code I have, sample variables only, follow:

app.component.ts (Main)

userService.login(data).subscribe(userInfo => {
  this.user = userInfo;
  this.authService.setAuthorizedUser(this.user); // Salva informacoes do usuario na SessionStorage
  this.router.navigateByUrl('/dashboard'); // Agora que tem tudo ok, vai para a rota "Home"
}, error => { // Faca o que quiser com seu erro aqui });

Ai now has the part of Canactivateservice:

canActivate() {
    const authInfo = this.authService.getAuthorizedUser();
    if (authInfo && authInfo.roles && authInfo.roles.length > 0) {
        return true;
    }

    return false;
}

In your route file/module:

canActivate: [CanActivateService]

That is, it will only enter the routes, if you have user information. While it does not have this, it will always be on the root route ('./'), on this route, will fall into app.component.ts and have a check whether or not there is a logged-in user.

  • I think I got it. I tried to apply something with save in session and check the profile by session key, the only problem I see in storing in session is to have the possibility of the user to go in the browser Storage and change the values, I try one profile as ADMIN and another as OPERATOR, where if the person is an operator he has the possibility in Storage to change the name from Operator to ADMIN, so he would have access to administrator things and that I see with a big problem. Do we have a solution for that? a sort of amendment block, something like?

  • Yes, Voce can use a service and store this info in a variable. But the ideal is to validate the user roles in the Backend, since that’s where Voce performs the methods/queries, etc. Never trust anything in the Front. If the guy changes and access a screen that is not for him to access, what will block it are the queries in Backend, IE, It will be just something visual, without relevant information.

  • 1

    My Backend already does this validation, I just really wanted to know if there’s any way to block the Session change. But ok, with the profiles in session I managed to solve the navigation problem. Thank you very much

Browser other questions tagged

You are not signed in. Login or sign up in order to post.