I am trying to implement a frontend with angular, but I am not sure if my login and overall flow is correct.
I have a login component for the login page which sends the user information to an auth service for authentication, then I save the auth token in the localhost and send it with every get request for the user data, but also I need to check for expired jwt token so I logout the user and clear the localstorage from the information. But I am not sure where this logout should happen.
Also my home page displays 2 different views if the user is logged or not, so I have a boolean in the service that I check. This is my flow:
The login component: Here the error is bind to the html lets say if credentials are invalid
export class LoginComponent implements OnInit {
error : string;
constructor(private authService : AuthService) { }
ngOnInit() {
this.authService.login("Robocop1", "password").subscribe(
data =>{
localStorage.setItem('Authorization', data['token'])
this.auth.isLoggedIn = true
},
err => this.error = err['error']
);
}
}
Service component:
export class AuthService {
isLoggedIn : boolean = false
constructor(private httpClient : HttpClient) { }
login(username, password) {
return this.httpClient.post('http://localhost:8090/login', {
username,
password
})
}
}
This is my home component that checks first if the user is logged:
export class HomeComponent implements OnInit {
isLoggedIn : boolean
constructor(private auth : AuthService) { }
ngOnInit() {
this.isLoggedIn = this.auth.isLoggedIn
}
}
And displays different part of the html:
<div *ngIf="!isLoggedIn">
<p>
hey user
</p>
</div>
<div *ngIf="isLoggedIn">
<p>
not logged
</p>
</div>
So my question is is injetcing a dependencies in the home component ok just to check for single boolean and is there a better way to do it.
Also I can have another data component where I get user data from the database. In the post request I am sending the authentication token so I will have something like:
this.dataService.getItems().subscribe(
data =>{
this.userData = data
},
err => {
if(err['error' === 'Jwt token has expired'){
this.authService.logout()
}else{
this.error = err['error']
}
}
);
So is again injecting dependency just to call a logout method ok? should this logout method be in the authService or elsewhere?
So my question is is injetcing a dependencies in the home component ok
just to check for single boolean and is there a better way to do it.
If your application will be about a couple of simple pages and will not expand, your approach might be enough but for it the best practise is using Angular Route Guards
A route guard is a CanActivate implementation in which you implement your authentication/authorization logic to guard your routes (pages)
So is again injecting dependency just to call a logout method ok?
should this logout method be in the authService or elsewhere?
This should be done implementing an HttpInterceptor. So that you don't need to handle each http call for handling faulty responses or adding authorization tokens. Catching the error response inside your http interceptor and logging out is the way to go. That way you do not have to inject your corresponding service into each place this is required.
HttpInterceptor is also not a big deal. You can follow this step by step guide and implement your own
Related
So in my login component, I generate one token that gives me access to Tablaue dashboard from the angular app
so when I log in at that time only the home component method should call/run. but when I refresh the home page(component) should not run/call that method
Example
login-page.component.ts
public getToken(){
// i received token here
// then i move to the home page component
// saving token into the session
this.router.navigate(["/home"]);
}
home-page.component.ts
public tokenAccess(){
// this method will be run only once when I come from the login page.
// should not run when the page is refreshed or reloaded
}
Thank you
Expanding from your comment, you could send a query parameter in the route to denote if the token has indeed been fetched in the login-page component.
login-page.component.ts
public getToken(){
this.router.navigate(["/home"], { queryParams: { token: true }});
}
The resulting routing URL would be of the form
http://example/home?token=true
The query parameter token would only be present when routed from the login-page component and not on page refresh or reload.
home-page.component.ts
import { ActivatedRoute } from '#angular/router';
constructor(private _actRoute: ActivatedRoute) { }
ngOnInit() {
this._actRoute.queryParams.subscribe(params => {
if (params['token']) { // <-- condition would fail on page refresh/reload
this.tokenAccess();
}
});
};
In a controller, I add the user object with a guard, inject some service and call that service to get some response. I have removed a lot of code for brevity.
#Controller()
#UseGuards(AuthGuard())
export class UserController() {
constructor(private readonly userService: UsersService) {
}
#Get(':id')
async findOne(#Param('id') id) {
return await this.userService.findOne(id);
}
}
Since I have the AuthGuard, I now know the user is logged in before entering :id route.
In the service I would do something like
#Injectable()
export class UsersService {
async findOne(id: number): Promise<User> {
return await this.usersRepository.findOne({where: {id: id}});
}
}
But of course we want to have some checks that the logged in user has access to the user it is querying. The question is now how do I get the current logged in user. I can send it as a parameter from the controller, but since a lot of the backend would need security checked on the current user, I'm not sure that is a good idea.
#Get(':id')
async findOne(#Param('id') id, #Req() req: any) {
return await this.userService.findOne(id, req.user);
}
Ideally, which doesn't work, I would be able to get it in the UserService:
async findOne(id: number, #Req req: any): Promise<User> {
if (id === req.user.id || req.user.roles.contains('ADMIN')) {
return await this.userRepository.findOne({where: {id: id}});
}
}
Or perhaps through injection in the UserService constructor
constructor(#Inject(REQUEST_OBJECT) private readonly req: any) {}
So, is there a better way to send the user object through the backend than always sending the request object in each function call?
Update March 2019
Since version v6, you can now inject the request object into a request-scoped provider:
import { REQUEST } from '#nestjs/core';
import { Request } from 'express';
#Injectable({ scope: Scope.REQUEST })
export class UsersService {
constructor(#Inject(REQUEST) private readonly request: Request) {}
}
Outdated answer
It's not possible to inject the user (or request) directly into the service. Nest.js does not yet support request-scoped providers. This might change with version 6. Until then, a service does not know anything about a request.
Logged in user
You can create a custom decorator #User. Using a decorator is preferable over injecting the request object because then a lot of nest's advantages get lost (like interceptors and exception filters).
export const User = createParamDecorator((data, req) => {
return req.user;
});
And then use it like this:
#UseGuards(AuthGuard())
#Get(':id')
async findOne(#Param('id') id, #User() user) {
return await this.userService.findOne(id, user);
}
Roles
You can create a RolesGuard that makes sure the logged in user has the required roles. For details, see this answer.
#Injectable({ scope: Scope.REQUEST }) worked for me, but I had a lot of problems when need inject on anothers services.
Exist a another alternative:
https://github.com/abonifacio/nestjs-request-context
For while its ok.
#Injectable({ scope: Scope.REQUEST }) worked for me, but I had a lot
of problems when need inject on anothers services.
Exist a another alternative:
https://github.com/abonifacio/nestjs-request-context
For while its ok.
Inject(REQUEST) doesn't work with any passport strategy due to its global state - issue. https://github.com/abonifacio/nestjs-request-context works fine.
I am implementing Login Authentication in my reactjs aplication following the tutorial https://auth0.com/blog/adding-authentication-to-your-react-flux-app//
I have written a class call the AuthService and inside the AuthService I have a function call Login as shown below
import LoginActions from './LoginAction';
const URL_LOGIN = 'loginurl';
class AuthService {
login(username, password) {
// do something
}
}
Now, I am calling this Login method in my Login Component as shown below
//this function is to save data to the API
loginUser = (user) => {
// Here, we call an external AuthService.
Auth.login(user.username, user.password)
.catch(function(err) {
console.log("Error logging in", err);
console.log(err);
});
Everythin works well but when I submit data, I get the error
TypeError: WEBPACK_IMPORTED_MODULE_4__authentication_AuthService.a.login(...) is undefined
When I console log at the login method of the AuthService class, I see the returned data. I have looked around for a fast solution to this error but I have not gotten it. Any help on this will be appreciate.
I do not want to bring this action to the component as I am also going to use it in other areas of my application.
Also,I am a newbie to Reactjs as this is my first Authentication I am doing here.
Add the static keyword before the login method in the AuthService class.
I was waiting for djfdev to see an answer as in his comment:
You’ve defined login as an instance method, but are calling it like a static method. Either add the static keyword before the function name, or create an instance of Auth before calling login on it.
But it seems he's not providing an answer. What he meant that you can define a static method like:
static login(username, password) {
Or call login method in an instance like:
const auth = new Auth
auth.login(user.username, user.password)
Further, I hope you're exporting the class like:
export default AuthService
And importing it like:
import Auth from '...'
From my comment on the OP:
You’ve defined login as an instance method, but are calling it like a static method. Either add the static keyword before the function name, or create an instance of Auth before calling login on it.
So your two options are to, A) use the static keyword to define your method on the class itself:
import LoginActions from './LoginAction';
const URL_LOGIN = 'loginurl';
class AuthService {
static login(username, password) {
// do something
}
}
Or B) create an instance of Auth before calling the login method:
loginUser = (user) => {
// Here, we call an external AuthService.
new AuthService().login(user.username, user.password)
.catch(function(err) {
console.log("Error logging in", err);
console.log(err);
});
See the MDN docs for static
I solved this problem by adding static to the login method as advice above and removing the catch from it.
I use JWT in a server node to check if the user is connected.
AuthGuard in an observable, it will check if my user is connected :
canActivate(): Observable<any> {
if ( !localStorage.getItem('currentUser')){
return Observable.of(false);
}
// Authorization with JWT Token
const options = {
headers: new HttpHeaders().set('Authorization', 'BEARER ' + localStorage.getItem('currentUser') )
};
//Check if the JWT Token is always available and if the signature is right
return this.http.get(environment.api + 'checkToken', options )
.map(() => true).catch(() => {
return Observable.of(false);
});
}
Now I would like to emit the boolean from canActivate to an other component. My other component is the nav-bar of my website. I want to hide some link in the navbar when the user is disconnected. However, I would like to displays some elements when the user is connected.
But even when I use an observable in the constructor of my navbar component, it will check just one time if the user is connected.
Do you have any ideas to emit the boolean from my AuthGuard to my navbar component ?
Thank you
You can use resolve on the route and do almost the same things in resolver class. Then you can access boolean value (or user object itself if you resolve it) from the component by injecting ActivatedRouteSnapshot.
Or if you don't want round trips then you can subscribe to the observable before you return it from canActivate and emit the value to some service that contains Subject for example so you would be able to inject this service into your component and use that subject.
I am using Angular 2 with angularfire library. I am having trouble catching changes in the user's state.
First) Logging in works fine. I see the user come through to my subscribe function and all is dandy.
However, When I disable that user on the Firebase console...The user state has changed. Why am I not seeing that change in state come through in my app component?
I would like to captures errors of different types, and handle them according to the error code. To do this, I created a service here:
#Injectable()
export class CZAuthService {
constructor(private af: AngularFire){ }
getAuthState() : Observable<FirebaseAuthState>{
return this.af.auth.catch((error)=>{
console.log(error);
return this.af.auth;
})
}
}
I would like to catch exceptions here in this catch operator, and then return the stream to maintain connection to the stream.
Then in my root app component, I am subscribing to this stream and handling when the user is signed in:
this.auth.getAuthState().subscribe((user)=>{
console.log(user);
if(user != null){
this._store.dispatch(new UserLogInSuccessAction(user))
}
});