0

I am trying to use sessionStorage to store user information which is retrieved at ngInit of AppComponent. But as this retrieval uses httpClient, it is like to be accessed in another component even before the key value is stored. This is the only time this key will be set.

@Injectable()
export class AuthService {

  public currentuser = new BehaviorSubject<User>(<User>JSON.parse(sessionStorage.getItem('currentuser')));

  constructor(private _http: HttpClient, private _trace: TraceService) { }

  public  authenticate(): void {
     this._http.get<User>(AppSettings.AuthUrl, {withCredentials: true})
                      .subscribe(user => {
                        sessionStorage.setItem('currentuser', JSON.stringify(user));
                        this.currentuser.next(user);
                      },
                           e => {
                             if (e.status === 403) {
                              this.currentuser.next(null);
                              this._trace.handleAuthError(e);
                             } else {
                               this.currentuser.next(null);
                               this._trace.handleError('Authenticating...', e);
                             }
                           });

  }
}

In auth guard component I have

@Injectable()
export class AuthGuard implements CanActivate {

  constructor(private _router: Router,
              private _authService: AuthService) { }

  public canActivate() {
    this._authService.currentuser.subscribe(u => {
      if (u === null) {
        this._router.navigateByUrl(AppSettings.NotAuthenticatedRoute);
        return false;
      }});
    return true;
  }
}

routing module

const routes: Routes = [ { path: AppSettings.LegalTermsRoute,  component: LegalTermComponent, canActivate: [AuthGuard] }];

In AppComponent

  ngOnInit(): void {
      this._authService.authenticate();
  }

The issue is that I am not not setting this subscription the right way the auth guard blocks the LegalTermComponent when the app comes up

4
  • Why do you create an observable from it, when you subscribe to it immediately anyways? This doesn't make much sense to be honest. What is your intention to do so? Commented Jan 8, 2018 at 18:45
  • Hi Patrick , I want to retrieve currentuser but it is null. So I want to access it after the authService is set it, but somewhere else in the app. The code I have posted is surely incorrect - but I am learning this stuff. Commented Jan 8, 2018 at 18:48
  • is it possible that your another component is getting initialized first before your AppComponent? Commented Jan 8, 2018 at 18:50
  • AppComponent is always the first component to be initialized. But HttpClient get is not a blocking call - so it is possible the currentuser is used before it is set Commented Jan 8, 2018 at 18:53

2 Answers 2

1

sessionStorage.getItem returns a value, so I'm not sure that you would be able to use Observable.from on it, but I guess it works for you. This is more like Observable.of -- either way, this will convert one value to an Observable that emits immediately and then completes which is not what you want.

You need to create a custom Observable that emits the value when it becomes available. BehaviorSubject works for this since any time you subscribe to it you will get its last value which will allow you to get said value even if you happen to subscribe after the value gets set.

export class AuthService {
  currentuser = new BehaviorSubject<User>(sessionStorage.getItem('currentuser') as User);

  authenticate() {
    // Do whatever authenticate usually does ... this is an example
    this.http.post<User>('/auth').subscribe(user => this.currentuser.next(user));
  }
  /* your other methods and properties ... */

I would also avoid implementing side effects (such as updating session storage) in components and do that in services instead.

Sign up to request clarification or add additional context in comments.

7 Comments

Hi, I should be setting the sessionStorage.setItem('currentuser', JSON.stringify(user)) before this.currentuser.next(user) - is that right?
For me, this solution turns out to be inconsistent, as the Observer somehow does not seem to wait for the Observable and does not authenticate successfully
@Anand you are correct; can you provide more information on what you mean about the Observer does not seem to wait for the Observable? Which Observer / Observable?
when the app comes up the route is blocked - but if I click the route link later - it works - so I think the subscription value that would cause the route to be unblocked is not being available at the time component for the route is initialized.
I am not sure how BehaviorSubject would work here as the initial value sessionStorage.getItem('currentuser') is null. This is available to the AuthGuard upon subscription and this blocks the route. What I think I need if for the AuthGuard is to skip this intial value where there is a subscription
|
0

I figured this so posting what I did as answer just in case this might help someone in my situation, which is simple Windows authentication in the Web API. I use Kendo - so used the loading spinner from there to make things synchronous.

// app.component.ts
  ngOnInit(): void {
  sessionStorage.clear();
  sessionStorage.setItem('requestedRoute', window.location.pathname);
  this._router.navigateByUrl(AppSettings.AuthRoute, {skipLocationChange: true});
}


 // AuthRoute -> auth.component.ts
 @Component({
 selector: 'app-auth',
template: `
<div *ngIf="isAuthenticating()" class="k-i-loading"></div>
<div style="padding:20px"><h4>Authenticating...</h4><div>
<div kendoDialogContainer></div>
`,
styles: []
})
   ngOnInit() {
this._authService.authenticate().subscribe((user: User) => {
  if (user !== undefined) {
    user.isAuthencated = true;
    sessionStorage.setItem('currentuser', JSON.stringify(user));
    this._router.navigateByUrl(sessionStorage.getItem('requestedRoute'));
  }
 });
}

public isAuthenticating() {
  return this._authService.isAuthenticating;
}

 //auth.service.ts
 public isAuthenticating = true; // loading spinner

 constructor(private _http: HttpClient, private _trace: TraceService) { }

 public  authenticate(): Observable<User> {
 this.isAuthenticating = true;
 return this._http.get<User>(AppSettings.AuthUrl, {withCredentials: true})
                    .catch<any, User>((e, u) => this._trace.handleAuthError(e))
                    .catch<any, User>(this._trace.handleError('GET ' + AppSettings.AuthUrl, []))
                    .finally(() => this.isAuthenticating = false);
}

  //auth.guard.ts
  public canActivate(): Observable<boolean> | boolean {
  const user = <User>JSON.parse(sessionStorage.getItem('currentuser'));
  if (user !== null && user.isAuthencated) {
    return true;
  } else {
  this._router.navigateByUrl(AppSettings.NotAuthenticatedRoute);
  return false;
 }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.