Azure active directory integration angular + adal+ spring boot - spring-boot

I'm trying to integrate Azure active directory with an application I have. The application front end is Angular 7 and back end is Spring boot. What I did was creating a web app in Azure portal and in Angular side by using microsoft adal library get the access token then passing that token with every request and authenticate that token in the spring boot backend. What I need to know is the way I'm doing is correct ie, I'm using the same azure app credentials(Client id, Tenant id.....) in Angular and spring boot. Is we need to create different app for fronend and backend. And getting access token from front end is correct or not.
app.module.ts
-----------------
function initializer(adalService: MsAdalAngular6Service) {
return () => new Promise((resolve, reject) => {
if (adalService.isAuthenticated) {
resolve();
} else {
adalService.login();
}
});
}
#NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
HttpClientModule,
MsAdalAngular6Module.forRoot({
tenant: 'xxxbef18-40f6-44e6-972c-407462a99xxx',
clientId: 'xxx4602f-e3c8-4114-ae23-42bf9e57dxxx',
redirectUri: 'http://localhost:4200',
navigateToLoginRequestUrl: false,
cacheLocation: 'localStorage'
})
],
providers: [ {
provide: APP_INITIALIZER,
useFactory: initializer,
multi: true,
deps: [MsAdalAngular6Service]
},
{
provide: HTTP_INTERCEPTORS,
useClass: TokenInterceptorService,
multi: true
}],
bootstrap: [AppComponent]
})
export class AppModule { }
Filter class in backend
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private AADAuthenticationFilter aadAuthFilter;
#Override
protected void configure(HttpSecurity http) throws Exception {
//allow all request access this url
http.authorizeRequests().antMatchers("/home").permitAll();
//access to this url requires authentication
http.authorizeRequests().antMatchers("/api/**").authenticated();
http.authorizeRequests().anyRequest().permitAll();
http.addFilterBefore(aadAuthFilter, UsernamePasswordAuthenticationFilter.class);
}
}
application.properties
# Specifies your Active Directory ID:
azure.activedirectory.tenant-id=92cbef18-40f6-44e6-972c-407462a99xxx
# Specifies your App Registration's Application ID:
spring.security.oauth2.client.registration.azure.client-id=xxx42c78-c557-48ef-8f09-be40c2093xxx
azure.activedirectory.client-id=xxx4602f-e3c8-4114-ae23-42bf9e57dxxx
# Specifies your App Registration's secret key:
spring.security.oauth2.client.registration.azure.client-secret=xxx-~H98Y68m5fFw9_P9sy-c4C4E3lAxxx
azure.activedirectory.client-secret=xxx-~H98Yxxxx5fFw9_P9sy-c4C4E3lAxxx
# Specifies the list of Active Directory groups to use for authorization:
azure.activedirectory.active-directory-groups=users
Any help would be appreciable

In theory, the access token can be obtained from the front end. From your configuration, the back end is equivalent to a resource, which is not a problem in itself, but it is generally not recommended.
As you can imagine, our general approach is to create different applications for the front end and the back end, using the front end as the web app end and the back end as the web server end, which provides access tokens.

Related

inject nestjs service to build context for graphql gateway server

In app.module.ts I have the following:
#Module({
imports: [
...,
GraphQLModule.forRoot<ApolloGatewayDriverConfig>({
server: {
context: getContext,
},
driver: ApolloGatewayDriver,
gateway: {
buildService: ({ name, url }) => {
return new RemoteGraphQLDataSource({
url,
willSendRequest({ request, context }: any) {
...
},
});
},
supergraphSdl: new IntrospectAndCompose({
subgraphs: [
{ name: 'iam', url: API_URL_IAM },
],
})
},
}),
]
...
})
here getContext is just a regular function which is not part of nestjs context (doesn't have injection, module capability) like below:
export const getContext = async ({ req }) => {
return {}
}
Is there any way to use nestjs services instead of plain old functional approach to build the context for graphql gateway in nestjs?
Thanks in advance for any kind of help.
I believe you're looking to create a service that is #Injectable and you can use that injectable service via a provider. What a provider will do is satisfy any dependency injection necessary.
In your scenario, I would import other modules as necessary. For building context, I would create a config file to create from env variables. Then create a custom provider that reads from the env variables and provides that implementation of the class/service to the other classes as their dependency injection.
For example, if I have a graphQL module. I would import the independent module. Then, I would provide in the providers section, the handler/service classes and the dependencies as an #injectable. Once your service class is created based on your config (which your provider class would handle), you would attach that service class to your GraphQL class to maybe lets say direct the URL to your dev/prod envs.

Spartacus Storefront Multisite I18n with Backend

We've run into some problems for our MultiSite Spartacus setup when doing I18n.
We'd like to have different translations for each site, so we put these on an API that can give back the messages dependent on the baseSite, eg: backend.org/baseSiteX/messages?group=common
But the Spartacus setup doesn't let us pass the baseSite? We can
pass {{lng}} and {{ns}}, but no baseSite.
See https://sap.github.io/spartacus-docs/i18n/#lazy-loading
We'd could do it by overriding i18nextInit, but I'm unsure how to achieve this.
In the documentation, it says you can use crossOrigin: true in the config, but that does not seem to work. The type-checking say it's unsupported, and it still shows uw CORS-issues
Does someone have ideas for these problems?
Currently only language {{lng}} and chunk name {{ns}} are supported as dynamic params in the i18n.backend.loadPath config.
To achieve your goal, you can implement a custom Spartacus CONFIG_INITIALIZER to will populate your i18n.backend.loadPath config based on the value from the BaseSiteService.getActive():
#Injectable({ providedIn: 'root' })
export class I18nBackendPathConfigInitializer implements ConfigInitializer {
readonly scopes = ['i18n.backend.loadPath']; // declare config key that you will resolve
readonly configFactory = () => this.resolveConfig().toPromise();
constructor(protected baseSiteService: BaseSiteService) {}
protected resolveConfig(): Observable<I18nConfig> {
return this.baseSiteService.getActive().pipe(
take(1),
map((baseSite) => ({
i18n: {
backend: {
// initialize your i18n backend path using the basesite value:
loadPath: `https://backend.org/${baseSite}/messages?lang={{lng}}&group={{ns}}`,
},
},
}))
);
}
}
and provide it in your module (i.e. in app.module):
#NgModule({
providers: [
{
provide: CONFIG_INITIALIZER,
useExisting: I18nBackendPathConfigInitializer,
multi: true,
},
],
/* ... */
})
Note: the above solution assumes the active basesite is set only once, on app start (which is the case in Spartacus by default).

Microsoft single sign on - react-aad-msal library - can't get access token

I'm developing a web application with a react frontend and a .NET CORE 3.1 backend and was asked to add Azure AD single sign on capabilities. I'm using the react-aad-msal library (https://www.npmjs.com/package/react-aad-msal). I'm calling MsalAuthProvider.getAccessToken() and get this error:
Can't construct an AccessTokenResponse from a AuthResponse that has a token type of "id_token".
Can anyone help me?
Anyone? Btw. getAccessToken() is actually inside the standard msal library, if that helps.
I found a solution myself by going into packages.json and lowering the version number on "msal" in "dependencies" like this:
"msal": "~1.3.0",
Change the scopes in authProvider.
export const authProvider = new MsalAuthProvider(
{
auth: {
authority: 'https://login.microsoftonline.com/5555555-5555-5555-5555-555555555555',
clientId: 'AAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAA',
postLogoutRedirectUri: 'http://localhost:3000/signin',
redirectUri: 'http://localhost:3000/signin',
validateAuthority: true,
navigateToLoginRequestUrl: false
},
system: {
logger: new Logger(
(logLevel, message, containsPii) => {
console.log("[MSAL]", message);
},
{
level: LogLevel.Verbose,
piiLoggingEnabled: false
}
)
},
cache: {
cacheLocation: "sessionStorage",
storeAuthStateInCookie: true
}
},
{
scopes: ["openid", "profile", "user.read"] // <<<-----------|
},
{
loginType: LoginType.Popup,
tokenRefreshUri: window.location.origin + "/auth.html"
}
);

DTO not working for microservice, but working for apis directly

I am developing apis & microservices in nestJS,
this is my controller function
#Post()
#MessagePattern({ service: TRANSACTION_SERVICE, msg: 'create' })
create( #Body() createTransactionDto: TransactionDto_create ) : Promise<Transaction>{
return this.transactionsService.create(createTransactionDto)
}
when i call post api, dto validation works fine, but when i call this using microservice validation does not work and it passes to service without rejecting with error.
here is my DTO
import { IsEmail, IsNotEmpty, IsString } from 'class-validator';
export class TransactionDto_create{
#IsNotEmpty()
action: string;
// #IsString()
readonly rec_id : string;
#IsNotEmpty()
readonly data : Object;
extras : Object;
// readonly extras2 : Object;
}
when i call api without action parameter it shows error action required but when i call this from microservice using
const pattern = { service: TRANSACTION_SERVICE, msg: 'create' };
const data = {id: '5d1de5d787db5151903c80b9', extras:{'asdf':'dsf'}};
return this.client.send<number>(pattern, data)
it does not throw error and goes to service.
I have added globalpipe validation also.
app.useGlobalPipes(new ValidationPipe({
disableErrorMessages: false, // set true to hide detailed error message
whitelist: false, // set true to strip params which are not in DTO
transform: false // set true if you want DTO to convert params to DTO class by default its false
}));
how will it work for both api & microservice, because i need all at one place and with same functionality so that as per clients it can be called.
ValidationPipe throws HTTP BadRequestException, where as the proxy client expects RpcException.
#Catch(HttpException)
export class RpcValidationFilter implements ExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
return new RpcException(exception.getResponse())
}
}
#UseFilters(new RpcValidationFilter())
#MessagePattern('validate')
async validate(
#Payload(new ValidationPipe({ whitelist: true })) payload: SomeDTO,
) {
// payload validates to SomeDto
. . .
}
I'm going out on a limb and assuming in you main.ts you have the line app.useGlobalPipes(new ValidationPipe());. From the documentation
In the case of hybrid apps the useGlobalPipes() method doesn't set up pipes for gateways and micro services. For "standard" (non-hybrid) microservice apps, useGlobalPipes() does mount pipes globally.
You could instead bind the pipe globally from the AppModule, or you could use the #UsePipes() decorator on each route that will be needing validation via the ValidationPipe
More info on binding pipes here
As I understood, useGlobalPipes is working fine for api but not for microservice.
Reason behind this, nest microservice is a hybrid application and it has some restrictions. Please refer below para.
By default a hybrid application will not inherit global pipes, interceptors, guards and filters configured for the main (HTTP-based) application. To inherit these configuration properties from the main application, set the inheritAppConfig property in the second argument (an optional options object) of the connectMicroservice() call.
Please refer this Nest Official Document
So, you need to add inheritAppConfig option in connectMicroservice() method.
const microservice = app.connectMicroservice(
{
transport: Transport.TCP,
},
{ inheritAppConfig: true },
);
It worked for me!

Using webpack dev server, how to proxy everything except "/app", but including "/app/api"

Using webpack dev server, I'd like to have a proxy that proxies everything to the server, except my app. Except that the api, which has an endpoint under my app, should be proxied:
/myapp/api/** should be proxied
/myapp/** should not be proxied (any
/** should be proxied
The following setup does this using a bypass function, but can it be done declaratively, using a single context specification?
proxy: [
{
context: '/',
bypass: function(req, res, options) {
if (
req.url.startsWith('/app') &&
!req.url.startsWith('/app/api')
) {
// console.log ("no proxy for local stuff");
return false;
}
// console.log ("Proxy!")
},
// ...
},
],
According to https://webpack.js.org/configuration/dev-server/#devserver-proxy webpack dev server uses http-proxy-middleware and at its documentation (https://github.com/chimurai/http-proxy-middleware#context-matching) you can use exclusion.
This should be working in your case:
proxy: [
{
context: ['**', '/myapp/api/**', '!/myapp/**'],
// ...
},
],

Resources