angular2 CanActivate with http - dependency-injection

I am having a component that has canactivate
import {isLoggedIn} from '../login/isLoginedIn';
#CanActivate((next, previous) => {
isLoggedIn()
})
My "isLoggedIn" is as below
import {Http, Headers} from 'angular2/http';
class Auth {
constructor( #Inject(Http) private _http: Api) { }
check() {
this._http.get('/Users/LoggedInUser')
}
}
export const isLoggedIn = () => {
let injector = Injector.resolveAndCreate([Auth, Http]);
let auth = injector.get(Auth);
return auth.check();
};
I can't inject a service which has http as dependancy. Can this be done like this or is there a better way to do it?

Since the CanActivate is a decorator instead of a method as with OnActivate or CanDeactivate then you are correct in assuming that constructor dependency injection of the component that you are attempting to authorize is not an option.
The method which you are using will work, but there is a missed #Injectable() on your Auth class.
import {Injectable} from 'angular2/core';
import {Http, Headers} from 'angular2/http';
#Injectable()
class Auth {
constructor( #Inject(Http) private _http: Api) { }
check() {
this._http.get('/Users/LoggedInUser')
}
}
This approach is sound and I don't think that besides some syntactic sugar or minor refactoring that there would be much to improve this and still achieve the same amount of readability / maintainability for this approach.
One other addition that could be made to improve the flow and prevent a potential bug would be to return the observable in CanActivate so that the navigation will wait for the Http request to complete before deciding to continue or cancel.
#CanActivate((next, previous) => {
return isLoggedIn()
})
or for short
#CanActivate(() => isLoggedIn())
(Single statement arrow functions are auto-returning)

Related

Best way to add a RequestInterceptor to a single FeignClient

I need to add a RequestInterceptor to a specific feign client. The interceptor will add auth information that I do not want to leak to a third party, hence I do not want it to trigger for ALL Feign clients. I have this working, but this seems a tad messy, and am hoping there is a cleaner (less code) option.
I am hoping someone can point to where I can simplify things. Particularly around the encoder/decoder stuff. I really dislike them cluttering up my services constructor like that and find it odd that they even need to be specified in the first place.
I have
// build.gradle
implementation 'org.springframework.cloud:spring-cloud-starter-openfeign'
I have a RequestInterceptor as
import feign.RequestInterceptor;
import feign.RequestTemplate;
public class BearerAuthRequestInterceptor implements RequestInterceptor {
#Override
public void apply(RequestTemplate requestTemplate) {
// ... SNIP ... (just adds an Authorization header)
}
}
I have a FeignClient as
#FeignClient(name = "myfeignclient")
public interface MyFeignClient {
#GetMapping(value = "/doTheThing")
String doTheThing();
}
I use my FeignClient from a service like so:
#Service
#Import(FeignClientsConfiguration.class)
public class MyService {
private final MyFeignClient myFeignClient;
#Autowired
public MyService(Decoder decoder, Encoder encoder, Contract contract) {
this.myFeignClient = Feign.builder()
.contract(contract)
.encoder(encoder)
.decoder(decoder)
.requestInterceptor(new BearerAuthRequestInterceptor())
.target(MyFeignClient.class, "https://whatever.com");
}
public void callTheFeignClient() {
myFeignClient.doTheThing();
}
}
Thanks to this comment, I managed to tidy up my implementation a little bit. So no more need for specifying encode/decoder nonsense, or having to manually build my Feign client.
The docs here provide some info, but as is typical they are a bit thin on concrete examples, so perhaps the below will help someone else. Note: I'm using spring boot, and including feign like so in build.gradle implementation 'org.springframework.cloud:spring-cloud-starter-openfeign'
First, create the RequestInterceptor like so:
import org.springframework.context.annotation.Bean;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.jwt.Jwt;
import feign.RequestInterceptor;
import lombok.extern.slf4j.Slf4j;
/**
* A Feign configuration to add the incoming Bearer token to an outgoing feign client request.
* Only annotate this class with "#Configuration" if you want this interceptor to apply globally to all your Feign clients.
* Otherwise you risk exposing the auth token to a third party, or adding it unnecessarily to requests that don't need it.
*/
#Slf4j
public class BearerAuthFeignConfig {
#Bean
public RequestInterceptor bearerAuthRequestInterceptor() {
return requestTemplate -> {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null && authentication.getPrincipal() instanceof Jwt) {
Jwt jwt = (Jwt) authentication.getPrincipal();
requestTemplate.header("Authorization", "Bearer " + jwt.getTokenValue());
} else {
log.error("Unable to add Authoriation header to Feign requestTemplate");
}
};
}
}
Then when declaring your feign client, pass the configuration
#FeignClient(
name = "my-client-that-needs-the-auth",
configuration = BearerAuthFeignConfig.class,
url = "http://whatever.com"
)
public interface PlayerManagementClient {
...
You'll also need the #EnableFeignClients annotation on your #SpringBootApplication class

How to pass reflector to Nest.js global guard?

I am new to nest.js and I have a question.
I have a Roles Guard like this
import { CanActivate, ExecutionContext, Injectable } from '#nestjs/common';
import { Observable } from 'rxjs';
import { Reflector } from '#nestjs/core';
#Injectable()
export class RolesGuard implements CanActivate {
constructor(private readonly reflector: Reflector) {
}
canActivate(context: ExecutionContext): boolean | Promise<boolean> | Observable<boolean> {
const roles = this.reflector.get<string[]>('roles', context.getHandler());
if (!roles) {
return true;
}
const request = context.switchToHttp().getRequest();
const user = request.user;
return user.role.some(role => !!roles.find(item => item === role));
}
}
Now I want to use this guard as a global guard like this
app.useGlobalGuards(new RolesGuard())
But it says that I need to pass argument(the reflector) to the guard as I mentioned in the constructor, now will it be okay to initialize the reflector like this?
const reflector:Reflector = new Reflector();
app.useGlobalGuards(new RolesGuard(reflector))
Or is there a better way to do this?
On the official Nest JS fundamentals course, in lecture "54 Protect Routes with Guards", the instructor specifies it is not best practice to create instance of reflector yourself.
A better way to resolve dependencies is to create a common module, and register your guard there. That way, reflector instance is resolved by nest runtime and you can also specify imports array for any other dependencies.
import { Module } from '#nestjs/common';
import { APP_GUARD } from '#nestjs/core';
import { AuthTokenGuard } from './guards/auth-token.guard';
import { ConfigModule } from '#nestjs/config';
#Module({
imports: [ConfigModule],
providers: [
{
provide: APP_GUARD,
useClass: AuthTokenGuard,
},
],
})
export class CommonModule {}
app.useGlobalGuards(new RolesGuard(new Reflector()));
It is working also. Could not find any better solution.
Although my answer might not add much value, I just want to re-iterate that is the intended way to get the reflector, this is a quote from NestJS's creator
kamilmysliwiec
When you create instance manually, you can create Reflector by
yourself:
new RoleGuard(new Reflector());
Source: https://github.com/nestjs/nest/issues/396#issuecomment-363111707
2023, NestJs 9, related problem:
In case you inject request-scoped dependency into the globally registered guard, the reflector will be undefined.
You can solve this issue by resolving such dependencies using ContextIdFactory and moduleRef.resolve() instead of injecting them normally:
const req = context.switchToHttp().getRequest();
const contextId = ContextIdFactory.getByRequest(req);
this.moduleRef.registerRequestByContextId(req, contextId);
this.authorizationService = await this.moduleRef.resolve(
RequestScopedService,
contextId
);
References:
https://docs.nestjs.com/fundamentals/module-ref
code example
https://discord.com/channels/520622812742811698/1060904277607985172

Angular ActionCable call method in component when notification received

I'm trying to practice using ActionCable in Angular. I created a quick Rails application that I put up on Heroku and then created an Angular application with the actioncable npm module as a dependency.
I configured my Rails appplication to allow http://localhost:4200 as an origin while I play around with my Angular app in development. I also didn't make this an API application because I wanted to have a working UI from the get-go. So I can log into the Rails application, send a message, and my separate Angular application is subscribed to that channel as well. I'm successfully receiving those notifications/messages.
Now I'd like to render something in Angular based on that message. I think I'm missing something pretty silly here, but I cannot refer to methods in the component that instantiates the subscription to that channel in the receive callback of the subscription.
import {
ComponentFactoryResolver,
ComponentRef,
OnInit,
ViewContainerRef,
Component,
ViewChild,
Output
} from '#angular/core';
import * as ActionCable from 'actioncable';
import { MessageComponent } from 'app/message/message.component';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
messageRef: ComponentRef<MessageComponent>;
#ViewChild('message', { read: ViewContainerRef }) message: ViewContainerRef;
title = 'app works!';
private cable: ActionCable.Cable;
private subscription: ActionCable.Channel;
constructor(
private componentFactoryResolver: ComponentFactoryResolver,
private viewContainerRef: ViewContainerRef) {
}
public ngOnInit(): void {
this.cable = ActionCable.createConsumer('wss://<my-heroku-app>.herokuapp.com/cable');
this.subscription = this.cable.subscriptions.create(
'RoomChannel',
{
connected: this.connected,
disconnected: this.disconnected,
received: this.received,
});
}
private showMessage(messageString) {
if (!this.messageRef) {
const messageComponent = this.componentFactoryResolver.resolveComponentFactory(MessageComponent);
this.messageRef = this.message.createComponent(messageComponent);
}
this.messageRef.instance.message = messageString;
this.messageRef.changeDetectorRef.detectChanges();
}
private connected() {
console.log('connected!');
}
private disconnected() {
console.log('disconnected!');
}
private received(data: any) {
console.log('received');
// What do I put here? `this` is of type Subscription,
// and thus, I can't call `this.showMessage(data.message)`
}
}
I want to use some sort of predicate or inject something into that context (sorry if I'm not using the right terminology), but I am just not sure how to do this. I plan on cleaning things up instead of having this all in the AppComponent class, but for now I'm just trying to learn.
Any ideas? Thanks!
The answer seems to be this:
this.cable = ActionCable.createConsumer(this.url)
this.subscription = this.cable.subscriptions.create(
'RoomChannel',
{
connected: this.connected,
disconnected: this.disconnected,
// The key here, apparently, is to use a fat-arrow
// function so that the `this` I care about is
// lexicographically scoped. I still need to better
// understand what exactly that means, but I have a
// general idea.
received: (data) => this.received(data)
});

angular2 inject a service to other - error when using #Inject [duplicate]

This question already has answers here:
Angular2 Beta dependency injection
(3 answers)
Closed 7 years ago.
I am using angular2 Beta. and getting error when using the #Inject annotation to DI my one service to another, not able to figure out where I am wrong. Everything seem to be as per Angular2 documentation.
I am using a cloud based data-services - CloudDB - for my application's data needs.
CloudDB gives me a javascript based client library that I can include in my js app and use to do CRUD operations in my cloudDB database or call other custom API I have stored in my CloudDB account, like UserAuth API (API to authenticate user's credentials).
Before using cloudDB js client lib API , I need to supply my cloudDB account's URL and authKey by calling CloudDB js object's getClient method.
In my angualar2 app, I created a injectable service class - CloudDBProvider - the would store my CloudDB account URL and authKey and call CloudDB.getClient to set the provider's js client object for my CloudDB account.
import {Injectable} from 'angular2/angular2';
///<reference path="../typeDefs/CloudDB.d.ts" /> //typedef of CloudDB js library
#Injectable()
export class CloudDBProvider {
private cloudDBClient: CloudDB.JSClient;
public get cloudDBClient(): CloudDB.JSClient {
return this.cloudDBClient;
}
constructor() {
this.cloudDBClient = new CloudDB.getClient(
"https://myaccount.CloudDB.com/",
"AcfdsfmyDdCMHeadfsdsdfHdsf68" // account authKey
);
}
}
Now, I want to create a UserUtils service in this angular2 app, to which I want to inject above class to get cloudDBClient object. I coded UserUtils service class like below, as learnt from your tutorial
import {Injectable, Inject} from 'angular2/angular2';
import {CloudDBProvider} from './CloudDBProvider';
#Injectable()
export class UserUtils {
private _userDetails: Object = {};
private _cloudDBProvider: CloudDBProvider;
private _cloudDBClient: Microsoft.WindowsAzure.MobileServiceClient;;
constructor( #Inject(CloudDBProvider) cloudDBPrvdr: CloudDBProvider) {
this._cloudDBProvider = cloudDBPrvdr;
this._cloudDBClient = this._cloudDBProvider.cloudDBClient; //the public getter property in the class CloudDBProvider
}
public authenicateUser(p_strUserName: string, p_strUserPassword: string) {
var p: Promise<any> = new Promise(
(resolve: (result: any) => void, reject: (err: any) => void) =>
this._cloudDBClient.userlogin(p_strUserName, p_strUserPassword).done( //using API 'userlogin' of cloudDB to authenticate user against my cloudDB's users table.
(loginResult) => {
alert("from Userutils - You are now logged in as: " + loginResult.user.basicProfile.firstName);
resolve(loginResult);
},
(loginErr: any) => {
alert("Error: " + loginErr.request.responseText);
reject(loginErr);
}
)
);
return p;
}
}
then I am trying to use UserUtils in my LoginPage component like below:
import {Component} from 'angular2/core';
import {WelcomePage} from "../views/welcome/welcome";
import {UserUtils} from "../services/UserUtils";
#Component({
templateUrl: 'app/login/login.html',
providers: [UserUtils]
})
export class LoginPage {
private _userUtils: UserUtils;
constructor( userUtils: UserUtils) {
this._userUtils = userUtils;
}
public loginButtonClicked(event, userName, password) { //called when Login Button is clicked by user
//...
//... to-do field value verification
//...
this._userUtils.authenicateUser(userName, password).then(
(result) => {
//navigate to WelcomePage
},
(err) => { alert(err); }
);
}
}
the component LoginPage doesn't work when I use UserUtils. The browser console throws error - No provider for CloudDBProvider! (LoginPage -> UserUtils -> CloudDBProvider)
Note that, if I move the 'authenicateUser' method from UserUtils to CloudDBProvider directly and use CloudDBProvider in LoginPage component for user authentication, then everything works just fine, user gets authenticated and navigated to welcome page after login. Also, no error is thrown and app working if I remove #Inject(CloudDBProvider) cloudDBPrvdr from UserUtils's constructor obviously I cannot use CloudDBProvider then in UserUtils, but point is app doesn't throw any error, which means something is wrong with #Inject.
any clue where I am going wrong?
Upto my Understanding your mistake is in the imports change the import of Injectablewith this
import {Component, Inject, Injectable} from 'angular2/core';
also accoriding to me when we have used #injectable annotation no need to use #inject in the constructor you simply put your service with the public identifier and can use that service into any another method of the same class.
Perhaps you could add the CloudDBProvider provider in the list of providers of your component:
#Component({
templateUrl: 'app/login/login.html',
providers: [UserUtils, CloudDBProvider]
})
export class LoginPage {
(...)
}
Or at application level within the second parameter of the bootstrap function:
bootstrap(MainComponent, [CloudDBProvider]);
This answer could give you some additional hints: Angular2 Beta dependency injection.
Hope it helps you,
Thierry

DI - No provider found for EventBus

Using Angular Dart, I define an event bus like this:
class MyModule extends Module {
MyModule() {
bind(EventBus, toImplementation: EventBus);
...
}
}
When I want to inject this event bus into a component by simply doing:
class MyComponent {
final EventBus _eventBus;
MyComponent(this._eventBus) {}
}
I am getting the error:
No provider found for EventBus!
I have no idea how to debug this...
The event bus is an external library, which looks like:
library event_bus;
import 'dart:async';
#MirrorsUsed(symbols: '*') // Do not keep any names.
import 'dart:mirrors';
class EventBus {
StreamController _streamController;
EventBus({bool sync: false}) {
_streamController = new StreamController.broadcast(sync: sync);
}
...
}
Any help welcome... thanks!
I'm a bit late, but in this case - when you can't put easily an #Injectable() annotation on the class - the simplest solution is to give the injector a value :
bind(EventBus, toValue: new EventBus());

Resources