diff --git a/.env.template b/.env.template index 509c4837f..310daeebe 100644 --- a/.env.template +++ b/.env.template @@ -7,6 +7,7 @@ API_BASE_URL=#{API_BASE_URL} AUTH0_DOMAIN=#{AUTH0_DOMAIN} AUTH0_CLIENT_ID=#{AUTH0_CLIENT_ID} AUTH0_REDIRECT_URI=#{AUTH0_REDIRECT_URI} +AUTH0_SCOPE=#{AUTH0_SCOPE} GITHUB_API_PAT=#{GITHUB_API_PAT} DISQUS_FORUM=#{DISQUS_FORUM} DISQUS_API_KEY=#{DISQUS_API_KEY} diff --git a/.github/workflows/template-build.yml b/.github/workflows/template-build.yml index 69ea95767..065e245bb 100644 --- a/.github/workflows/template-build.yml +++ b/.github/workflows/template-build.yml @@ -120,6 +120,7 @@ jobs: AUTH0_DOMAIN: ${{ vars.AUTH0_DOMAIN }} AUTH0_CLIENT_ID: ${{ vars.AUTH0_CLIENT_ID }} AUTH0_REDIRECT_URI: ${{ vars.AUTH0_REDIRECT_URI }} + AUTH0_SCOPE: ${{ vars.AUTH0_SCOPE }} GITHUB_API_PAT: ${{ secrets.CONTENT_GITHUB_TOKEN }} DISQUS_FORUM: ${{ vars.DISQUS_FORUM }} DISQUS_API_KEY: ${{ secrets.DISQUS_API_KEY }} diff --git a/src/components/bookmark/bookmark.js b/src/components/bookmark/bookmark.js index 0714c7e4c..c1c8d300a 100644 --- a/src/components/bookmark/bookmark.js +++ b/src/components/bookmark/bookmark.js @@ -7,6 +7,7 @@ import { BookmarkRule, RemoveBookmark, } from '../../services/apiService'; +import { useAuthService } from '../../services/authService'; import PropTypes from 'prop-types'; import { ApplicationInsights } from '@microsoft/applicationinsights-web'; @@ -21,8 +22,8 @@ const Bookmark = (props) => { const [change, setChange] = useState(0); const [bookmarked, setBookmarked] = useState(false); - const { isAuthenticated, user, getIdTokenClaims, loginWithRedirect } = - useAuth0(); + const { isAuthenticated, user, loginWithRedirect } = useAuth0(); + const { fetchIdToken } = useAuthService(); useEffect(() => { if (isAuthenticated) { @@ -42,10 +43,10 @@ const Bookmark = (props) => { async function onClick() { if (isAuthenticated) { setBookmarked(!bookmarked); - const jwt = await getIdTokenClaims(); + const jwt = await fetchIdToken(); const data = { ruleGuid: ruleId, UserId: user.sub }; !bookmarked - ? BookmarkRule(data, jwt.__raw) + ? BookmarkRule(data, jwt) .then(() => { setChange(change + 1); }) @@ -55,7 +56,7 @@ const Bookmark = (props) => { severityLevel: 3, }); }) - : RemoveBookmark({ ruleGuid: ruleId, UserId: user.sub }, jwt.__raw) + : RemoveBookmark({ ruleGuid: ruleId, UserId: user.sub }, jwt) .then(() => { setChange(change + 1); }) diff --git a/src/components/comments-not-connected/comments-not-connected.js b/src/components/comments-not-connected/comments-not-connected.js index a33def317..8b53c1b65 100644 --- a/src/components/comments-not-connected/comments-not-connected.js +++ b/src/components/comments-not-connected/comments-not-connected.js @@ -6,6 +6,7 @@ import { ConnectUserCommentsAccount, DisqusError, } from '../../services/apiService'; +import { useAuthService } from '../../services/authService'; import DisqusIcon from '-!svg-react-loader!../../images/disqusIcon.svg'; import { useAuth0 } from '@auth0/auth0-react'; @@ -20,7 +21,8 @@ const CommentsNotConnected = ({ userCommentsConnected, setState, state }) => { const [disqusUsername, setDisqusUsername] = useState(); const [errorMessage, setErrorMessage] = useState(null); - const { user, getIdTokenClaims } = useAuth0(); + const { user } = useAuth0(); + const { fetchIdToken } = useAuthService(); function connectAccounts() { if (disqusUsername) { @@ -29,13 +31,13 @@ const CommentsNotConnected = ({ userCommentsConnected, setState, state }) => { if (success.code == DisqusError.InvalidArg) { setErrorMessage('Username does not exist'); } - const jwt = await getIdTokenClaims(); + const jwt = await fetchIdToken(); ConnectUserCommentsAccount( { UserId: user.sub, CommentsUserId: success.response.id, }, - jwt.__raw + jwt ) .then((response) => { setState(state + 1); diff --git a/src/components/layout/layout.js b/src/components/layout/layout.js index 50dae85fb..1146dd750 100644 --- a/src/components/layout/layout.js +++ b/src/components/layout/layout.js @@ -38,10 +38,11 @@ const Layout = ({ authorizationParams={{ redirect_uri: process.env.AUTH0_REDIRECT_URI, }} - // redirectUri={process.env.AUTH0_REDIRECT_URI} onRedirectCallback={onRedirectCallback} useRefreshTokens={true} cacheLocation="localstorage" + scope={process.env.AUTH0_SCOPE} + audience={process.env.AUTH0_DOMAIN} > {/* eslint-disable-next-line jsx-a11y/no-static-element-interactions */}
{ const [disqusPrivacyEnabled, setDisqusPrivacyEnabled] = useState(false); const [change, setChange] = useState(0); const [viewStyle, setViewStyle] = useState('titleOnly'); - const { user, getIdTokenClaims, isAuthenticated } = useAuth0(); + const { user, isAuthenticated } = useAuth0(); + const { fetchIdToken } = useAuthService(); const handleOptionChange = (e) => { setViewStyle(e.target.value); }; async function onRemoveClick(ruleGuid) { - const jwt = await getIdTokenClaims(); + const jwt = await fetchIdToken(); if ( isAuthenticated && window.confirm('Are you sure you want to remove this tag?') ) { props.filter == Filter.Bookmarks - ? RemoveBookmark({ ruleGuid: ruleGuid, UserId: user.sub }, jwt.__raw) + ? RemoveBookmark({ ruleGuid: ruleGuid, UserId: user.sub }, jwt) .then(() => { setChange(change + 1); props.setState(props.state + 1); @@ -74,7 +76,7 @@ const ProfileContent = (props) => { severityLevel: 3, }); }) - : RemoveReaction({ ruleGuid: ruleGuid, UserId: user.sub }, jwt.__raw) + : RemoveReaction({ ruleGuid: ruleGuid, UserId: user.sub }, jwt) .then(() => { setChange(change + 1); }) @@ -139,8 +141,8 @@ const ProfileContent = (props) => { } async function getUserComments() { - const jwt = await getIdTokenClaims(); - GetUser(user.sub, jwt.__raw).then((success) => { + const jwt = await fetchIdToken(); + GetUser(user.sub, jwt).then((success) => { if (!success) { appInsights.trackException({ error: new Error('Error getting user'), @@ -350,11 +352,12 @@ const RuleList = ({ const components = { greyBox: GreyBox, }; - const { user, getIdTokenClaims } = useAuth0(); + const { user } = useAuth0(); + const { fetchIdToken } = useAuthService(); async function RemoveDisqusUser() { - const jwt = await getIdTokenClaims(); - RemoveUserCommentsAccount({ UserId: user.sub }, jwt.__raw) + const jwt = await fetchIdToken(); + RemoveUserCommentsAccount({ UserId: user.sub }, jwt) .then(() => { setState(state + 1); }) diff --git a/src/components/reaction/reaction.js b/src/components/reaction/reaction.js index 7e80a3e02..5d17b3e1e 100644 --- a/src/components/reaction/reaction.js +++ b/src/components/reaction/reaction.js @@ -8,6 +8,7 @@ import { ReactionType, RemoveReaction, } from '../../services/apiService'; +import { useAuthService } from '../../services/authService'; import { ApplicationInsights } from '@microsoft/applicationinsights-web'; const appInsights = new ApplicationInsights({ @@ -25,8 +26,8 @@ const Reaction = (props) => { const [change, setChange] = useState(0); const [currentReactionType, setCurrentReactionType] = useState(null); - const { isAuthenticated, user, getIdTokenClaims, loginWithRedirect } = - useAuth0(); + const { isAuthenticated, user, loginWithRedirect } = useAuth0(); + const { fetchIdToken } = useAuthService(); useEffect(() => { if (isAuthenticated) { @@ -81,11 +82,11 @@ const Reaction = (props) => { ruleGuid: ruleId, userId: user.sub, }; - const jwt = await getIdTokenClaims(); + const idToken = await fetchIdToken(); if (currentReactionType == type) { removePreviousReaction(); setCurrentReactionType(null); - RemoveReaction(data, jwt.__raw) + RemoveReaction(data, idToken) .then(() => { setChange(change + 1); }) @@ -109,7 +110,7 @@ const Reaction = (props) => { removePreviousReaction(); } setCurrentReactionType(type); - PostReactionForUser(data, jwt.__raw) + PostReactionForUser(data, idToken) .then(() => { setChange(change + 1); }) diff --git a/src/components/signin/signin.js b/src/components/signin/signin.js index 1cda678f8..5e2683399 100644 --- a/src/components/signin/signin.js +++ b/src/components/signin/signin.js @@ -6,6 +6,7 @@ import { GetGithubOrganisations, setUserOrganisation, } from '../../services/apiService'; +import { useAuthService } from '../../services/authService'; import { ApplicationInsights } from '@microsoft/applicationinsights-web'; const appInsights = new ApplicationInsights({ @@ -15,8 +16,8 @@ const appInsights = new ApplicationInsights({ }); const SignIn = () => { - const { isAuthenticated, loginWithRedirect, user, getIdTokenClaims } = - useAuth0(); + const { isAuthenticated, loginWithRedirect, user } = useAuth0(); + const { fetchIdToken } = useAuthService(); useEffect(() => { isAuthenticated ? setUserOrg() : null; @@ -26,13 +27,13 @@ const SignIn = () => { isAuthenticated ? await GetGithubOrganisations(user.nickname) .then(async (success) => { - const jwt = await getIdTokenClaims(); + const jwt = await fetchIdToken(); success.forEach(async (org) => { const data = { OrganisationId: org.id.toString(), UserId: user.sub, }; - await setUserOrganisation(data, jwt.__raw).catch((err) => { + await setUserOrganisation(data, jwt).catch((err) => { console.error('error: ' + err); }); }); diff --git a/src/pages/profile.js b/src/pages/profile.js index 292466250..30a4d670e 100644 --- a/src/pages/profile.js +++ b/src/pages/profile.js @@ -10,6 +10,7 @@ import GitHubIcon from '-!svg-react-loader!../images/github.svg'; import DisqusIcon from '-!svg-react-loader!../images/disqusIcon.svg'; import { useAuth0 } from '@auth0/auth0-react'; import { GetUser } from '../services/apiService'; +import { useAuthService } from '../services/authService'; import { ApplicationInsights } from '@microsoft/applicationinsights-web'; const appInsights = new ApplicationInsights({ @@ -21,8 +22,8 @@ const appInsights = new ApplicationInsights({ const Profile = ({ data, gitHubUsername }) => { const [selectedFilter, setSelectedFilter] = useState(4); const [state, setState] = useState(0); - const { user, isAuthenticated, loginWithRedirect, getIdTokenClaims } = - useAuth0(); + const { user, isAuthenticated, loginWithRedirect } = useAuth0(); + const { fetchIdToken } = useAuthService(); const [commentsConnected, setCommentsConnected] = useState(false); const [bookmarkedRulesCount, setBookmarkedRulesCount] = useState(); const [superLikedRulesCount, setSuperLikedRulesCount] = useState(); @@ -32,8 +33,8 @@ const Profile = ({ data, gitHubUsername }) => { const [commentedRulesCount, setCommentedRulesCount] = useState(); async function CheckUser() { - const jwt = await getIdTokenClaims(); - GetUser(user.sub, jwt.__raw) + const jwt = await fetchIdToken(); + GetUser(user.sub, jwt) .then((success) => { setCommentsConnected(success.commentsConnected); }) diff --git a/src/services/authService.js b/src/services/authService.js new file mode 100644 index 000000000..95e339cce --- /dev/null +++ b/src/services/authService.js @@ -0,0 +1,40 @@ +import { useAuth0 } from '@auth0/auth0-react'; + +export const useAuthService = () => { + const { getIdTokenClaims, getAccessTokenSilently, loginWithRedirect } = + useAuth0(); + + const fetchIdToken = async () => { + try { + const claims = await getIdTokenClaims(); + const expiryTime = claims.exp * 1000; + const currentTime = new Date().getTime(); + + if (expiryTime - currentTime < 60000) { + await getAccessTokenSilently({ + audience: process.env.AUTH0_DOMAIN, + scope: process.env.AUTH0_SCOPE, + cacheMode: 'off', + }); + + const refreshedClaims = await getIdTokenClaims(); + return refreshedClaims.__raw; + } + return claims.__raw; + } catch (error) { + if (window.confirm('Your session has expired. Please log in again')) { + const currentPage = + typeof window !== 'undefined' + ? window.location.pathname.split('/').pop() + : null; + await loginWithRedirect({ + appState: { + targetUrl: currentPage, + }, + }); + } + } + }; + + return { fetchIdToken }; +}; diff --git a/src/templates/rule.js b/src/templates/rule.js index 5df578667..b3fc8ed89 100644 --- a/src/templates/rule.js +++ b/src/templates/rule.js @@ -25,6 +25,7 @@ import RuleSideBar from '../components/rule-side-bar/rule-side-bar'; import formatDistance from 'date-fns/formatDistance'; import { format } from 'date-fns'; import { useAuth0 } from '@auth0/auth0-react'; +import { useAuthService } from '../services/authService'; import { faGithub } from '@fortawesome/free-brands-svg-icons'; const appInsights = new ApplicationInsights({ @@ -41,20 +42,21 @@ const Rule = ({ data, location }) => { }; const rule = data.markdownRemark; const categories = data.categories.nodes; - const { user, isAuthenticated, getIdTokenClaims } = useAuth0(); + const { user, isAuthenticated } = useAuth0(); + const { fetchIdToken } = useAuthService(); const [hiddenCount, setHiddenCount] = useState(0); const loadSecretContent = async (userOrgId) => { const hidden = document.getElementsByClassName('hidden'); if (hidden.length != 0) { - const token = await getIdTokenClaims(); + const token = await fetchIdToken(); for (var hiddenBlock of hidden) { const contentID = hiddenBlock.textContent || hiddenBlock.innerText; const guid = contentID.substring(0, 36); const orgID = contentID.substring(37); if (parseInt(orgID) == parseInt(userOrgId)) { isAuthenticated && guid - ? await GetSecretContent(guid, token.__raw) + ? await GetSecretContent(guid, token) .then((success) => { GetGithubOrganisationName(orgID) .then((nameSuccess) => {