I've got a single page application using adal-angular (https://github.com/AzureAD/azure-activedirectory-library-for-js) for secure access. Whenever the token expires (after 1 hour), I want to redirect the user to the AD login screen, to re-enter their password. I've modified adal-angular.js to call _adal.login() whenever the token has expired, however this appears to automatically re-authenticate the user and take them directly to the redirect URI with the renewed token. I'm not sure what it is that is cached, that allows the renewal automatically.
How can I force the user to re-enter their password whenever the token expires?
AFAIK, the adal.js acquire the token using the implicit flow in a iframe when the token is expired. It doesn't support to force the user sign-in again.
To implement this feature, you can modify the source code. Here is an code sample modifying the acquireToken
and login
method in the adal.js for your reference:
AuthenticationContext.prototype.acquireToken = function (resource, callback) {
if (this._isEmpty(resource)) {
this.warn('resource is required');
callback('resource is required', null, 'resource is required');
return;
}
var token = this.getCachedToken(resource);
if (token) {
this.info('Token is already in cache for resource:' + resource);
callback(null, token, null);
return;
}
//add by me-begin
var expiry = this._getItem(this.CONSTANTS.STORAGE.EXPIRATION_KEY + resource);
var isExpired = true;
// If expiration is within offset, it will force renew
var offset = this.config.expireOffsetSeconds || 300;
if (expiry && (expiry > this._now() + offset)) {
isExpired = false;
}
//add by me-end
if (!this._user) {
this.warn('User login is required');
callback('User login is required', null, 'login required');
return;
}
//add by me-begin
else if (isExpired) {
this.login(null,"prompt=login");
}
//add by me-end
// refresh attept with iframe
//Already renewing for this resource, callback when we get the token.
if (this._activeRenewals[resource]) {
//Active renewals contains the state for each renewal.
this.registerCallback(this._activeRenewals[resource], resource, callback);
}
else {
if (resource === this.config.clientId) {
// App uses idtoken to send to api endpoints
// Default resource is tracked as clientid to store this token
this.verbose('renewing idtoken');
this._renewIdToken(callback);
} else {
this._renewToken(resource, callback);
}
}
};
//add the extraParameters to force user sign-in
AuthenticationContext.prototype.login = function (loginStartPage,extraParameters) {
// Token is not present and user needs to login
if (this._loginInProgress) {
this.info("Login in progress");
return;
}
var expectedState = this._guid();
this.config.state = expectedState;
this._idTokenNonce = this._guid();
this.verbose('Expected state: ' + expectedState + ' startPage:' + window.location.href);
this._saveItem(this.CONSTANTS.STORAGE.LOGIN_REQUEST, loginStartPage || window.location.href);
this._saveItem(this.CONSTANTS.STORAGE.LOGIN_ERROR, '');
this._saveItem(this.CONSTANTS.STORAGE.STATE_LOGIN, expectedState);
this._saveItem(this.CONSTANTS.STORAGE.NONCE_IDTOKEN, this._idTokenNonce);
this._saveItem(this.CONSTANTS.STORAGE.ERROR, '');
this._saveItem(this.CONSTANTS.STORAGE.ERROR_DESCRIPTION, '');
var urlNavigate = this._getNavigateUrl('id_token', null) + '&nonce=' + encodeURIComponent(this._idTokenNonce);
urlNavigate = urlNavigate + "&"+extraParameters;//add by me
this._loginInProgress = true;
if (this.config.displayCall) {
// User defined way of handling the navigation
this.config.displayCall(urlNavigate);
}
else if (this.popUp) {
this._loginPopup(urlNavigate);
}
else {
this.promptUser(urlNavigate);
}
};
If you have any idea or feedback about this library you can rise a new issue from here.