Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FR-13875 - support logout hosted login in middleware #312

Merged
merged 5 commits into from
Jan 24, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions packages/nextjs/src/common/FronteggBaseProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import AppContext from './AppContext';
import initializeFronteggApp from '../utils/initializeFronteggApp';
import useRequestAuthorizeSSR from './useRequestAuthorizeSSR';
import useOnRedirectTo from '../utils/useOnRedirectTo';
import config from '../config';

const Connector: FC<FronteggProviderProps> = ({ router, appName = 'default', ...props }) => {
const isSSR = typeof window === 'undefined';
Expand All @@ -30,11 +31,6 @@ const Connector: FC<FronteggProviderProps> = ({ router, appName = 'default', ...
);
ContextHolder.setOnRedirectTo(onRedirectTo);

// useEffect(() => {
// if(window.location.pathname == '/account/login') {
// app.store.dispatch({ type: 'auth/requestAuthorize', payload: true });
// }
// }, [app]);
useRequestAuthorizeSSR({ app, user, tenants, activeTenant, session });
return (
<AppContext.Provider value={app}>
Expand All @@ -49,6 +45,8 @@ const Connector: FC<FronteggProviderProps> = ({ router, appName = 'default', ...
};

export const FronteggBaseProvider: FC<FronteggProviderProps> = (props) => {
config.fronteggAppOptions = props ?? {};

return (
<Connector {...props} framework={'nextjs'}>
{props.children}
Expand Down
8 changes: 3 additions & 5 deletions packages/nextjs/src/common/FronteggRouterBase.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import { FRONTEGG_AFTER_AUTH_REDIRECT_URL } from '../utils/common/constants';
import AppContext from './AppContext';
import React from 'react';
import { ParsedUrlQuery } from 'querystring';
import { useLogoutHostedLogin } from './hooks';

interface FronteggRouterBaseProps {
queryParams?: ParsedUrlQuery;
Expand All @@ -20,8 +19,7 @@ export function FronteggRouterBase(props: FronteggRouterBaseProps) {
const { queryParams = {}, pathArr, isAppDirEnabled } = props;
const app = useContext(AppContext);
const loginWithRedirect = useLoginWithRedirect();
const { requestAuthorize } = useLoginActions();
const logoutHosted = useLogoutHostedLogin();
const { requestAuthorize, logout } = useLoginActions();

useEffect(() => {
if (!app) {
Expand All @@ -40,7 +38,7 @@ export function FronteggRouterBase(props: FronteggRouterBaseProps) {
}
loginWithRedirect();
} else if (pathname === routesObj.logoutUrl) {
logoutHosted(window.location.origin + window.location.search);
logout();
}
} else {
if (pathname.startsWith(routesObj.hostedLoginRedirectUrl ?? '/oauth/callback')) {
Expand All @@ -54,6 +52,6 @@ export function FronteggRouterBase(props: FronteggRouterBaseProps) {
}
}
}
}, [app, queryParams, pathArr, loginWithRedirect, logoutHosted]);
}, [app, queryParams, pathArr, loginWithRedirect, logout]);
return <></>;
}
1 change: 1 addition & 0 deletions packages/nextjs/src/common/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { buildLogoutRoute } from '../api/urls';
* Hook to logout client side for hosted login
* @returns {Function} logout function to be used in the client side for hosted login
* @param redirectUrl - The URL to redirect to after successful logout will be window.location.href by default.
* @deprecated use `const { logout } = useLoginActions();`
*/

export const useLogoutHostedLogin = () => {
Expand Down
9 changes: 8 additions & 1 deletion packages/nextjs/src/common/useRequestAuthorizeSSR.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,19 @@ import { FronteggApp } from '@frontegg/js';
import { AllUserData } from '../types';

export default function useRequestAuthorizeSSR({ app, user, tenants, session }: { app: FronteggApp } & AllUserData) {
const userWithTokensOrNull = user
? {
...user,
refreshToken: session?.refreshToken,
accessToken: user.accessToken ?? session?.accessToken,
}
: null;
useEffect(() => {
app?.store.dispatch({
type: 'auth/requestAuthorizeSSR',
payload: {
accessToken: session?.accessToken,
user: user ? { ...user, refreshToken: session?.refreshToken } : null,
user: userWithTokensOrNull,
tenants,
},
});
Expand Down
5 changes: 4 additions & 1 deletion packages/nextjs/src/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ const setupEnvVariables = {
};

class Config {
public authRoutes: Partial<AuthPageRoutes> = {};
public fronteggAppOptions: Partial<WithFronteggAppOptions> = {};
constructor() {
if (typeof window === 'undefined') {
Expand Down Expand Up @@ -66,6 +65,10 @@ class Config {
return generateCookieDomain(this.appUrl);
}

get authRoutes(): Partial<AuthPageRoutes> {
return this.fronteggAppOptions?.authOptions?.routes ?? {};
}

private validatePassword() {
const passwordMaps = this.password;
for (let key of Object.keys(passwordMaps)) {
Expand Down
13 changes: 12 additions & 1 deletion packages/nextjs/src/middleware/ProxyResponseCallback.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ import { NextApiResponse } from 'next';
import config from '../config';
import CookieManager from '../utils/cookies';
import { createSessionFromAccessToken } from '../common';
import { isFronteggLogoutUrl } from './helpers';
import { isFronteggLogoutUrl, isFronteggOauthLogoutUrl } from './helpers';
import fronteggLogger from '../utils/fronteggLogger';
import { isSSOPostRequest } from '../utils/refreshAccessToken/helpers';
import { buildLogoutRoute } from '../api/urls';
import { authInitialState } from '@frontegg/redux-store';

const logger = fronteggLogger.child({ tag: 'FronteggApiMiddleware.ProxyResponseCallback' });
/**
Expand Down Expand Up @@ -41,6 +43,15 @@ const ProxyResponseCallback: ProxyResCallback<IncomingMessage, NextApiResponse>
res,
req,
});
if (isFronteggOauthLogoutUrl(url) || config.isHostedLogin) {
const referer = req.headers['referer'];
const logoutPath = config.authRoutes?.logoutUrl ?? authInitialState.routes.logoutUrl;
const shouldUseReferer = !!(referer && !referer.endsWith(logoutPath));
const redirectUrl = shouldUseReferer ? referer : config.appUrl;
const { asPath: hostedLogoutUrl } = buildLogoutRoute(redirectUrl, config.baseUrl);
res.status(302).end(hostedLogoutUrl);
return;
}
yuvalotem1 marked this conversation as resolved.
Show resolved Hide resolved
res.status(statusCode).end(bodyStr);
return;
}
Expand Down
17 changes: 11 additions & 6 deletions packages/nextjs/src/middleware/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,14 @@ export const rewritePath = (
return url;
};

export const isFronteggLogoutUrl = (url: string) => {
return url.endsWith('/logout');
// return (
// fronteggAuthApiRoutesRegex.filter((path) => path.endsWith('/logout')).findIndex((route) => url.endsWith(route)) >= 0
// );
};
/**
* Checks If route is a logout route
* @param url
*/
export const isFronteggLogoutUrl = (url: string) => url.endsWith('/logout');

/**
* Checks If route is a hosted logout route
* @param url
*/
export const isFronteggOauthLogoutUrl = (url: string) => url.endsWith('/oauth/logout');
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,6 @@ export const withFronteggApp = (app: FronteggCustomAppClass, options?: WithFront
};
};

config.authRoutes = options?.authOptions?.routes ?? {};
config.fronteggAppOptions = options ?? {};

function CustomFronteggApp(appProps: AppProps) {
const { user, tenants, activeTenant, session, envAppUrl, envBaseUrl, envClientId } = appProps.pageProps;
return (
Expand Down
2 changes: 1 addition & 1 deletion packages/nextjs/src/utils/routing/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export function getAuthRoutes(): { routesArr: string[]; routesObj: Record<string
export function isAuthRoute(pathname: string): boolean {
const { routesArr, routesObj } = getAuthRoutes();

if (config.fronteggAppOptions.hostedLoginBox) {
if (config.isHostedLogin) {
return (
routesObj.loginUrl === pathname ||
routesObj.logoutUrl === pathname ||
Expand Down
Loading