Skip to content

Commit

Permalink
Auth exp check reset (#4028)
Browse files Browse the repository at this point in the history
* option to reset expiration intervals for oauth2

* prettier

* Add unit tests for auth layer

* prettier

---------

Co-authored-by: Anna Milewska <anna.milewska@sap.com>
Co-authored-by: Waldemar Mazurek <waldemar.mazurek@sap.com>
  • Loading branch information
3 people authored Nov 29, 2024
1 parent 342d013 commit 919bcf1
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 15 deletions.
7 changes: 5 additions & 2 deletions core/src/core-api/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ class LuigiAuth {
* @param {AuthData} data - new auth data object
* @example Luigi.auth().store.setAuthData(data)
*/
setAuthData: data => AuthStoreSvc.setAuthData(data),
setAuthData: (data) => AuthStoreSvc.setAuthData(data),
/**
* Clears authorization data from store
* @memberof AuthorizationStore
Expand All @@ -135,7 +135,10 @@ class LuigiAuth {
* @memberof AuthorizationStore
* @example Luigi.auth().store.setNewlyAuthorized()
*/
setNewlyAuthorized: () => AuthStoreSvc.setNewlyAuthorized()
setNewlyAuthorized: () => {
AuthStoreSvc.setNewlyAuthorized();
AuthLayerSvc.resetExpirationChecks();
}
};
}
}
Expand Down
20 changes: 13 additions & 7 deletions core/src/services/auth-layer.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,11 @@ class AuthLayerSvcClass {
this.idpProviderInstance = this.getIdpProviderInstance(idpProviderName, idpProviderSettings);
if (GenericHelpers.isPromise(this.idpProviderInstance)) {
return this.idpProviderInstance
.then(resolved => {
.then((resolved) => {
this.idpProviderInstance = resolved;
return this.checkAuth(idpProviderSettings);
})
.catch(err => {
.catch((err) => {
const errorMsg = `Error: ${err.message || err}`;
console.error(errorMsg, err.message && err);
LuigiConfig.setErrorMessage(errorMsg);
Expand Down Expand Up @@ -88,13 +88,13 @@ class AuthLayerSvcClass {
}

if (this.idpProviderInstance.settings && GenericHelpers.isFunction(this.idpProviderInstance.settings.userInfoFn)) {
this.idpProviderInstance.settings.userInfoFn(this.idpProviderInstance.settings, authData).then(userInfo => {
this.idpProviderInstance.settings.userInfoFn(this.idpProviderInstance.settings, authData).then((userInfo) => {
this.setUserInfo(userInfo);
this.setLoggedIn(true);
});
} else {
if (GenericHelpers.isFunction(this.idpProviderInstance.userInfo)) {
this.idpProviderInstance.userInfo(idpProviderSettings).then(userInfo => {
this.idpProviderInstance.userInfo(idpProviderSettings).then((userInfo) => {
this.setUserInfo(userInfo);
this.setLoggedIn(true);
});
Expand All @@ -121,7 +121,7 @@ class AuthLayerSvcClass {

async startAuthorization() {
if (this.idpProviderInstance) {
return this.idpProviderInstance.login().then(res => {
return this.idpProviderInstance.login().then((res) => {
AuthStoreSvc.setNewlyAuthorized();
if (res) {
// TODO: is not required for secure usecases, only if auth is done within core.
Expand All @@ -135,7 +135,7 @@ class AuthLayerSvcClass {

logout() {
const authData = AuthHelpers.getStoredAuthData();
const logoutCallback = async redirectUrl => {
const logoutCallback = async (redirectUrl) => {
await LuigiAuth.handleAuthEvent('onLogout', this.idpProviderInstance.settings, undefined, redirectUrl);
AuthStoreSvc.removeAuthData();
};
Expand All @@ -162,7 +162,7 @@ class AuthLayerSvcClass {
const idpProvider = GenericHelpers.getConfigValueFromObject(idpProviderSettings, 'idpProvider');
if (idpProvider) {
const customIdpInstance = await new idpProvider(idpProviderSettings);
['login'].forEach(requiredFnName => {
['login'].forEach((requiredFnName) => {
if (!GenericHelpers.isFunction(customIdpInstance[requiredFnName])) {
throw this.IdpProviderException(
`${requiredFnName} function does not exist in custom IDP Provider ${idpProviderName}`
Expand Down Expand Up @@ -190,6 +190,12 @@ class AuthLayerSvcClass {
this.idpProviderInstance.unload();
}
}

resetExpirationChecks() {
if (this.idpProviderInstance && GenericHelpers.isFunction(this.idpProviderInstance.resetExpirationChecks)) {
this.idpProviderInstance.resetExpirationChecks();
}
}
}

export const AuthLayerSvc = new AuthLayerSvcClass();
55 changes: 55 additions & 0 deletions core/test/services/auth-layer.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -496,5 +496,60 @@ describe('AuthLayer', () => {
});
});
});

describe('resetExpirationChecks', () => {
let resetExpirationChecksStub;

beforeEach(() => {
resetExpirationChecksStub = sinon.stub();
AuthLayerSvc.idpProviderInstance = { resetExpirationChecks: resetExpirationChecksStub };
});

afterEach(() => {
sinon.restore();
});

it('should call resetExpirationChecks once when idpProviderInstance is defined', () => {
AuthLayerSvc.resetExpirationChecks();
sinon.assert.calledOnce(resetExpirationChecksStub);
});

it('should not call resetExpirationChecks when idpProviderInstance is undefined', () => {
AuthLayerSvc.idpProviderInstance = undefined;
AuthLayerSvc.resetExpirationChecks();
sinon.assert.notCalled(resetExpirationChecksStub);
});

it('should call resetExpirationChecks multiple times for consecutive invocations', () => {
AuthLayerSvc.resetExpirationChecks();
AuthLayerSvc.resetExpirationChecks();
sinon.assert.calledTwice(resetExpirationChecksStub);
});

it('should handle errors thrown by resetExpirationChecks', () => {
const error = new Error('Test Error');
resetExpirationChecksStub.throws(error);
try {
AuthLayerSvc.resetExpirationChecks();
assert.fail('Expected error to be thrown');
} catch (e) {
assert.strictEqual(e.message, 'Test Error');
}
});

it('should invoke resetExpirationChecks independently for each idpProviderInstance', () => {
const resetExpirationChecksStub1 = sinon.stub();
const resetExpirationChecksStub2 = sinon.stub();

AuthLayerSvc.idpProviderInstance = { resetExpirationChecks: resetExpirationChecksStub1 };
AuthLayerSvc.resetExpirationChecks();

AuthLayerSvc.idpProviderInstance = { resetExpirationChecks: resetExpirationChecksStub2 };
AuthLayerSvc.resetExpirationChecks();

sinon.assert.calledOnce(resetExpirationChecksStub1);
sinon.assert.calledOnce(resetExpirationChecksStub2);
});
});
});
});
15 changes: 9 additions & 6 deletions plugins/auth/src/auth-oauth2/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,7 @@ export default class oAuth2ImplicitGrant {
}

parseIdToken(token) {
const payload = token
.split('.')[1]
.replace(/-/g, '+')
.replace(/_/g, '/');
const payload = token.split('.')[1].replace(/-/g, '+').replace(/_/g, '/');
return JSON.parse(window.atob(payload));
}

Expand Down Expand Up @@ -88,7 +85,7 @@ export default class oAuth2ImplicitGrant {
// TODO: We're not resolving the promise at any time,
// since oauth2 is redirecting off the page
// maybe it is possible to catch errors
document.querySelector('form#signIn').addEventListener('load', e => {
document.querySelector('form#signIn').addEventListener('load', (e) => {
console.info('load, e', e, this);
});
});
Expand Down Expand Up @@ -149,7 +146,13 @@ export default class oAuth2ImplicitGrant {
const crypto = window.crypto;
const random = Array.from(crypto.getRandomValues(new Uint8Array(20)));

return random.map(x => validChars[x % validChars.length]).join('');
return random.map((x) => validChars[x % validChars.length]).join('');
}

resetExpirationChecks() {
this.unload();
this.setTokenExpirationAction();
this.setTokenExpireSoonAction();
}

unload() {
Expand Down

0 comments on commit 919bcf1

Please sign in to comment.