akveo / nebular

:boom: Customizable Angular UI Library based on Eva Design System :new_moon_with_face::sparkles:Dark Mode
https://akveo.github.io/nebular
MIT License
8.07k stars 1.51k forks source link

IsAuthenticatedOrRefresh doesn't work with firebase #2690

Open RezaRahmati opened 3 years ago

RezaRahmati commented 3 years ago

Issue type

I'm submitting a ... (check one with "x")

Issue description

Current behavior:

Throwing error Error: 'refreshToken' is not supported by 'NbFirebaseGoogleStrategy', use 'authenticate'

While if you autheticate without nebular with AngularFire this problem doesn't happen, and I think it could be related to this https://stackoverflow.com/a/37908468/2279488

Expected behavior:

Token doesn't expire until user signed out

Steps to reproduce:

Related code:

import { NbAuthSocialLink } from '@nebular/auth';
import { NbFirebaseGoogleStrategy, NbFirebasePasswordStrategy } from '@nebular/firebase-auth';

const socialLinks: Array<NbAuthSocialLink> = [{
    link: 'google',
    icon: 'google',
    title: 'google',
}];

export const defaultAuthSettings: any = {

    forms: {
        login: {
            strategy: 'password',
            rememberMe: true,
            socialLinks: socialLinks,
        },
        register: {
            strategy: 'password',
            terms: true,
            socialLinks: socialLinks,
        },
        logout: {
            strategy: 'password',
        },
        requestPassword: {
            strategy: 'password',
            socialLinks: socialLinks,
        },
        resetPassword: {
            strategy: 'password',
            socialLinks: socialLinks,
        },
        validation: {
            password: {
                required: true,
                minLength: 6,
                maxLength: 50,
            },
            email: {
                required: true,
            },
            fullName: {
                required: false,
                minLength: 4,
                maxLength: 50,
            },
        },
    },
    strategies: [
        NbFirebasePasswordStrategy.setup({
            name: 'password',
            login: {
                redirect: {
                    success: '/',
                },
            },
            register: {
                redirect: {
                    success: '/',
                },
            },
            logout: {
                redirect: {
                    success: 'auth/login',
                },
            },
            requestPassword: {
                redirect: {
                    success: 'auth/login',
                },
            },
            resetPassword: {
                redirect: {
                    success: 'auth/login',
                },
            },
        }),
        NbFirebaseGoogleStrategy.setup({
            name: 'google',
        }),
    ],
};

Other information:

npm, node, OS, Browser

<!--
Node, npm: `node --version` and `npm --version`
OS: Windows (7/8/10). Linux (incl. distribution). macOS (El Capitan? Sierra?)
Browser: Chrome/Safari/Firefox/etc?
-->

Angular, Nebular

        "@angular/animations": "~11.0.5",
        "@angular/cdk": "^11.0.3",
        "@angular/common": "~11.0.5",
        "@angular/compiler": "~11.0.5",
        "@angular/core": "~11.0.5",
        "@angular/fire": "^6.1.4",
        "@angular/forms": "~11.0.5",
        "@angular/platform-browser": "~11.0.5",
        "@angular/platform-browser-dynamic": "~11.0.5",
        "@angular/router": "~11.0.5",
        "@nebular/auth": "^7.0.0",
        "@nebular/eva-icons": "^7.0.0",
        "@nebular/firebase-auth": "^7.0.0",
        "@nebular/theme": "^7.0.0",
vip20 commented 2 years ago

Facing similar issue, any update on this bug report? currently on version 9.1.0-rc.8

fabio-stein commented 1 year ago

My solution:

I extended the original class with the refresh implemented, they didn't had implemented it

import { Injectable } from '@angular/core';
import firebase from 'firebase/compat/app';
import { Observable, of as observableOf, from } from 'rxjs';
import { catchError, map, switchMap, take } from 'rxjs/operators';
import { NbAuthStrategyOptions, NbAuthStrategyClass, NbAuthResult } from '@nebular/auth';

import { NbFirebaseBaseStrategy, NbFirebaseIdentityProviderStrategyOptions } from '@nebular/firebase-auth';

@Injectable()
export class FirebaseGoogleStrategy extends NbFirebaseBaseStrategy {
    protected defaultOptions: NbFirebaseIdentityProviderStrategyOptions = new NbFirebaseIdentityProviderStrategyOptions();

    static setup(options: NbFirebaseIdentityProviderStrategyOptions): [NbAuthStrategyClass, NbAuthStrategyOptions] {
        return [FirebaseGoogleStrategy, options];
    }

    authenticate(data?: any): Observable<NbAuthResult> {
        const module = 'authenticate';
        const provider = new firebase.auth.GoogleAuthProvider();
        const scopes = this.getOption('scopes');
        scopes.forEach((scope) => provider.addScope(scope));
        provider.setCustomParameters(this.getOption('customParameters'));

        return from(this.afAuth.signInWithPopup(provider)).pipe(
          switchMap((res) => this.processSuccess(res, module)),
          catchError((error) => this.processFailure(error, module)),
        );
      }

    refreshToken(data?: any): Observable<NbAuthResult> {
        const module = 'refreshToken';
        return this.afAuth.authState.pipe(
            take(1),
            switchMap((user) => {
                if (user == null) {
                    return observableOf(
                        new NbAuthResult(false, null, null, [
                            "There is no logged in user so refresh of id token isn't possible",
                        ]),
                    );
                }
                return this.refreshIdToken(user, module);
            }),
        );
    }

    protected refreshIdToken(user: firebase.User, module): Observable<NbAuthResult> {
        return from(user.getIdToken(true)).pipe(
            map((token) => {
                return new NbAuthResult(
                    true,
                    null,
                    this.getOption(`${module}.redirect.success`),
                    [],
                    this.getOption(`${module}.defaultMessages`),
                    this.createToken(token),
                );
            }),
            catchError((error) => this.processFailure(error, module)),
        );
    }
}