ADAL.js: как заставить пользователя повторно ввести пароль после истечения срока действия токена

У меня есть одностраничное приложение, использующее adal-angular (https://github.com/AzureAD/azure-activedirectory-library-for-js) для безопасного доступа. Всякий раз, когда срок действия токена истекает (через 1 час), я хочу перенаправить пользователя на экран входа в AD, чтобы повторно ввести свой пароль. Я изменил adal-angular.js для вызова _adal.login() всякий раз, когда срок действия токена истек, однако это, по-видимому, автоматически повторно аутентифицирует пользователя и направляет его непосредственно на URI перенаправления с обновленным токеном. Я не уверен, что именно кешируется, что позволяет автоматически обновлять.

Как я могу заставить пользователя повторно вводить свой пароль каждый раз, когда срок действия токена истекает?


person gooram    schedule 30.06.2017    source источник
comment
Насколько я знаю, через час истекает срок действия токена доступа, но не токена обновления (который по умолчанию истекает через 2 недели), и насколько я знаю, библиотека ADAL может использовать его автоматически. Это означает, что вы можете обновить токен доступа с помощью токена обновления, не заставляя пользователя повторно вводить пароль.   -  person Nikolai    schedule 30.06.2017
comment
Да, это я тоже понимаю, но как заставить пользователя снова войти в систему, а не делать это молча, используя токен обновления?   -  person gooram    schedule 30.06.2017


Ответы (1)


Насколько мне известно, adal.js получает токен, используя неявный поток в iframe, когда срок действия токена истек. Он не поддерживает принудительный повторный вход пользователя.

Чтобы реализовать эту функцию, вы можете изменить исходный код. Вот пример кода, изменяющий методы acquireToken и login в файле adal.js для справки:

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);
    }
};

Если у вас есть какие-либо идеи или отзывы об этой библиотеке, вы можете поднять новую проблему с здесь.

person Fei Xue - MSFT    schedule 03.07.2017
comment
Приглашение = логин было тем, что я искал! Мне не удалось найти никакой документации по этим параметрам URL, спасибо Fei Xue. - person gooram; 04.07.2017
comment
@gooram Подробные параметры см. по адресу по этой ссылке. - person Fei Xue - MSFT; 04.07.2017