user176504
user176504

Reputation: 323

I'm trying to extend Nebular Auth to allow email validation but I'm coming up against errors

I'm building an Angular application using Nebular auth components. I need to add a custom implementation for registering, and also want to include an email validation stage, where the user is entered into the DB as inactive with registration, but is emailed a code in a link which they use to validate their email so they can be marked as active.

I keep getting tied up in dependency injection knots, but I don't see where I'm going wrong. I'd really appreciate some eyes on this. Thanks!

app.module.ts

@NgModule({
    imports: [
        BrowserModule,
        BrowserAnimationsModule,
        HttpClientModule,
        AppRoutingModule,
        SharedModule,
        ThemeModule.forRoot(),
        NbToastrModule.forRoot(),
        StoreModule.forRoot(reducers, {
            metaReducers,
            runtimeChecks: {
                strictStateImmutability: true,
                strictActionImmutability: true,
            },
        }),
        EffectsModule.forRoot(),
        NbAuthModule.forRoot({
            strategies: [
                CustomAuthStrategy.setup({
                    name: "email",
                    baseEndpoint: "/auth/",
                    login: {
                        endpoint: "login",
                        redirect: {
                            success: HOME_PAGE,
                            failure: null,
                        },
                    },
                    register: {
                        endpoint: "register",
                        redirect: {
                            success: null,
                            failure: null,
                        },
                    },
                    verifyemail: {
                        endpoint: "verify",
                    },
                    logout: {
                        endpoint: "",
                    },
                    requestPass: {
                        endpoint: "requestpass",
                    },
                    resetPass: {
                        endpoint: "resetpass",
                    },
                    token: {
                        class: NbAuthJWTToken,
                        key: "token",
                    },
                }),
            ],
            forms: {},
        }),
    ],
    declarations: [AppComponent],
    providers: [
        {
            provide: HTTP_INTERCEPTORS,
            useClass: AddTokenHeaderHttpRequestInterceptor,
            multi: true,
        },
        ApiService,
        CustomAuthStrategy,
        { provide: NbAuthService, useClass: CustomAuthService },
        NbTokenService,
        HttpClient,
    ],
    bootstrap: [AppComponent],
})
export class AppModule {}

custom-auth.service.ts:

@Injectable({
    providedIn: "root",
})
export class CustomAuthService extends NbAuthService {
    strategies: NbAuthStrategy[];

    constructor(
        protected tokenService: NbTokenService,
        private injector: Injector,
    ) {
        // Initialize strategies after injector is available
        super(tokenService, []);
        this.strategies = this.injector.get(NbAuthStrategy, []);
    }

    verifyEmail(
        email: string,
        code: string,
        http: HttpClient,
    ): Observable<NbAuthResult> {
        const endpoint = "/auth/verify";
        return http.post(endpoint, { email, code }).pipe(
            map((res: any) => {
                return new NbAuthResult(
                    res.success,
                    res,
                    res.success ? "/auth/login" : null,
                    res.success ? [] : ["Verification failed."],
                    res.success
                        ? ["Email verified successfully. Please log in."]
                        : [],
                );
            }),
        );
    }
}

custom-auth.strategy.ts:

export interface CustomAuthStrategyOptions
    extends NbPasswordAuthStrategyOptions {
    verifyemail?: {
        endpoint: string;
    };
}

@Injectable({
    providedIn: "root",
})
export class CustomAuthStrategy extends NbPasswordAuthStrategy {
    constructor(
        protected http: HttpClient,
        protected injector: Injector,
        route: ActivatedRoute,
        options: CustomAuthStrategyOptions,
    ) {
        super(http, route);
    }

    static setup(
        options: CustomAuthStrategyOptions,
    ): [typeof CustomAuthStrategy, CustomAuthStrategyOptions] {
        return [CustomAuthStrategy, options];
    }

    protected handleResponse(res: any): NbAuthResult {
        if (res.success) {
            return new NbAuthResult(
                true,
                res,
                this.getOption("register.redirect.success"),
                [],
                res.message,
            );
        } else {
            return new NbAuthResult(
                false,
                res,
                this.getOption("register.redirect.failure"),
                this.getOption("errors.getter")(res),
                this.getOption("messages.getter")(res),
            );
        }
    }

    register(data?: any): Observable<NbAuthResult> {
        return this.http.post(this.getActionEndpoint("register"), data).pipe(
            map((res) => this.handleResponse(res)),
            catchError((res: HttpErrorResponse) => {
                return of(
                    new NbAuthResult(
                        false,
                        res,
                        this.getOption("register.redirect.failure"),
                        this.getOption("errors.getter")(res),
                    ),
                );
            }),
        );
    }
}

At present, I'm getting an error:

Error: This constructor was not compatible with Dependency Injection. at Module.ɵɵinvalidFactory (core.mjs:10057:11) at Object.CustomAuthStrategy_Factory [as factory] (

The other error I seem to be alternating with is: NullInjectorError: NullInjectorError: No provider for NB_AUTH_STRATEGIES!

Can you help a fried brain out? Thanks!

Upvotes: 0

Views: 53

Answers (1)

user176504
user176504

Reputation: 323

In the end I worked this out.

  1. Removed the verifyemail definition from the module setup, and everything bar ApiService and HttpClient from the providers.
  2. Removed CustomAuthStrategyOptions and constructor for CustomAuthStrategy. Changed setup to:
static setup(
    options: NbPasswordAuthStrategyOptions,
): [typeof CustomPasswordAuthStrategy, NbPasswordAuthStrategyOptions] {
    return [CustomPasswordAuthStrategy, options];
}

That was enough to prevent the DI errors.

Upvotes: 0

Related Questions