Hi, change your front-end into:
<Button
color="secondary"
startIcon={<GoogleIcon />}
onClick={googleLogin}
fullWidth
variant="outlined"
size="large"
>
Login with Google
</Button>
and add googleLogin func as below:
const googleLogin = async () => {
try {
window.open(`http://localhost:3001/auth/google-logins/${from.replaceAll('/', '@')}`, "_self");
} catch (ex) {
console.log(ex)
}
}
change your route file like this:
<Route path="google-oauth-success-redirect">
<Route path=":accessToken/:refreshToken/:from" element={<GoogleOAuthSuccessRedirect />} />
</Route>
and add GoogleOAuthSuccessRedirect component:
import React, { useEffect } from 'react'
import { useNavigate, useParams } from 'react-router-dom';
import { setAuthTokens } from 'redux/features/auth/authSlice';
import { useAppDispatch } from 'redux/hooks';
type Props = {}
const GoogleOAuthSuccessRedirect = (props: Props) => {
let { accessToken, refreshToken, from } = useParams();
const navigate = useNavigate();
const dispatch = useAppDispatch()
useEffect(() => {
if (from && accessToken && refreshToken) {
dispatch(setAuthTokens({ accessToken, refreshToken }))
navigate('/' + from, { replace: true });
}
}, [accessToken, dispatch, from, navigate, refreshToken])
return (
<div>Loading...</div>
)
}
export default GoogleOAuthSuccessRedirect
turn to the backend side of your app and change that like this:
on controller:
@Get('google-logins/:from')
@UseGuards(GoogleOauthGuard)
async googleLogin(@Req() req: Request) {
}
@Get('google/callback')
@UseGuards(GoogleOauthGuard)
async googleLoginCallback(
@Req() req: Request,
@Res() res: Response,
) {
const auth = await this.authService.login(req.user);
res.redirect(`http://localhost:3000/google-oauth-success-redirect/${auth.accessToken}/${auth.refreshToken}${req.params.from}`)
}
on google strategy:
authenticate(req: any, options: any) {
if (!options?.state) {
options = { ...options, state: req.params.from }
}
return super.authenticate(req, options)
}
and google oauth guard as below:
import {
ExecutionContext,
Injectable,
UnauthorizedException,
} from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { AuthGuard, IAuthModuleOptions } from '@nestjs/passport';
import { Request, Response } from 'express';
@Injectable()
export class GoogleOauthGuard extends AuthGuard('google') {
constructor(private configService: ConfigService) {
super({
// accessType: 'offline',
// response_type: "code",
// display: 'popup',
// approvalPrompt: 'auto',
prompt: 'select_account', //"consent"
});
}
async canActivate(context: ExecutionContext) {
try {
const request = context.switchToHttp().getRequest() as Request;
const from = (request.query.state as string)?.replace(/\@/g, '/')
// response.setHeader('X-Frame-Options', 'SAMEORIGIN');
// await super.logIn(request) //to enabling session / we dont need it
const activate = (await super.canActivate(context)) as boolean;
request.params.from = from
return activate;
} catch (ex) {
throw ex
}
}
}
at the end you could access complete source code of my app that implemented with react typescript (redux toolkit rtk Query) and nestjs included of google oauth2 flow with passport.js.
resource