使用Auth0 getAccessTokenSilently时遭遇frame-ancestors 'none' CSP错误
问题描述
用户会话过期后,调用getAccessTokenSilently获取新访问令牌时,触发以下错误:
Refused to frame 'https://****.uk.auth0.com/' because an ancestor violates the following Content Security Policy directive: "frame-ancestors 'none'"
复现步骤
- 让用户会话过期
- 调用
getAccessTokenSilently获取新的访问令牌 - 控制台中观察到上述错误
相关代码
_app.tsx
import React from 'react'; import { Provider } from 'react-redux'; import { Auth0Provider, useAuth } from '@auth0/auth0-react'; import axios from 'axios'; import { ApiProvider } from './ApiProvider'; import { store } from './store'; import appConfig from './config'; import { handleOnRedirectCallback } from './auth'; const ApiProviderCustom = (props: ExtendedAppProps) => { const { Component, pageProps } = props; const { user, isAuthenticated, getAccessTokenSilently } = useAuth(); const instance = axios.create(); if (user && isAuthenticated) { instance.interceptors.request.use( async config => { const accessToken = await getAccessTokenSilently(); config.headers.Authorization = `Bearer ${accessToken}`; return config; }, error => Promise.reject(error) ); } return ( <ApiProvider instance={instance}> <Component {...pageProps} /> </ApiProvider> ); }; const App = (props: ExtendedAppProps) => { return ( <Provider store={store}> <Auth0Provider domain={appConfig.auth.CLIENT_DOMAIN} clientId={appConfig.auth.CLIENT_ID} authorizationParams={{ audience: appConfig.auth.AUDIENCE, scope: appConfig.auth.SCOPE.replace(',', ' '), redirect_uri: typeof window !== 'undefined' ? window.location.origin : undefined, }} cacheLocation='localstorage' onRedirectCallback={handleOnRedirectCallback} > <ApiProviderCustom {...props} /> </Auth0Provider> </Provider> ); }; export default App;
ApiContext.tsx
import { ReactNode, createContext } from 'react' import { ApiContextType } from 'src/types/global' import BackendApi from 'src/api/BackendApi' import { useAuth } from 'src/hooks/useAuth' import { isDefined } from 'src/utils/miscellaneous' import { AxiosInstance } from 'axios' import appConfig from 'src/configs/appConfig' type Props = { instance: AxiosInstance children: ReactNode } const defaultProvider: ApiContextType = { backendApi: new BackendApi(undefined, appConfig.backend_base_url, undefined, appConfig.machine_learning_base_url) } const ApiContext = createContext(defaultProvider) const ApiProvider = ({ instance, children }: Props) => { // ** Hooks const { user, isAuthenticated } = useAuth() if (isDefined(user) && isAuthenticated) { const authenticatedProvider: ApiContextType = { backendApi: new BackendApi(undefined, appConfig.backend_base_url, instance, appConfig.machine_learning_base_url) } return <ApiContext.Provider value={authenticatedProvider}>{children}</ApiContext.Provider> } else { return <ApiContext.Provider value={defaultProvider}>{children}</ApiContext.Provider> } } export { ApiContext, ApiProvider }
注:当前使用Auth0 Universal Login配置,问题与租户CSP设置的frame-ancestors 'none'指令直接相关。
问题原因
会话过期后,getAccessTokenSilently默认采用iframe静默授权方式获取新令牌,但Auth0租户的CSP规则frame-ancestors 'none'禁止任何页面嵌入该Auth0域名的iframe,因此触发权限错误。
解决方案
方案1:调整Auth0租户CSP设置
- 登录Auth0管理后台,进入目标租户
- 导航至Security > Content Security Policy
- 在
frame-ancestors指令中添加你的应用域名(例如https://your-app-domain.com),修改后示例:frame-ancestors 'self' https://your-app-domain.com; - 保存设置并等待生效
方案2:启用Refresh Token模式绕过iframe
通过配置useRefreshTokens让getAccessTokenSilently优先使用Refresh Token获取新令牌,完全避免iframe依赖:
- 先在Auth0应用设置中开启Refresh Token:
- 进入应用的Advanced Settings > Grant Types,勾选
Refresh Token
- 进入应用的Advanced Settings > Grant Types,勾选
- 修改
Auth0Provider配置:<Auth0Provider domain={appConfig.auth.CLIENT_DOMAIN} clientId={appConfig.auth.CLIENT_ID} authorizationParams={{ audience: appConfig.auth.AUDIENCE, scope: appConfig.auth.SCOPE.replace(',', ' '), redirect_uri: typeof window !== 'undefined' ? window.location.origin : undefined, }} cacheLocation='localstorage' onRedirectCallback={handleOnRedirectCallback} useRefreshTokens={true} // 启用Refresh Token useRefreshTokensFallback={false} // 禁用iframe回退(可选) > <ApiProviderCustom {...props} /> </Auth0Provider>
方案3:捕获错误并回退到显式登录
在axios拦截器中捕获CSP错误,引导用户重新登录:
- 在
ApiProviderCustom中解构loginWithRedirect:const { user, isAuthenticated, getAccessTokenSilently, loginWithRedirect } = useAuth(); - 修改拦截器逻辑:
instance.interceptors.request.use( async config => { try { const accessToken = await getAccessTokenSilently(); config.headers.Authorization = `Bearer ${accessToken}`; return config; } catch (error) { // 识别iframe相关的CSP错误 if (error instanceof Error && error.message.includes('frame-ancestors')) { await loginWithRedirect(); // 触发显式登录 } return Promise.reject(error); } }, error => Promise.reject(error) );
内容的提问来源于stack exchange,提问作者Parth Parmar
相关产品推荐
相关产品推荐

