NestJS - current auth user but not via decorator - javascript

I create belowed decorator to get current logged in to the system user,
export const CurrentUser = createParamDecorator(
(data: unknown, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest();
return request.user;
},
);
but I do not want to use this because i need to use in any of my controller
which is a bit troublesome for me because some functions are optional, i.e. both for the logged in user and the non logged in user,
so, how can I get current logged in user in my service in functions when i want to get current user instead of all via decorator in controller?
thanks for any help

You'd have to make a custom provider and inject the request into it. Something like this
{
provider: 'CURRENT_USER',
inject: [REQUEST],
useFactory: (req: Request) => {
return req.user;
},
scope: Scope.REQUEST,
}
(REQUEST is injected from #nestjs/core)
Then the user can be injected into the service with #Inject('CURRENT_USER'). Keep in mind, this will make the service REQUEST scoped, and by scope hierarchy it will make whatever you inject the service into REQUEST scoped.
Edit 2/15/21
An example of this module could look something like this:
#Module({
providers: [{
provider: 'CURRENT_USER',
inject: [REQUEST],
useFactory: (req: Request) => {
return req.user;
},
scope: Scope.REQUEST,
}],
exports: ['CURRENT_USER'],
})
export class CurrentUserModule {}
And now in whatever module that has the service that needs the current user you do
#Module({
imports: [CurrentUserModule],
providers: [ServiceThatNeedsUser],
})
export class ModuleThatNeedsUser {}
and in the service:
#Injectable()
export class ServiceThatNeedsUser {
constructor(#Inject('CURRENT_USER') private readonly user: UserType) {}
// rest of class implementation
}

Related

Call Service inside ngdobootstrap

I am creating application using angular and calling api call inside the ngdobootrap method. I am getting error while injecting the service inside bootstrap:
export class AppModule {
constructor(private upgrade: UpgradeModule, private router: Router, private location: Location,private services : someservice) { }
ngDoBootstrap(moduleRef) {
this.getdata()
}
getdata() {
this.services.data().subscribe((res: any) => {
console.log(res)
}, error => {
})
}
Error:
zone.js:682 Unhandled Promise rejection: Trying to get the AngularJS injector before it being set. ; Zone: <root> ; Task: Promise.then ; Value: Error: Trying to get the AngularJS injector before it being set.
at injectorFactory (vendor.js:148805:15)
Add the forwardRef to the service in the constructor,
#Inject(forwardRef(() => 'someservice')) private services : someservice
There is more detail here

How to write Test cases for below angular method

I have created a component that opens my custom type dialog, I just want to create Jasmine unit test cases for this method.
export class OpenPopUpComponent implements OnInit {
constructor(public dialog:NewCustomDialog) {}
ngOnInit() {
}
openModel(){
this.dialog.open(NewComponent,<NewCustomDialogConfig>{
size: 'double',
data: {
title: 'New Dialog'
}
});
}
}
You will not test the dialog itself. What you need to do is to mock the NewCustomDialog and provide it as injected.
In your spec.ts
beforeEach(() => {
const spy = jasmine.createSpyObj('NewCustomDialog', ['open']);
TestBed.configureTestingModule({
// Provide (spy) dependency
providers: [
{ provide: NewCustomDialog, useValue: {newCustomDialogSpy} }
]
});
// Inject both the service-to-test and its (spy) dependency
masterService = TestBed.get(MasterService);
valueServiceSpy = TestBed.get(ValueService);
});
Then you can check that the spy has been called with parameters (the ones you expect).
The intension of the unit test is to test the feature of component itself and not to start testing the features which is outside the scope of component which is to be tested. So,
you do not need to test dialog.open as this should be tested in unit test of NewCustomDialog itself.
start by creating a Stub which you can use as a placeholder for NewCustomDialog, such as
export class NewCustomDialogStub{
open(){ return null; }
close(){ return null; }
// and similar dummy methods which is required by "OpenPopUpComponent"
}
Inject this stub as useClass in providers as below:
export class NewCustomDialogStub{
open(){ return null; }
close(){ return null; }
// and similar dummy methods which is required by "OpenPopUpComponent"
}
describe('OpenPopUpComponent', () => {
let component: OpenPopUpComponent;
let fixture: ComponentFixture<OpenPopUpComponent>;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [],
declaration: [OpenPopUpComponent],
providers: [
{ provide: NewCustomDialog, useClass: NewCustomDialogStub }
]
}).compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(OpenPopUpComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should be defined',()=>{
expect(component).toBeDefined();
})
it('should call "open" method of dialog on calling openModel() ',()=>{
spyon(component.dialog,'open').and.callThrough();
component.openModel();
expect(component.dialog.open).toHaveBeenCalled();
})
})
This is very basic testing but if you want to know more about writing tests , you can refer to this series of articles where I have covered almost all basic testing scenarios . Check the bottom of article for all links. The one which I used here is this one

Not able to find route custom data in angular 6 in app component

I have an angular application and added custom data to the route given as
{ path: 'profile', component: ProfileComponent, data: {title: 'example'} }
and in app.component.ts file I have the following code as
private route: ActivatedRoute
console.log('route snapshot', this.route.firstChild.data._value.pageType );
the error i am getting is
ERROR in src/app/app.component.ts(71,64): error TS2339: Property '_value' does not exist on type 'Observable<Data>'.
can anyone tell me how to access data of route in app.component.ts
Update
You will need to subscribe to the router events in the AppComponent and get the params from it:
constructor(private router: Router, route: ActivatedRoute) { }
ngOnInit() {
this.router.events.subscribe(event => {
if(event instanceof NavigationEnd){
console.log(this.route.root.firstChild.snapshot.data['title']);
}
});
}
If you want it only for that path, you can add one more condition
if(event instanceof NavigationEnd && event.url === '/profile')
Old Answer
You will need to access the snapshot to get the data params
this.route.snapshot.data['title'];

Passing Value from RouterStateSnapshot to my Root Routing File in Angular 2 App

I am trying to figure out how to pass the value from the RouterStateSnapshot in my auth.guard file to my routing file in my Angular 2 app. I want to do this because, rather than loading a hard-coded default component first, I want, after re-login, for the last active component/page to load up. I have this value in my canActivate() function in my AuthGuard file, because I can console out it out via RouterStateSnapshot. So now I need to figure out how to pass this value on to my root routing file so it, on login/re-login, that component gets loaded.
This is the canActivate() function in my AuthGuard file:
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot)
{
// Get route content id
let contentId = Object.getPropertyValueAtPath(route, 'data.contentId');
console.log(state.url);
// If route does not have session id, don’t load in tab view
if (!String.isNotNullOrEmpty(contentId))
{
console.error(`Route (${route.routeConfig.path}) does not have a content id.`);
this.router.navigateByUrl(''); // Forward to default page.
this.router.navigate([state.url]);
return false;
}
if (this.disabled) return true;
if (sessionStorage.getItem('currentUser'))
{
// logged in so return true
return true;
}
// not logged in so redirect to login page with the return url
this.router.navigate(['/login', {returnUrl: state.url}]);
return false;
}
Notice that I am doing this within that function: console.log(state.url). This gives me the correct value. Now I need to pass it to my app-routing file.
To clarify, currently, on re-login, the last active component is loaded -- but it displays as a background tab, and the default 'redirect' component is what loads up as the active component (i.e, it shows as the active tab).
A simplified version of the app-routing file looks like this:
import { HomeComponent } ...
export const routes: Routes = [
{ path: 'login', component: LoginComponent },
{ path: 'home', component: HomeComponent },
{ path: 'about', component: AboutComponent },
{ path: '**', redirectTo: 'home' }
];
As you can see above, on initial load I currently redirect the user to the 'home component' by default. What I'd like to do is re-direct them to the value that is stored in "state.url" from RouterStateSnapshot. I'm not clear how to do this, however. Any ideas as to how I'd pass that value from my AuthGuard file down to my app-routing file? Can I simply inject RouterStateSnapshot into my app-routing file to get that desired value directly? Or can I use "resolve" here along with the path in routing? What's the recommended way to handle this kind of scenario?
I accomplish this by storing the url in a shared service from my AuthGuard
// auth-guard.ts
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
let isLoggedIn = this.authService.isUserLoggedIn();
if(isLoggedIn){
return true;
}else{
this.someService.storeRedirectUrl(state.url);
this.router.navigateByUrl('/login');
return false;
}
}
Then when the user logs in, check if that redirect url was stored and navigate to it
// login method within login page
login(){
this.authService.login(email, password).subscribe(
res => {
// successful user login, so determine where to route user
if(this.someService.redirectUrl){
// redirect url found, navigate to that url
this.router.navigateByUrl(this.someService.redirectUrl);
}else{
// if no redirect url found, navigate to normal landing page
this.router.navigateByUrl('/home');
}
});
}
Routes File
// routes
export const routes: Routes = [
{
path: 'login',
component: LoginComponent
},
{
path: 'home',
component: HomeComponent,
canActivate: [AuthGuard]
},
{
path: 'about',
component: AboutComponent,
canActivate: [AuthGuard]
},
{
path: '**',
redirectTo: 'home'
}
];
Can I simply inject RouterStateSnapshot into my app-routing file to get that desired value directly?
app-routing is just for mapping routes to components, so there is no injecting the route snapshot into it.
Another option you could do is to pass the redirect url as a query parameter of the login page within the auth guard. (I think this was what you were heading towards)
// auth-guard.ts
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
let isLoggedIn = this.authService.isUserLoggedIn();
if(isLoggedIn){
return true;
}else{
this.router.navigate(['/login', {redirectUrl: state.url}]);
return false;
}
}
Then the process is the same after a user logs in successfully, except this time you fetch the redirect url from the url parameters instead of the service.

How to authenticate angular 2 using oauth node js

I have this requirement for my website. The website will be a SPA built with Angular 2.
What I want is that the user can login using Google, Facebook, Twitter oauth.
I can setup the oauth on the node server, but the problem is how do I allow login through angular 2 app.
In the angular 2 app if when the user clicks facebook signin option, I cant redirect the user as then I lose the state of my application.
I dont know if this is a very beginner problem. Can anyone help me with the flow of what happens in angular 2 + node oauth
You are going to want to setup routes in the angular app to handle the front end of your application. Then create a service to handle the auth0 authentication of the application,
This is a overview of setting up a secure set of routes and a public set of routes in your app. Once someone logs in with oauth they will be forwarded to the secure routes.
So starting out here is the routes. We will specify a secure and public in the app.routing.ts file
Routes
const APP_ROUTES: Routes = [
{ path: '', redirectTo: '/home', pathMatch: 'full', },
{ path: '', component: PublicComponent, data: { title: 'Public Views' }, children: PUBLIC_ROUTES },
{ path: '', component: SecureComponent, canActivate: [Guard], data: { title: 'Secure Views' }, children: SECURE_ROUTES }
];
Ok so now that you have that. You can create a templates directory. Inside create secure.component and public.component. Then I create a directory called secure and one called public which I put all of my components dependent on the authentication level to access them. I also add their routes to a file in those directories to keep everything separate.
Notice in my routes above I have the [Guard] setup on the secure. This will block anyone from going to the secure routes without authentication.
Here is an example of what that guard looks like.
import { Injectable } from '#angular/core';
import { CanActivate, Router, ActivatedRouteSnapshot, RouterStateSnapshot } from '#angular/router';
import { Auth } from './auth.service';
import { Observable } from 'rxjs/Observable';
#Injectable()
export class Guard implements CanActivate {
constructor(protected router: Router, protected auth: Auth ) {}
canActivate() {
if (localStorage.getItem('access_token')) {
// logged in so return true
return true;
}
// not logged in so redirect to login page
this.router.navigate(['/home']);
return false;
}
}
Now that we have that we have the routes secured with Guard. We can setup the auth0 client.
Create a config file with your credentials you get from auth0
interface AuthConfiguration {
clientID: string,
domain: string,
callbackURL: string
}
export const myConfig: AuthConfiguration = {
clientID: 'clietnifherefromauth0',
domain: 'username.auth0.com',
// You may need to change this!
callbackURL: 'http://localhost:3000/endpoint/'
};
Then to actually authenticate someone. Receive their data and save the token as well as their data to the local storage. Also provide a logout function and a check to make sure they are logged in.
import { Injectable } from '#angular/core';
import { tokenNotExpired, JwtHelper } from 'angular2-jwt';
import { Router } from '#angular/router';
import { myConfig } from './auth.config';
declare var Auth0Lock: any;
var options = {
theme: {
logo: '/img/logo.png',
primaryColor: '#779476'
},
languageDictionary: {
emailInputPlaceholder: "email#example.com",
title: "Login or SignUp"
},
};
#Injectable()
export class Auth {
lock = new Auth0Lock(myConfig.clientID, myConfig.domain, options, {});
userProfile: Object;
constructor(private router: Router) {
this.userProfile = JSON.parse(localStorage.getItem('profile'));
this.lock.on('authenticated', (authResult: any) => {
localStorage.setItem('access_token', authResult.idToken);
this.lock.getProfile(authResult.idToken, (error: any, profile: any) => {
if (error) {
console.log(error);
return;
}
localStorage.setItem('profile', JSON.stringify(profile));
this.userProfile = profile;
this.router.navigateByUrl('/CHANGETHISTOYOURROUTE');
});
this.lock.hide();
});
}
public login() {
this.lock.show();
}
private get accessToken(): string {
return localStorage.getItem('access_token');
}
public authenticated(): boolean {
try {
var jwtHelper: JwtHelper = new JwtHelper();
var token = this.accessToken;
if (jwtHelper.isTokenExpired(token))
return false;
return true;
}
catch (err) {
return false;
}
}
public logout() {
localStorage.removeItem('profile');
localStorage.removeItem('access_token');
this.userProfile = undefined;
this.router.navigateByUrl('/home');
};
}
Make sure to go into your auth0 dashboard and select the social links you want. In your case facebook twitter and Google. Then when someone activates the widget those three will appear.
So all we have to do now is show the widget when someone clicks login,
html will show a login link. But if they are logged in it will show a bit of information about them instead.
<ul class="nav navbar-nav pull-right">
<li class="nav-item">
<a class="nav-link" (click)="auth.login()" *ngIf="!auth.authenticated()">Login / SignUp</a>
<a class="aside-toggle" href="#" role="button" aria-haspopup="true" aria-expanded="false" *ngIf="auth.authenticated()">
<span *ngIf="auth.authenticated() && auth.userProfile" class="profile-name">{{auth.userProfile.nickname}}</span>
<span *ngIf="!auth.authenticated() && !auth.userProfile" class="profile-name">Account</span>
<i class="icon-bell"></i><span class="tag tag-pill tag-danger profile-alerts">5</span>
<img *ngIf="auth.authenticated() && auth.userProfile" [src]="auth.userProfile.picture" class="img-avatar profile-picture" alt="User profile picture">
<img *ngIf="!auth.authenticated() && !auth.userProfile" src="/img/avatars/gravatar-default.png" alt="Default profile-picture">
</a>
</li>
</ul>
Let me know if anything is not clear. I would be glad to help.

Categories