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.
Related
What is the correct way to add keycloak authentication to page component which is server side rendered (using getServerSideProps). I want to ensure that getServerSideProps() method wont be executed if customer is not allowed to view page.
I tried to check if user is authorized inside getServerSideProps() -> it always returns that keycloak.login is not a function.
export const getServerSideProps: GetServerSideProps = async (context:
GetServerSidePropsContext & AppContext) => {
let keycloak = getKeycloakInstance(keycloakConfig,
SSRCookies(parseCookies(context.ctx?.req)))
if (!keycloak?.authenticated) {
keycloak.login();
}
...
Second approach is to add keycloak inside component -> again when I try to call keycloak.login(), same error apears like on first approch (keycloak.login is not a function), but when I test it with console.log(keycloak), then that keycloak object is there with all methods inside it.
import {useKeycloak} from "#react-keycloak/ssr";
import {KeycloakInstance} from "keycloak-js";
...
const ProductDetailPage: React.FC<{
errorCode: number | null;
product: Product; }> = ({ errorCode, product }): ReactElement => {
const { keycloak } = useKeycloak<KeycloakInstance>();
if (!keycloak?.authenticated) {
keycloak.login();
}
...
Conclusion:
When I move keycloak.login() inside useEffect() in second approach, it works and customer is redirected to login but the problem is that page is still shown for a second before redirect. I want to ensure that getServerSideProps() method wont call api and fetch all data for component if customer is not allowed to access that page and that customer is immediatelly redirected to login if tries to access non-public page.
I'm following the NestJS docs for creating an EventEmitter (Doc Link.
When I running my code I'm getting an error :
"[Nest] 129586 - 16/06/2021, 20:43:31 [ExceptionsHandler] this.eventEmitter.emit is not a function"
This is what my code looks like:
import { EventEmitter2 } from "#nestjs/event-emitter";
#EntityRepository(Auth)
export class AuthRepository extends Repository{
constructor(private eventEmitter: EventEmitter2) {
super();
}
private logger = new Logger(AuthRepository.name);
async createUser(authDao: SignUpDto): Promise {
const { password, username, role, email, dateOfBirth, fname, lname } = authDao;
let user = await this.findOne({ username });
if (user) {
throw new ForbiddenException("Username already taken");
}
user = this.create({ password, username, role });
await this.save(user).catch(e => this.logger.error(e));
this.eventEmitter.emit("user.created", {
fname, lname, dateOfBirth, email
});
};
}
I'm not sure what I'm missing here.
Nest will not do any DI on TypeORM Repository classes. This is because these classes have other dependencies necessary to them by TypeORM, such as entity managers, and connections. Injecting the EventEmitter is something that should be done in a regular NestJS Provider, and not a TypeO Repo class
One thing I would mention is that you want the line where you declare your class to look like this:
export class AuthRepository extends Repository<Auth> {
as shown here: https://docs.nestjs.com/techniques/database#custom-repository
The reason that the event emitter is not being injected properly is because Nest is already injecting other things into the constructor of a Repository subclass, so the signature doesn't match.
More importantly, this would be potentially considered a bad design, because the Repository should only be concerned with persistence operations on the entities. You might want to refactor this, and create a Service (Provider), and then inject both the eventEmitter and your repository into it. Then, in that service, you can call the createUser method on the repository instance, and follow it up with emitting the event.
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
I've added an interceptor for my HTTP requests where I have to use the access token of my user instance. In my app component I initialise my user:
app.component.ts
private async restoreUser(): Promise<UserModel | any> {
// ... some view stuff
return this.userService.restore()
// login instance could be found
.then(async () => {
// ... some view stuff
})
// local storage is empty -> login is necessary
.catch(async () => {
// ... some view stuff
this.subscription = this.networkSrv.getNetworkStatus()
.subscribe((status: ConnectionStatus) => {
if (status === ConnectionStatus.Online) {
// ... some view stuff
} else {
// ... some view stuff
}
});
});
}
http.interceptor.ts
return this.userSrv.user.pipe(
map((user: UserModel) => request.clone(
{setParams: {'access-token': user.accessToken}}
)),
mergeMap(request => next.handle(request))
);
Now I would like to do a request by initialising my app. The problem is, that the user instance is empty and the application throws an error. Is there a way to do something like await -> so that the user instance is set?
Example:
this.transmissionSrv.restoreQueue().then((projects: ProjectModel[]) => {
this.transmissionSrv.transmitProjects(projects, true).subscribe(console.log);
});
Currently, I use the setTimeout-method, but that isn't the way I should do it, right? In addition, sorry for not being consistent by using Observer; Ionic often uses Promises(?)
You should try adding a filter before your map. Using the filter, your map wont get call until the user is set.
return this.userSrv.user.pipe(
filter(Boolean),
map((user: UserModel) => request.clone(
{setParams: {'access-token': user.accessToken}}
)),
mergeMap(request => next.handle(request))
);
There are a couple of ways you could solve this.
Synchronously: Use an Angular APP_INITIALIZER (see here) to make the backend call and ensure the user object is present when the app bootstraps.
Asynchronously: Modify your existing application to store the user instance in an RxJs BehaviorSubject in a service somewhere and have components that depend on it subscribe to that BehaviorSubject wherever the user instance is needed. When the service constructs, have it make the backend call and stick the completed user instance inside the BehaviorSubject (userSubject.next(user)) when it's complete.
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.