I am currently working on implementing the authorization code flow with Auth0 for our Spartacus application. The authentication works fine and I can navigate the page from the root. However when I try to access a deep link, e.g. /configure/vc/product/entityKey/key I get redirected to the Homepage. Same behaviour when I refresh the page.
I observed that the pages request gets canceled and instead the Router routes to "/". I don't see any Fail Actions being fired by ngrx.
Debugging the flow, I believe it has to do with the AuthService.checkOAuthParamsInUrl() method, which throws an exception in line
const result = await this.oAuthLibWrapperService.tryLogin(); (https://github.com/SAP/spartacus/blob/develop/projects/core/src/auth/user-auth/facade/auth.service.ts)
The method seems to check for the code inside the url, which is not available in deep links. I assumed it would take the token from the local storage if available.
Am I on the wrong track, or is the only possiblity to adjust the behaviour of the AuthService? If so, how should I go on with it?
Spartacus Version: 3.4.3.
rxjs Version: 6.6.7
[EDIT] My AuthConfig:
provideConfig(<AuthConfig>{
authentication: {
OAuthLibConfig: {
responseType: 'code',
redirectUri: environment.spartacus.auth.redirectUrl,
customQueryParams: {
connection: 'main-tenant-oidc',
audience: 'my-audience'
}
},
baseUrl: 'https://my-auth.auth0.com',
client_id: 'id',
client_secret: 'secret',
loginUrl: '/authorize',
tokenEndpoint: '/oauth/token',
userinfoEndpoint: '/userinfo',
revokeEndpoint: '/oauth/revoke'
}
})
[EDIT]
The AuthGuard confirms that the user is logged in, i.e. the isUserLoggedIn() function returns true.
[EDIT]
Updated to Spartacus 4 following the reference structure, no change of behaviour. I am configuring authentication via
routing: {protected: true}
My Feature Module looks like this, knowing that I definitly dont need all of the imports:
import {NgModule} from '#angular/core';
import {OrderConfirmationModule, ReplenishmentOrderConfirmationModule} from "#spartacus/checkout/components";
import {CheckoutOccModule} from "#spartacus/checkout/occ";
import {
AuthModule,
CartModule,
CartOccModule,
CostCenterOccModule,
ExternalRoutesModule,
ProductModule,
ProductOccModule,
UserOccModule
} from "#spartacus/core";
// TODO:Spartacus - 'ProductVariantsModule' was removed from #spartacus/storefront. Use #spartacus/product/variants feature-library instead. To benefit from lazy loading it by default, consider removing the module import and running the command 'ng add #spartacus/product --features=Product-Variants'.
// TODO:Spartacus - 'UserComponentModule' - Following module imports 'LoginModule', 'LoginFormModule', 'LoginRegisterModule', 'RegisterComponentModule' were removed. Those modules are now part of #spartacus/user.
import {
AddressBookModule,
BannerCarouselModule,
BannerModule,
BreadcrumbModule,
CartComponentModule,
CartPageEventModule,
CategoryNavigationModule,
CmsParagraphModule,
FooterNavigationModule,
HamburgerMenuModule,
LinkModule,
MyCouponsModule,
MyInterestsModule,
NavigationEventModule,
NavigationModule,
NotificationPreferenceModule,
OrderCancellationModule,
OrderDetailsModule,
OrderHistoryModule,
OrderReturnModule,
PaymentMethodsModule,
ProductCarouselModule,
ProductDetailsPageModule,
ProductFacetNavigationModule,
ProductImagesModule,
ProductIntroModule,
ProductListingPageModule,
ProductListModule,
ProductPageEventModule,
ProductReferencesModule,
ProductSummaryModule,
ProductTabsModule,
ReplenishmentOrderDetailsModule,
ReplenishmentOrderHistoryModule,
ReturnRequestDetailModule,
ReturnRequestListModule,
SearchBoxModule,
SiteContextSelectorModule,
StockNotificationModule,
TabParagraphContainerModule,
WishListModule
} from "#spartacus/storefront";
import {
CloseAccountModule,
ForgotPasswordModule,
RegisterComponentModule,
ResetPasswordModule,
UpdateEmailModule,
UpdatePasswordModule,
UpdateProfileModule
} from "#spartacus/user/profile/components";
import {ProductVariantsFeatureModule} from './features/product/product-variants-feature.module';
import {LoginFormModule, LoginModule, LoginRegisterModule} from "#spartacus/user/account/components";
import { UserFeatureModule } from './features/user/user-feature.module';
#NgModule({
declarations: [],
imports: [
// Migrating the StorefrontModule
ProductDetailsPageModule,
ProductListingPageModule,
ExternalRoutesModule.forRoot(),
// Migrating the CmsLibModule
HamburgerMenuModule,
CmsParagraphModule,
LinkModule,
BannerModule,
CategoryNavigationModule,
NavigationModule,
FooterNavigationModule,
BreadcrumbModule,
SearchBoxModule,
SiteContextSelectorModule,
AddressBookModule,
OrderHistoryModule,
OrderCancellationModule,
OrderReturnModule,
ReturnRequestListModule,
ReturnRequestDetailModule,
ProductListModule,
ProductFacetNavigationModule,
ProductTabsModule,
ProductCarouselModule,
ProductReferencesModule,
OrderDetailsModule,
PaymentMethodsModule,
CartComponentModule,
TabParagraphContainerModule,
OrderConfirmationModule,
ProductImagesModule,
ProductSummaryModule,
ProductIntroModule,
BannerCarouselModule,
MyCouponsModule,
WishListModule,
NotificationPreferenceModule,
MyInterestsModule,
StockNotificationModule,
ReplenishmentOrderHistoryModule,
ReplenishmentOrderConfirmationModule,
ReplenishmentOrderDetailsModule,
CloseAccountModule,
UpdateEmailModule,
UpdatePasswordModule,
UpdateProfileModule,
ForgotPasswordModule,
ResetPasswordModule,
// Migrating the StorefrontFoundationModule
AuthModule.forRoot(),
CartModule.forRoot(),
ProductModule.forRoot(),
// Migrating the OccModule
CartOccModule,
CheckoutOccModule,
ProductOccModule,
UserOccModule,
CostCenterOccModule,
// Migrating the EventsModule
CartPageEventModule,
NavigationEventModule,
ProductPageEventModule,
ProductVariantsFeatureModule,
// UserComponentModule Substitution
LoginModule,
LoginFormModule,
LoginRegisterModule,
RegisterComponentModule,
UserFeatureModule
]
})
export class SpartacusFeaturesModule {
}
[EDIT]
Important to notice: Our tokens are not being validated by SAP Commerce Backend. So it may be an issue with the error responses, that differ from the native error responses.
Thanks for any hints :)
The throw from this method is not unusual and is handled with the try-catch. This confirms even that if this throws we don't invoke this.authRedirectService.redirect(); method.
In my opinion the depth of the url should not make any difference to Auth handling. I would look either into other guards that could cause some redirect or into this code specific to this configurator routes.
I m following this https://www.codemag.com/Article/2001081/Nest.js-Step-by-Step-Part-3-Users-and-Authentication for implementing jwt using passport in nestJS, everything is working as expected, but I have one question in this approach, if we use UseGuards(#AuthGurads()), the app knows to use passportstrategy to verify the token and call the validate method to proceed further, but how come passport strategy knows the right jwt strategy class to pick for calling the validate method, it is not explicitly mentioned that we are asking passport strategy to use jwtStrategy class and it is not a default export, but still how passportstrategy after validating the token calls the right JwtStrategy class for validate method ?
The important thing for the answer is in this code block: (pulled from the linked article)
#Module({
imports: [ ...,
PassportModule.register({
defaultStrategy: 'jwt',
property: 'user',
session: false,
}), ...
],
controllers: [AuthController],
providers: [AuthService, JwtStrategy],
exports: [PassportModule],
})
export class AuthModule {}
The defaultStrategy tells Nest "when I use AuthGuard(), take this strategy as the one to use". In this case, 'jwt'. Each passport strategy has a default name for passport to know what strategy is being used. In the case of passport-jwt it's (surprise, surprise) 'jwt'
I have several initializable services that implement this simple interface:
interface InitializableService {
init(): void;
}
Those services make some global subscriptions (local and external) that I need during all the live of my app. What I do now is inject those services into my app.component.ts and call them one for one. I want to automate this process.
I have found some info about ways to do this and I just found ways to do it when providing the services into an module. I provide that services in root and I do not want to change this, besides the initial arrangement take the same time to just injecting them into app.components.ts.
I tried creating a base class and extend it so each class service that implement it would become "automatic initialized" on app load but I wasn't able to get the list of services that extend that class to call init on them.
I have no idea where to start to achieve this, or if this is even possible with provideIn: 'root' services.
Have you tried to use Modules with providers ?
solution is , to create a module , something like core.module.ts
then import the module into app.module.ts.
The key is , inside of core module , using module with providers , you can initial the those useful services.
Using provide in root , I just don't like it
core.module.ts Will be something like this
import { ModuleWithProviders, NgModule } from '#angular/core';
import { CommonModule } from '#angular/common';
import { UsersData } from './data/users';
import { UsersService } from './mock/users.service';
const DATA_SERVICES = [
{ provide: UsersData, useClass: UsersService },
];
#NgModule({
imports: [
CommonModule,
],
providers: []
})
export class CoreModule {
static forRoot(): ModuleWithProviders {
return {
ngModule: CoreModule,
providers: [
...DATA_SERVICES,
]
} as ModuleWithProviders;
}
}
What is the correct way to import dynamic modules when there's a circular dependency between them? I simply changed forwardRef(() => MyModule) to forwardRef(() => MyModule.forRoot()), and I'm getting Nest can't resolve dependencies error.
The best way to deal with nest module dependency is to import dependencies always through modules and never through services. If you run into a circular dependency, nest will show you an error and you can easily fix it with forwardRef().
let say your application has 3 tightly coupled modules.
#moduleA()
#moduleB()
#moduleC()
And two supporting modules
#moduleX()
#moduleY()
#moduleZ()
Also, all modules are exporting services with the same name.
Consider a situation where
#moduleA() imports [serviceB, serviceX]
And
#moduleB() imports [serviceA, serviceY, serviceZ]
Now in #moduleC() if you want to use serivceA() then you will have to
#moduleC() imports [serviceA, serviceB, serviceX, serviceY, serviceZ]
In most cases, nest throws the correct error saying which dependency is missing where. But some times, nest only says dependency at [index] is missing, nest doesn't says where it is missing. This will lead to great level of confusion.
A neat approach is to always import modules
#moduleA() imports [moduleB, moduleX]
#moduleB() imports [moduleA, moduleY, moduleZ]
#moduleC() imports [moduleA]
If nest is complaining again about dependency issue then import modules using forwardRef
#module({
imports: [
forwardRef(() => ModuleA)
ModuleB
],
controllers: [],
providers: []
})
Sometimes even after doing all these, you may again run into dependency not available error. Then you can use ModuleRef. Let's take the same example where you want to use ServiceA() in ModuleC()
You will then be adding serviceA() to providers[] in ModuleC() and in the serviceC() class you need to add the following.
import { Injectable, OnModuleInit } from '#nestjs/common'; \\need OnModuleInit
import { ServiceA} from '../FolderA/serviceA.service';
export class ServiceC implements OnModuleInit {
private serviceA: ServiceA;
constructor(private readonly moduleRef: ModuleRef) {}
onModuleInit() {
this.serviceA= this.moduleRef.get(ServiceA);
}
foo(){
console.log(this.serviceA.someFunction())
}
}
Please check more at Official documentation.
Is it possible to globally configure a date format for the ngx-bootstrap datepicker?
The documentation mentions the BsDatepickerConfig class and how to pass it to each individual datepicker, but im a bit surprised that there seems to be no possibility to configure this globally (at least not documented)
https://valor-software.com/ngx-bootstrap/#/datepicker#bs-datepicker-config
This is not yet documented, but it can be achieved pretty easy. We have several demos of that for different components (tooltips, popovers, etc) and the code for datepicker will be almost the same. If you want to configure datepickers globally, replace basic BsDatepickerConfig with your one in providers section of your component or module.
export function getDatepickerConfig(): BsDatepickerConfig {
return Object.assign(new BsDatepickerConfig(), {
dateInputFormat: 'YYYY-MM-DD'
});
}
#NgModule({
...
providers: [{ provide: BsDatepickerConfig, useFactory: getDatepickerConfig }]
})
Example - https://stackblitz.com/edit/angular-tvahsw?file=app%2Fapp.module.ts