diff --git a/src/config/oidcConfig.js b/src/config/oidcConfig.js index 98e59117b..cccc6c404 100644 --- a/src/config/oidcConfig.js +++ b/src/config/oidcConfig.js @@ -1,7 +1,7 @@ -/* This file is part of the BIMData Platform package. -(c) BIMData support@bimdata.io -For the full copyright and license information, please view the LICENSE -file that was distributed with this source code. */ +/** + * OIDC Client Configuration + * See: https://github.com/IdentityModel/oidc-client-js/wiki#configuration + */ const APP_BASE_URL = process.env.VUE_APP_BASE_URL; const AUTHORITY = `${process.env.VUE_APP_IAM_BASE_URL}/auth/realms/bimdata`; @@ -9,18 +9,27 @@ const OIDC_ENDPOINT = `${AUTHORITY}/protocol/openid-connect`; const CLIENT_ID = process.env.VUE_APP_OIDC_CLIENT_ID; export const oidcConfig = { + // Auth request config authority: AUTHORITY, - response_type: "code", client_id: CLIENT_ID, - redirect_uri: `${APP_BASE_URL}/oidc-callback`, scope: "openid profile email", - post_logout_redirect_uri: APP_BASE_URL, + response_type: "code", + redirect_uri: `${APP_BASE_URL}/oidc-callback`, extraQueryParams: { kc_idp_hint: "bimdataconnect" }, - automaticSilentRenew: true, + + // Logout config + post_logout_redirect_uri: APP_BASE_URL, revokeAccessTokenOnSignout: true, + + // Enable access token refresh + automaticSilentRenew: true, + + // Other options clockSkew: 900, + + // Auth metadata metadata: { issuer: AUTHORITY, authorization_endpoint: `${OIDC_ENDPOINT}/auth`, diff --git a/src/server/AuthService.js b/src/server/AuthService.js index 806166a3c..62798d9d7 100644 --- a/src/server/AuthService.js +++ b/src/server/AuthService.js @@ -17,6 +17,10 @@ class AuthServive { ); } + signInSilent() { + return userManager.signinSilent(); + } + signInCallback() { return userManager.signinRedirectCallback(); } @@ -24,6 +28,15 @@ class AuthServive { signOut() { return userManager.signoutRedirect(); } + + onUserLoaded(callback) { + this._userLoadedCallback = callback; + userManager.events.addUserLoaded(callback); + } + + offUserLoaded() { + userManager.events.removeUserLoaded(this._userLoadedCallback); + } } const service = new AuthServive(); diff --git a/src/state/auth.js b/src/state/auth.js index 8421cba7e..22a85bb3e 100644 --- a/src/state/auth.js +++ b/src/state/auth.js @@ -8,14 +8,21 @@ const state = reactive({ }); const authenticate = async redirectPath => { - const user = await AuthService.getUser(); - if (user) { - if (!state.isAuthenticated) { - state.isAuthenticated = true; - state.accessToken = user.access_token; - } - } else { + let user = await AuthService.getUser(); + if (!user) { await AuthService.signIn(redirectPath); + return; + } + if (!state.isAuthenticated) { + if (user.expired) { + // Refresh access token silently + user = await AuthService.signInSilent(); + } + // Keep access token up to date across refresh + AuthService.onUserLoaded(user => (state.accessToken = user.access_token)); + // Set auth state + state.isAuthenticated = true; + state.accessToken = user.access_token; } }; @@ -30,11 +37,12 @@ const signInCallback = async () => { const signOut = async () => { await AuthService.signOut(); + AuthService.offUserLoaded(); state.isAuthenticated = false; - state.user = null; + state.accessToken = null; }; -// Keep access token up to date across refresh +// Keep api client access token in sync with state watchEffect(() => (apiClient.accessToken = state.accessToken)); export function useAuth() { diff --git a/src/views/model-viewer/ModelViewer.vue b/src/views/model-viewer/ModelViewer.vue index b2bb8f68f..562c20203 100644 --- a/src/views/model-viewer/ModelViewer.vue +++ b/src/views/model-viewer/ModelViewer.vue @@ -6,7 +6,7 @@