The problem was that when using reponseType: 'code'
as an option, I was not redirecting the popup window to the proper url. See the correct code below
// frontend
const webAuth = new auth0.WebAuth({
domain: activeConfig.client.auth.domain,
clientID: activeConfig.client.auth.clientId
});
const google = () => {
webAuth.popup.authorize({
domain: activeConfig.client.auth.domain,
clientId: activeConfig.client.auth.clientId,
audience: activeConfig.client.auth.audience,
redirectUri: `${activeConfig.client.app.url}${popupSocialAuthCB}`,
connection: 'google-oauth2',
responseType: 'code',
scope: 'openid profile email offline_access',
}, (err: auth0.Auth0Error | null, res: auth0.Auth0Result) => {
if (err) {
console.error(`Could not social auth. '${JSON.stringify(err)}'`);
throw err;
}
console.log(res);
});
}
// server.ts
app.use(popupSocialAuthCB, handlePopupSocialAuth);
// authenticate.ts
const storeTokensInCookies = async (req: express.Request, res: express.Response, stateRedirectCookie: string) => {
try {
const code = req.query.code;
const state = req.query.state;
if (state !== stateRedirectCookie) throw new Error(`Bad nonce '${state}'`);
const authRes = await fetch(`https://${activeConfig.server.auth.domain}/oauth/token`, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
grant_type: 'authorization_code',
audience: activeConfig.server.auth.audience,
client_id: activeConfig.server.auth.clientId,
client_secret: activeConfig.server.auth.secret,
code,
redirect_uri: `${activeConfig.server.app.url}${popupSocialAuthCB}`
}),
});
const data = await authRes.json();
if (!authRes.ok) {
const msg = `Token retrieval failed. '${JSON.stringify(data)}'`;
console.error(msg);
throw new Error(msg)
}
res.cookie(accessTokenCookie, data.access_token, {
httpOnly: true,
// secure: true,
});
res.cookie(refreshTokenCookie, data.refresh_token, {
httpOnly: true,
// secure: true,
});
return data
} catch (e) {
console.error(`[Authenticate] Couldn't get auth tokens`, e.stack);
throw e;
}
}
export const handlePopupSocialAuth = async (req: express.Request, res: express.Response) => {
try {
const state = JSON.parse(decodeURI(req.cookies[`com.auth0.auth.${req.query.state}`])).state;
const {
access_token,
id_token,
scope,
expires_in,
token_type,
} = await storeTokensInCookies(req, res, state);
// redirect so popup window has data in url to pass back to parent window
res.redirect(`${activeConfig.server.app.url}/popup-auth`
+ `#access_token=${access_token}`
+ `&scope=${scope}`
+ `&expires_in=${expires_in}`
+ `&token_type=${token_type}`
+ `&state=${state}`
+ `&id_token=${id_token}`
);
} catch (e) {
console.error(`[Authenticate] Couldn't handle popup auth callback`, e.stack);
res.status(500).send('Could not log you in');
}
}
// frontend, Next.js React SSR rendered for path of /popup-auth
// popup-auth.tsx
import auth0 from 'auth0-js';
import { activeConfig } from '../config';
import { isServer } from '../client/utils/isServer';
const CheckoutSocialAuth = () => {
if (isServer()) return null;
const webAuth = new auth0.WebAuth({
domain: activeConfig.client.auth.domain,
clientID: activeConfig.client.auth.clientId
});
webAuth.popup.callback({
// the default value of when left undefined, but typescript complains so we explictly specify
hash: window.location.hash
});
return <div>Logging in</div>;
}
export default CheckoutSocialAuth;