offline_access scope 포함시키기useRefreshTokens: true로 설정하면 Auth0 React SDK는 Refresh Token을 발급받아 자동으로 관리한다.getAccessTokenSilently를 호출할 때, Access Token이 만료되었을 경우 SDK가 자동으로 Refresh Token을 사용해 새로운 Access Token을 발급받는다.cacheLocation 설정에 따라 Refresh Token이 메모리나 로컬 스토리지에 저장된다.| Idle session time | Max session time | Max access token | 결과 |
|---|---|---|---|
| 1min | 2min | 2min | 1분 넘으면 자동 logout |
| 1min | 2min | 2min | 1분 넘으면 자동 logout |
| 2min | 3min | 2min | 2분 뒤 client-side exception has occurred |
| 2min | 3min | 2min | 2분 뒤 client-side exception has occurred |
2-1. Front에서 addEventListener를 활용해서 user action 감지
// 예시
import { useEffect } from 'react';
import { useAuth0 } from '@auth0/auth0-react';
const useUserActionTracker = () => {
const { getAccessTokenSilently, logout } = useAuth0();
let idleTimer;
useEffect(() => {
const resetIdleTimer = () => {
clearTimeout(idleTimer);
// 세션 연장을 위해 Access Token 갱신 시도
refreshSession();
};
const refreshSession = async () => {
try {
await getAccessTokenSilently();
console.log('Session extended');
} catch (error) {
console.error('Failed to refresh session', error);
logout({ returnTo: window.location.origin });
}
};
const events = ['mousemove', 'keydown', 'scroll', 'focus'];
// 이벤트 리스너 추가
events.forEach(event => {
window.addEventListener(event, resetIdleTimer);
});
// 초기 타이머 설정
idleTimer = setTimeout(() => {
logout({ returnTo: window.location.origin });
}, 10 * 60 * 1000); // 10분
return () => {
// 이벤트 리스너 및 타이머 정리
events.forEach(event => {
window.removeEventListener(event, resetIdleTimer);
});
clearTimeout(idleTimer);
};
}, [getAccessTokenSilently, logout]);
};
export default useUserActionTracker;
2-2. 세션 갱신 요청 남발을 막기 위해 debounce or throttle 활용
// 예시
import { useEffect, useRef } from 'react';
import { useAuth0 } from '@auth0/auth0-react';
const useUserActionTracker = () => {
const { getAccessTokenSilently, logout } = useAuth0();
const idleTimerRef = useRef(null);
const lastRefreshTimeRef = useRef(Date.now());
useEffect(() => {
const refreshSession = async () => {
try {
const now = Date.now();
// 9분 주기로 세션 갱신
if (now - lastRefreshTimeRef.current > 9 * 60 * 1000) {
await getAccessTokenSilently();
lastRefreshTimeRef.current = now;
console.log('Session extended');
}
} catch (error) {
console.error('Failed to refresh session', error);
logout({ returnTo: window.location.origin });
}
};
const resetIdleTimer = () => {
clearTimeout(idleTimerRef.current);
idleTimerRef.current = setTimeout(() => {
logout({ returnTo: window.location.origin });
}, 10 * 60 * 1000); // 10분
refreshSession();
};
const events = ['mousemove', 'keydown', 'scroll', 'focus'];
// 이벤트 리스너 등록
events.forEach(event => {
window.addEventListener(event, resetIdleTimer);
});
// 초기 타이머 설정
resetIdleTimer();
return () => {
// 이벤트 리스너 제거 및 타이머 정리
events.forEach(event => {
window.removeEventListener(event, resetIdleTimer);
});
clearTimeout(idleTimerRef.current);
};
}, [getAccessTokenSilently, logout]);
};
export default useUserActionTracker;
2-3. Refresh token 활용
// Auth0 Provider 설정
const Auth0Wrapper = ({ children }) => (
<Auth0Provider
domain="YOUR_AUTH0_DOMAIN"
clientId="YOUR_CLIENT_ID"
authorizationParams={{
redirect_uri: window.location.origin,
scope: 'openid profile email offline_access', // offline_access 추가
audience: 'YOUR_API_AUDIENCE',
}}
useRefreshTokens={true}
cacheLocation="localstorage" // Refresh Token 저장소 설정
>
{children}
</Auth0Provider>
);
export default () => (
<Auth0Wrapper>
<App />
</Auth0Wrapper>
);
import { useEffect, useRef } from 'react';
import { useAuth0 } from '@auth0/auth0-react';
const useUserActionTracker = () => {
const { getAccessTokenSilently, logout } = useAuth0();
const idleTimerRef = useRef(null);
const actionTimerRef = useRef(null);
useEffect(() => {
const refreshSession = async () => {
try {
await getAccessTokenSilently();
console.log('Access Token refreshed');
} catch (error) {
console.error('Failed to refresh session:', error);
logout({ returnTo: window.location.origin });
}
};
const resetIdleTimer = () => {
clearTimeout(idleTimerRef.current);
clearTimeout(actionTimerRef.current);
// 10분 뒤 자동 로그아웃
idleTimerRef.current = setTimeout(() => {
logout({ returnTo: window.location.origin });
}, 10 * 60 * 1000); // 10분
// 10분 뒤 Access Token 갱신
actionTimerRef.current = setTimeout(() => {
refreshSession();
}, 10 * 60 * 1000); // 10분
};
const userActionEvents = ['mousemove', 'keydown', 'scroll', 'click'];
// 유저 액션 리스너 등록
userActionEvents.forEach(event =>
window.addEventListener(event, resetIdleTimer)
);
// 초기 타이머 설정
resetIdleTimer();
return () => {
// 리스너 및 타이머 정리
userActionEvents.forEach(event =>
window.removeEventListener(event, resetIdleTimer)
);
clearTimeout(idleTimerRef.current);
clearTimeout(actionTimerRef.current);
};
}, [getAccessTokenSilently, logout]);
};
export default useUserActionTracker;
import React from 'react';
import { Auth0Provider } from '@auth0/auth0-react';
import useUserActionTracker from './useUserActionTracker';
const App = () => {
useUserActionTracker();
return (
<div>
<h1>Welcome to My App</h1>
<p>If you're inactive for 10 minutes, you will be logged out.</p>
</div>
);
};
import { Auth0Provider } from '@auth0/auth0-react';
const App = ({ children }) => (
<Auth0Provider
domain="YOUR_AUTH0_DOMAIN"
clientId="YOUR_CLIENT_ID"
authorizationParams={{
redirect_uri: window.location.origin,
scope: 'openid profile email offline_access', // Refresh Token을 위한 offline_access 추가
audience: 'YOUR_API_AUDIENCE',
}}
useRefreshTokens={true} // Refresh Token 사용
cacheLocation="localstorage" // localStorage에 토큰 저장
>
{children}
</Auth0Provider>
);
export default App;
And then, you need to use LocalStorageCache class to access the keys and its value.
https://community.auth0.com/t/how-to-get-refresh-token-like-getaccesstokensilently/86973
const refresh_token = new LocalStorageCache;
const key = refresh_token.allKeys().find(key => key.includes('auth0spa'));
const refresh_token_value = refresh_token.get(key);
const finalRefreshToken = refresh_token_value?.body?.refresh_token
localStorage.setItem('refresh_token', finalRefreshToken);