I've been stuck on this for a couple days now and need some help/guidance of how to check if a user is authenticated in my angular-rc1 app.
I'm getting a token back from my server and would like to store it in local storage (unless someone has a better idea of persisting the token in the app). The main problem I'm having is that when the token changes, I'm not able to detect that change in my components.
Through reading about 20 SO posts/articles, I think I've found the best way is to create an observable in my user.service.ts
and subscribe to that observable in my components that I need to detect when the user changes their token.
import {BehaviorSubject} from 'rxjs/BehaviorSubject';
export class UserService {
// Observable userToken source
_userToken = new BehaviorSubject<string>("");
// Observable userToken stream
userToken$ = this._userToken.asObservable();
// set the user token in a way that is observable for subscribed components
setUserToken(token) {
this._userToken.next(token);
}
...
}
import {Subscription} from 'rxjs/Subscription';
export class NavBarComponent implements OnInit{
subscription: Subscription;
...
ngOnInit(){
this.subscription = this._userService.userToken$.subscribe(
userToken => {
console.log('setting user token on navbar to ' + userToken);
this.userToken = userToken
});
}
}
export class LoginComponent implements OnInit {
...
ngOnInit() {
/*
Get the authentication code from twitch and then send that authentication
code to the server. The server does stuff and then either passes back a
200 with key token in data, or passes back an error 4xxx code.
*/
var routeParams:any = this._routeParams.params
if(routeParams.code){
this.isLoading = true;
this._userService.authenticateTwitch(routeParams.code)
.subscribe(resp => {
this._userService.setUserToken(resp.key);
this.isLoading = false;
this._router.navigate(['Search']);
});
}
}
}
Ok, so the problem with this method is that when the LoginComponent fires this._userService.setUserToken(resp.key)
, the subscribe event is not seen in NavbarComponent. When NavbarComponent is initialized, its console.log in the subscribe method fires, but it does not do anything when the token changes in LoginComponent.
I feel pretty good about Method #1 and I think that's the right way to go. It's just the subscribe is not picking up the change for some reason.
Here I'm using local storage directly using "angular2-localstorage": "^0.4.0"
. I make a new service to handle the local storage change and then subscribe to that Observable in my components
export class StorageService {
public userToken$: Observable<string>;
private _userTokenObserver;
private _userToken;
constructor() {
this._userToken = "";
this.userToken$ = new Observable(observer => {
this._userTokenObserver = observer;
}).share();
}
add(value: string) {
console.log('setting userToken to ' + value);
this._userToken = value;
this._userTokenObserver.next(this._userToken);
}
load() {
this._userTokenObserver.next(this._userToken);
}
}
export class NavbarComponent {
...
ngOnInit() {
console.log('navbar init');
this._storageService.userToken$.subscribe(latestUserToken => {
console.log('subscribe fired on navbar with latest user token: ', latestUserToken);
this.userToken = latestUserToken;
console.log('user token on nav component is now: ', this.userToken);
});
this._storageService.load();
}
}
I also have similar ngOnInit
code in my LoginComponent
. This method is interesting because the subscribe kinda works in the LoginComponent
, but not in the NavbarComponent
.
I say "kinda" because I don't think the subscribe actually worked. It appears the NavbarComponent
is initialized first and the token is not ready, whereas the LoginComponent is initialized after the token is available. If the token changes (such as logging out and clearing the token), the subscribe method does not pick up the change.
...at least on the LoginComponent
! Logout is on the NavbarComponent
, and the subscribe event fires on the NavbarComponent. So it seems the event is being contained just to the component where it happens rather than being shared to all the components that subscribe to that event.
navbar init
navbar.component.ts:34 subscribe fired on navbar with latest user token:
navbar.component.ts:36 user token on nav component is now:
login init
user.service.ts:31 authenticating against server with twitch token: aaaaa
storage.service.ts:19 setting userToken to bbbbb
login.component.ts:42 subscribe fired in login componenet with latest usertoken: bbbbb login.component.ts:44 use
r token on login component is now: bbbbbb
I thought one possible reason for these problems is that all my components are rendered through RouterOutlet, and my NavbarComponent is rendered straight in the app.template.html
. However, I tried moving the navbar into my components, but it still had the same behavior.
<!-- app.template.html -->
<hero-navbar></hero-navbar>
<router-outlet></router-outlet>
Another thing might be that the subscribe shouldn't go in the ngOnInit
function. It seems the code runs once, but then doesn't run again. I think it's more likely the problem is in the underlying observer/subscribe relationship though...
If you have read this whole post, I congratulate you and deeply thank you for taking the time to read this, it's by far my longest SO post. Please let me know if I can provide any more info to help give ideas on how to solve this problem.
canActivate: [AuthGuard, AdminRoleGuard] An AuthGuard that will check if the user is logged in and redirect to a login screen if that's not the case. An AdminRoleGuard will check if the current user is an administrator and redirect to an error screen if that's not the case.
Open command prompt and go to project root folder. Start the application. Create a new service, AuthService to authenticate the user. Open AuthService and include below code.
I faced the same issue on my angular2 app. I solved it by using a common ApiService that adds the required information to the request (ie. Authorization header). Other services that make calls to my API, extend from this service.
When a user logs in, I save the token in the local storage AND set the attribute 'isAuthenticated' to true on my AppState. I also pass that attribute to my header in my app.component.html. This way your header will update automatically whenever a user logs in/out.
app.component.ts:
constructor(public appState: AppState) {
appState.state.isAuthenticated = false;
}
app.component.html:
<navbar [isAuthenticated]="appState.state.isAuthenticated"></navbar>
navbar.component.ts:
@Input() isAuthenticated:boolean;
auth.service.ts:
return this.http.post(this.baseUrl, body, { headers }).subscribe((result) => {
localStorage.setItem('oauth', JSON.stringify(result.json()));
this.appState.set('isAuthenticated', true);
return result;
});
I hope this helps!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With