Persisting user and user profile in Angular and data changes after consulting it

Yesterday I encountered a very peculiar bug which I have not been able to solve. I essentially created services to handle user authentication and data management. I also created an additional service to handle cookies for data persistence. The issue begins when a Rout Guard executes the cookie service.
This is mainly because the cookie service uses document.cookies as this is what chatgpt suggested. I really didn’t consider this to be appropriate, knowing this was a potential issue. I also didn’t like the ngx-cookie-service approach to this solution. That’s why I opted to do it myself. When I execute the code, everything works as expected. I’m able to log in, logout and move around the page. The guard does its job as expected. However, I get an error saying the document isn’t available in the Guard’s context. Here is the error code

As you can see the error comes from the cookie service. Specifically from the following code (not full code just the relevant part)

import { Injectable } from '@angular/core';

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

  constructor() { }

  /**
   * Gets an object like cookie
   * @param name name of the cookie to get
   * @returns the value of the cookie requested or undefined
   */
  public getCookie(name:string):object|undefined{
    if(document!==undefined){
      const cookies = document.cookie.split("; ")
      for (const cookie of cookies){
        const [key,value] = cookie.split("=")
        if (key === name){
          try{
            
            let values = JSON.parse(decodeURIComponent(value));
            console.log("Values", values)
            return values;
          }catch(error){
            console.error('Error parsing cookie value:', error);
            return undefined;
          }
        }
      }
      console.log(`Cookie with name "${name}" not found.`);
      return undefined;
    }else{
      console.error("DOM not found")
      return undefined;
    }
  }
  

Next I will provide the Guard code and the available Routs

export const authGuard: CanActivateFn = (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => {
  const firebaseService = inject(FirebaseService);
  const router = inject(Router);
  if(firebaseService.haveUser()){
    return true;
  }else{
    return router.parseUrl('/');
  }
};

export const sessionGuard: CanActivateFn = (routes:ActivatedRouteSnapshot, state:RouterStateSnapshot)=>{
  const firebaseService = inject(FirebaseService);
  const router = inject(Router);
  if(!firebaseService.haveUser()){
    return true;
  }else{
    return router.parseUrl('/Oportunidades');
  }
}

Now the routes

export const routes: Routes = [
    {path:'', redirectTo:"/IniciarSession",pathMatch:'full'},
    {path:'IniciarSession',component:LoginComponent,canActivate:[sessionGuard]},
    {path:'Oportunidades',component:OportunidadesComponent, canActivate:[authGuard]},
]

In order to see the use of the cookie service you will need to see part of the firebase service

@Injectable({
  providedIn: 'root'
})

export class FirebaseService {
  private app:FirebaseApp;
  protected db: Firestore;
  private auth:Auth;
  protected userCredential?:UserCredential;


  constructor(private cookie_service:CookieService) {
    this.app = initializeApp(environment.firebaseConfig);
    this.db = getFirestore(this.app);
    this.auth = getAuth(this.app);
  }
  //Falta agregar que revise si la sesion sigue activa
  public haveUser():boolean{
    this.checkPersistance()
    if(this.userCredential){
      return true;
    }else{
      return false;
    }
  }
  
  private checkPersistance():void{
    const credentials = this.cookie_service.getCookie("userCredentials")
    if(credentials){
      this.userCredential=credentials as UserCredential;
    }else{
      console.log("No user persisted")
    }
  }
}

I ignored the error for a while because the code just works. I don’t know why it just does. Of course, I knew this would cause problems later on and not soon after I stumbled with this nasty bug. Where I do get the expected data, and suddenly it changes and gives me a weird promise. This happens when I’m saving a cookie using the profile service, which is dedicated to handling only profile data. Which is then called by the login component to save it into a cookie. Which latter the cookie gets called and the error begins.

@Injectable({
  providedIn: 'root'
})
export class ProfileService extends FirebaseService {

  constructor(cookie_service:CookieService) {
    super(cookie_service);
  }


  public async getUserProfile():Promise<DocumentData|null|undefined>{
    if(this.userCredential){
      const usersCollection = collection(this.db,"users");
      const userDoc = doc(usersCollection,this.userCredential.user.uid);
      const userDocSnap = await getDoc(userDoc)
      if(userDocSnap.exists()){
        return userDocSnap.data();
      }else{
        return null
      }
    }else{
      return null
    }
  }


  
}

This is called in the login component, which then redirects you to the starting page. And on the starting page is where we try to access the user profile cookie. Which does work at first but the suddenly changes value, and I’m unable to use it.

@Component({
  selector: 'app-login',
  standalone: true,
  imports: [MagicInputComponent,ReactiveFormsModule,ToasterComponent],
  templateUrl: './login.component.html',
})
export class LoginComponent {
  loginForm = new FormGroup({
    username: new FormControl(''),
    password: new FormControl('')
  });

  @ViewChild(ToasterComponent) ToasterTools!:ToasterComponent;


  constructor(private router:Router, private auth:FirebaseService, private profile: ProfileService, private cookie_service:CookieService){}

  async onSubmit(){
    let username = this.loginForm.get("username")!.value as string;
    let password = this.loginForm.get("password")!.value as string;
    try{
      await this.auth.Login(username,password);
      this.ToasterTools.setKind(ToastKind.success);
      this.ToasterTools.setTitle("Inicio de Session exitoso");
      this.ToasterTools.setMessage("");
      this.ToasterTools.showToast();
      const user_profile = this.profile.getUserProfile(); //<--- get the user profile from firebase
      if(user_profile!=null){
        await this.cookie_service.setCookie("user_profile", user_profile,undefined ,1); //<-- Here we set the user_profile cookie
      }else{
        this.ToasterTools.setKind(ToastKind.error);
        this.ToasterTools.setTitle("Error");
        this.ToasterTools.setMessage("La carga de tu perfil fallo, intentelo denuevo mas tarde");
        this.ToasterTools.showToast();
        return;
      }
      this.router.navigate(["/Oportunidades"]);
    }catch(error){
      this.ToasterTools.setKind(ToastKind.error);
      this.ToasterTools.setTitle("Error");
      this.ToasterTools.setMessage("Usuario o contraseña incorrectos");
      this.ToasterTools.showToast();
      console.error(error);

    }
  }
  

}

Here is the sarting page component

@Component({
  selector: 'app-oportunidades',
  standalone: true,
  imports: [UserDataHeaderComponent,PropertyCardComponent],
  templateUrl: './oportunidades.component.html',
  styleUrl: './oportunidades.component.css'
})
export class OportunidadesComponent implements OnInit {
  cards = [1,2,3,4]

  constructor(private cookie_service:CookieService){}

  ngOnInit(): void {
    const value = this.cookie_service.getCookie("user_profile")
    console.log("Client value",value)
    
  }
}

In the following image, you are seeing the output of the console logs of the cookie. As you can see, the value from the getCookies of the cookie service just suddenly changes value without me being able to use the original value which is the correct one. Then the client receives the incorrect one.

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật