上下文&可重复的方案
我正在使用这些库和工具的组合:
我正在使用 auth code + pkce 重定向流,因此这是用户的流程:
-
他们降落在/,主页
他们单击/me
路由器链接
他们转到Azure B2C登录,因为所述页面具有此逻辑:
< msalauthenticationTemplate
InteractionType = {InteractionType.redirect}
AuthenticationRequest = {loginRequest}>
其中 loginrequest.state
设置为 router.aspath
(“预期”页面:/me
)
请注意,该页面也包裹在 a < nossr>
基于stack溢出的组件。<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<' /a>
-
用户在Azure B2C上登录,在/
(root)上重定向回我的应用程序
- :现在用户简要介绍
/
(主页)页面
在很短的时间后,用户将发送到/me
,其中将其签名为< /p>
MSAL文档中签名的地方似乎在OIDC或THE的 State
属性上没有太多内容重定向行为,我在。
简而言之:问题
如何确保我的nextJS应用程序中的msal反应将用户立即发送到启动上的“预期”页面,而不会简短地显示root Page的身份服务器重定向到位置?
相关的额外信息
这是我的自定义 _app.js
组件,这似乎是相关的,因为使重定向到预期的页面:
export default function MyApp({ Component, pageProps }) {
return (
<MsalProvider instance={msalInstance}>
<PageHeader></PageHeader>
<Component {...pageProps} />
</MsalProvider>
);
}
ps。为了帮助人们在线搜索的人们找到以下问题:该行为是由 navigatetoginrequesturl触发的:true
(是默认值)。将其设置为 false
明确完全禁用将用户完全发送到预期的页面。
使用中间件的尝试解决方案
我根据 app_initializer
在Angular中工作的方式尝试 ,使用中间件在某个时候这样:
// From another file:
// export const msalInstance = new PublicClientApplication(msalConfig);
export async function middleware(_request) {
const targetUrlAfterLoginRedirect = await msalInstance.handleRedirectPromise()
.then((result) => {
if (!!result && !!result.state) {
return result.state;
}
return null;
});
console.log('Found intended target before login flow: ', targetUrlAfterLoginRedirect);
// TODO: Send user to the intended page with router.
}
但是,这会在服务器的控制台上登录:
在登录流之前找到了预期的目标:null
因此中间件似乎还为时过早, msal-reactct
无法应对?耻辱,因为中间件将是完美的,可以为目标页面提供尽可能多的SSR。
这不是更改B2C方面重定向URL的选择,因为我将不断地向需要此行为的应用添加新路由。
请注意,我还尝试使用中间件来嗅出状态
,但是由于中间件在节点上运行,因此无法访问哈希片段。
动画gif显示闪烁的主页
这是一个动画gif,显示/home
页面在/me
之前已正确打开。警告,gif是一个浮躁的杂草,所以在扰流板标签中:
从浏览器中,问题“> 使用自定义 navigationclient
尝试解决方案
的 添加自定义 navigationclient
以更紧密地模仿Microsoft存储库中的NextJS示例,例如:
import { NavigationClient } from "@azure/msal-browser";
// See: https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-react/docs/performance.md#how-to-configure-azuremsal-react-to-use-your-routers-navigate-function-for-client-side-navigation
export class CustomNavigationClient extends NavigationClient {
constructor(router) {
super();
this.router = router;
}
async navigateInternal(url, options) {
console.log('
Context & Reproducible Scenario
I'm using the combination of these libraries and tools:
I'm using the Auth Code + PKCE redirect flow so this is the flow for users:
-
They land on /
, the home page
-
They click a /me
router link
-
They go to Azure B2C to log in because said page has this logic:
<MsalAuthenticationTemplate
interactionType={InteractionType.Redirect}
authenticationRequest={loginRequest}>
where loginRequest.state
is set to router.asPath
(the "intended" page: /me
)
Note that the page is also wrapped in a <NoSsr>
component based off Stack Overflow.
-
User logs in on Azure B2C, gets redirected back to my app at /
(the root)
-
⛔ Problem: the user now briefly sees the /
(home) page
-
After a very brief moment, the user gets sent to /me
where they are signed in
The MSAL docs don't seem to have much on the state
property from OIDC or this redirect behavior, and I can't find much about this in the MSAL sample for NextJS either.
In short: the issue
How do I make sure MSAL-React in my NextJS application send users to the "intended" page immediately on startup, without briefly showing the root page where the Identity Server redirects to?
Relevant extra information
Here's my custom _app.js
component, which seems relevant because it is a component that triggers handleRedirectPromise
which causes the redirect to intended page:
export default function MyApp({ Component, pageProps }) {
return (
<MsalProvider instance={msalInstance}>
<PageHeader></PageHeader>
<Component {...pageProps} />
</MsalProvider>
);
}
PS. To help folks searching online find this question: the behavior is triggered by navigateToLoginRequestUrl: true
(is the default) in the configuration. Setting it to false
plainly disables sending the user to the intended page at all.
Attempted solutions with middleware
I figured based on how APP_INITIALIZER
s work in Angular, to use middleware like this at some point:
// From another file:
// export const msalInstance = new PublicClientApplication(msalConfig);
export async function middleware(_request) {
const targetUrlAfterLoginRedirect = await msalInstance.handleRedirectPromise()
.then((result) => {
if (!!result && !!result.state) {
return result.state;
}
return null;
});
console.log('Found intended target before login flow: ', targetUrlAfterLoginRedirect);
// TODO: Send user to the intended page with router.
}
However, this logs on the server's console:
Found intended target before login flow: null
So it seems middleware is too early for msal-react
to cope with? Shame, because middleware would've been perfect, to allow as much SSR for target pages as possible.
It's not an option to change the redirect URL on B2C's side, because I'll be constantly adding new routes to my app that need this behavior.
Note that I also tried to use middleware to just sniff out the state
myself, but since the middleware runs on Node it won't have access to the hash fragment.
Animated GIF showing the flashing home page
Here's an animated gif that shows the /home
page is briefly (200ms or so) shown before /me
is properly opened. Warning, gif is a wee bit flashy so in a spoiler tag:

Attempted solution with custom NavigationClient
I've tried adding a custom NavigationClient
to more closely mimic the nextjs sample from Microsoft's repository, like this:
import { NavigationClient } from "@azure/msal-browser";
// See: https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-react/docs/performance.md#how-to-configure-azuremsal-react-to-use-your-routers-navigate-function-for-client-side-navigation
export class CustomNavigationClient extends NavigationClient {
constructor(router) {
super();
this.router = router;
}
async navigateInternal(url, options) {
console.log('???? Navigating Internal to', url);
const relativePath = url.replace(window.location.origin, "");
if (options.noHistory) {
this.router.replace(relativePath);
} else {
this.router.push(relativePath);
}
return false;
}
}
This did not solve the issue. The console.log
is there allowing me to confirm this code is not run on the server, as the Node logs don't show it.
Attempted solution: go through MSAL's SSR docs
Another thing I've tried is going through the documentation claiming @azure/msal-react
supports Server Side Rendering (SSR) but those docs nor the linked samples demonstrate how to solve my issue.
Attempted solution in _app.tsx
Another workaround I considered was to sniff out the hash fragment client side when the user returns to my app (and make sure the intended page is also in that state). I can successfully send the OpenID state
to B2C like this...
const extendedAuthenticationRequest = {
...authenticationRequest,
state: `~path~${asPath}~path~`,
};
...and see it returned in the Network tab of the dev tools.
However, when I try to extract it in my _app.tsx
still doesn't work. I tried this code from another Stack Overflow answer to get the .hash
:
const [isMounted, setMounted] = useState(false);
useEffect(() => {
if (isMounted) {
console.log('====> saw the following hash', window.location.hash);
const matches = /~path~(.+)~path~/.exec(window.location.hash);
if (matches && matches.length > 0 && matches[1]) {
const targetUrlAfterOpenIdRedirect = decodeURIComponent(matches[1]);
console.log("Routing to", targetUrlAfterOpenIdRedirect);
router.replace(targetUrlAfterOpenIdRedirect);
}
} else {
setMounted(true);
}
}, [isMounted]);
if (!isMounted) return null;
// else: render <MsalProvider> and the intended page component
This does find the intended page from the state
and executes routing, but still flashes the /home
page before going to the intended page.
Footnote: related GitHub issue
Submitted an issue at MSAL's GitHub repository too.
发布评论