feathersjs / feathers

The API and real-time application framework
https://feathersjs.com
MIT License
14.97k stars 742 forks source link

Profile not being returned from google oauth redirect #3465

Closed ericuldall closed 2 months ago

ericuldall commented 2 months ago

I'm using the following strategy:

import querystring from 'qs';
import { oauth, OAuthStrategy } from '@feathersjs/authentication-oauth'
import { OAuth2Client } from 'google-auth-library'
import { NotAuthenticated } from '@feathersjs/errors'
export class GoogleStrategy extends OAuthStrategy {
    async getEntityData(profile, existing, params) {
        console.log({profile, existing, params});
        const baseData = await super.getEntityData(profile, existing, { ...params, provider: null });
        return {
            ...baseData,
            //profilePicture: profile.picture,
            email: profile.email,
            googleEmail: profile.email,
            oauthVerified: profile.email_verified || false,
            firstName: profile.given_name,
            lastName: profile.family_name
        };
    }

    async getEntityQuery(profile, params, queryKey, profileKey) {
        return {
            [queryKey]: profile[profileKey]
        }
    }

    async findEntity(profile, params, queryKey, profileKey) {
        if (typeof profile?.[profileKey] === "undefined") { return null };
        const query = await this.getEntityQuery(profile, params, queryKey, profileKey);
        const result = await super.entityService.find({
            ...params,
            provider: null,
            query
        });
        const [ entity = null ] = result.data ? result.data : result;
        return entity;
    }

    async getRedirect (authResult, params) {
        const { success, error } = this.authentication.configuration.oauth.redirect;
        const queryRedirect = (params && params.redirect) || '';
        let redirect, qs = '', query = false;
        if( authResult instanceof Error) {
            redirect = error;
            query = { error: authResult.message };
        } else if (authResult.accessToken) {
            redirect = success;
        } else {
            redirect = error;
        }
        const redirectUrl = redirect;
        const separator = redirect.endsWith('?') ? '' : '?';
        if (!query) {
            if (authResult.user.oauthVerified && authResult.accessToken) {
                query = { accessToken: authResult.accessToken };
            } else if (!authResult.accessToken) {
                query = { error: data.message || 'Google Auth Failed' };
            }
        }
        if (queryRedirect.length) {
            query.redirect = queryRedirect;
        }
        if (Object.keys(query).length) {
            qs = separator + querystring.stringify(query);
        }
        return redirectUrl + qs;
    }

    async authenticate (authentication, params) {
        const entity = super.configuration.entity;
        const profile = await super.getProfile(authentication, params);
        const existingEntity = await this.findEntity(profile, params, 'googleId', 'sub')
        || await this.findEntity(profile, params, 'email', 'email')
        || await super.getCurrentEntity(params);
        if (existingEntity) {
            profile.oauthVerified = typeof existingEntity.oauthVerified !== "undefined" ? existingEntity.oauthVerified : false;
        } else {
            profile.oauthVerified = true;
        }
        const authEntity = !existingEntity ? await super.createEntity(profile, {...params, provider: null})
        : await super.updateEntity(existingEntity, profile, { ...params, provider: null });
        return {
            authentication: { strategy: super.name },
            [entity]: authEntity
        }
    }
}

When I get redirected back from the google SSO page the authenticate argument doesn't have the profile property. Here's what I get if I console log that:

{
    strategy: 'google',
    code: '4/0AeaYSHDq8l615q312vfLpFMIaH9pZq8qyOmE8xD95smCvmC_iTfUPEM3_0COv9tfHC25Yg',
    scope: 'email profile https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile openid',
    authuser: '0',
    prompt: 'consent'
  }

@daffl Any idea why I might not be getting the profile info back?

Current versions:

npm list | grep feathers
├── @feathersjs/adapter-commons@5.0.11
├── @feathersjs/authentication-client@5.0.11
├── @feathersjs/authentication-local@5.0.11
├── @feathersjs/authentication-oauth@5.0.11
├── @feathersjs/authentication@5.0.11
├── @feathersjs/cli@5.0.11
├── @feathersjs/configuration@5.0.11
├── @feathersjs/errors@5.0.11
├── @feathersjs/feathers@5.0.11
├── @feathersjs/koa@5.0.11
├── @feathersjs/mongodb@5.0.11
├── @feathersjs/rest-client@5.0.11
├── @feathersjs/schema@5.0.11
├── @feathersjs/socketio@5.0.11
├── @feathersjs/transport-commons@5.0.11
├── @feathersjs/typebox@5.0.11
├── feathers-dataloader@0.1.0
├── feathers-hooks-common@7.0.3
├── feathers-mongodb@6.4.1
ericuldall commented 2 months ago

Fixed by switching to GSI button https://developers.google.com/identity/gsi/web/guides/personalized-button