Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions src/Auth/Auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,17 @@ function isUserSignedIn(callback) {
return currentProvider.isUserSignedIn(callback);
}

async function refreshTokens() {
if (currentProvider === undefined) {
return;
}
if (!currentProvider.refreshTokens) {
console.warn('Method refreshTokens is not implemented in the current authentication provider');
return;
}
return currentProvider.refreshTokens();
}

function getUserEmail() {
if (currentProvider === undefined) {
return undefined;
Expand Down Expand Up @@ -163,6 +174,7 @@ const Auth = {
signOut,
onAuthStateChanged,
isUserSignedIn,
refreshTokens,
getUserEmail,
getUserName,
getUserId,
Expand Down
89 changes: 48 additions & 41 deletions src/AuthKeycloakRedirect/AuthKeycloakRedirect.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ const redirectOnAuthSuccess = () => {

const _acquireTokensByRequestAndAccount = async (tokenReq, account) => {
if (!tokenReq) {
console.warn('No base access token request provided');
console.warn('No token request provided');
tokenReq = {};
}

Expand All @@ -65,29 +65,30 @@ const _acquireTokensByRequestAndAccount = async (tokenReq, account) => {
msalApp
.acquireTokenRedirect(tokenReq)
.then((tokenRes) => tokenRes) // Token acquired with interaction
.catch((tokenRedirectError) => tokenRedirectError); // Token retrieval failed
.catch((tokenRedirectError) => console.error(tokenRedirectError));
}
throw silentTokenFetchError;
});
};

export const acquireTokens = async () => {
// When forceRefresh is set to true, existing tokens in browser storage are ignored and new tokens are retrieved with
// a silent request
export const acquireTokens = async (forceRefresh = false) => {
if (!checkInit()) return;

const idToken = readFromStorage('authIdToken');
const accessToken = readFromStorage('authAccessToken');
const authenticated = readFromStorage('authAuthenticated') === 'true';
if (authenticated && idToken != null && accessToken != null) {
return { accessToken, idToken };
if (!forceRefresh) {
const idToken = readFromStorage('authIdToken');
const accessToken = readFromStorage('authAccessToken');
const authenticated = readFromStorage('authAuthenticated') === 'true';
if (authenticated && idToken != null && accessToken != null) {
return { accessToken, idToken };
}
}

const account = msalApp.getAllAccounts()?.[0];
const tokenReq = config.accessRequest;
if (account === undefined) {
return undefined;
}
if (account === undefined) return;

return await _acquireTokensByRequestAndAccount(tokenReq, account);
return await _acquireTokensByRequestAndAccount(config.accessRequest, account);
};

const handleResponse = (response) => {
Expand Down Expand Up @@ -144,6 +145,11 @@ export const isAsync = () => {
return false;
};

const _updateTokensInStorage = (tokens) => {
if (tokens?.idToken) writeToStorage('authIdToken', tokens.idToken);
if (tokens?.accessToken) writeToStorage('authAccessToken', tokens.accessToken);
};

const _extractRolesFromAccessToken = (accessToken) => {
let result = [];
if (accessToken) {
Expand All @@ -162,41 +168,42 @@ export const isUserSignedIn = async () => {
return true;
}

// Resume interaction if one is already in progress
if (readFromStorage('authInteractionInProgress') === name) {
clearFromStorage('authInteractionInProgress');

const locationHashParameters = new URLSearchParams(window.location.hash.substring(1));
if (locationHashParameters.has('state')) {
if (locationHashParameters.has('iss', config?.msalConfig?.auth?.authorityMetadata?.issuer)) {
// Resume redirect workflow process
msalApp.handleRedirectPromise().then(handleResponse);
} else if (locationHashParameters.has('iss')) {
console.warn(
'Issuer found in url ("' +
config?.msalConfig?.auth?.authorityMetadata?.issuer +
'") does not match the keycloak configuration ("' +
locationHashParameters.get('iss') +
'")'
);
try {
// Resume interaction if one is already in progress
if (readFromStorage('authInteractionInProgress') === name) {
clearFromStorage('authInteractionInProgress');

const locationHashParameters = new URLSearchParams(window.location.hash.substring(1));
if (locationHashParameters.has('state')) {
if (locationHashParameters.has('iss', config?.msalConfig?.auth?.authorityMetadata?.issuer)) {
msalApp.handleRedirectPromise().then(handleResponse); // Resume redirect workflow process
} else if (locationHashParameters.has('iss')) {
const configIssuer = config?.msalConfig?.auth?.authorityMetadata?.issuer;
const urlIssuer = locationHashParameters.get('iss');
console.warn(`Issuer found in url "${urlIssuer}" does not match keycloak configuration: "${configIssuer}"`);
}
}
}
}

// Otherwise, try to acquire a token silently to implement SSO
const tokens = await acquireTokens();
if (tokens?.idToken !== undefined) {
writeToStorage('authIdToken', tokens.idToken);
}
if (tokens?.accessToken !== undefined) {
const accessToken = tokens.accessToken;
authData.roles = _extractRolesFromAccessToken(accessToken);
writeToStorage('authAccessToken', accessToken);
return true;
// Otherwise, try to acquire a token silently to implement SSO
const tokens = await acquireTokens();
_updateTokensInStorage(tokens);
if (tokens?.accessToken !== undefined) {
authData.roles = _extractRolesFromAccessToken(tokens.accessToken);
return true;
}
} catch (e) {
console.error(e);
}
return false;
};

export const refreshTokens = async () => {
const tokens = await acquireTokens(true);
_updateTokensInStorage(tokens);
return tokens;
};

export const getUserEmail = () => {
if (!checkInit()) return;
// Note: account data from MSAL seems to contain user email in the 'username' property
Expand Down