Тихое обновление не работает с OIDC-клиентом в Angular 5

У меня проблема с автоматическим обновлением с помощью oidc-client. Вход работает нормально, и я могу получить токен. Однако тихое обновление не срабатывает, ничего не происходит. Когда я подписываюсь на методы, которые проверяют истечение срока действия токена (методы в subscribeevents в authservice.ts ниже), эти методы никогда не срабатывают - и метод isLoggedIn() всегда возвращает true, даже если срок действия токена истек.

Вот мой код:

import { Component, OnInit } from '@angular/core';
import { UserManager } from 'oidc-client';
import { getClientSettings } from '../openIdConnectConfig';
import { AuthService } from '../services/auth.service';
    
@Component({
    selector: 'app-silentrefresh',
    templateUrl: './silentrefresh.component.html',
    styleUrls: ['./silentrefresh.component.css']
})
export class SilentRefreshComponent implements OnInit {
    constructor(private _authService:AuthService) {
    }
    
    ngOnInit() {
        this._authService.refreshCallBack();
    }
}

Тогда мой authservice:

import { UserManagerSettings, UserManager, User } from 'oidc-client';
import { Injectable } from '@angular/core';
import { getClientSettings } from '../openIdConnectConfig';
    
@Injectable()
export class AuthService {

    private _manager = new UserManager(getClientSettings());
    private _user: User = null;

    constructor() {
        this._manager.getUser().then(user => {
            this._user = user;
        });

        this._manager.events.addUserLoaded(user => {
            this._user = user;
        });

        this.subscribeevents();
    }

    public isLoggedIn(): boolean {
        return this._user != null && !this._user.expired;
    }

    public getClaims(): any {
        return this._user.profile;
    }

    public subscribeevents(): void {
        this._manager.events.addSilentRenewError(() => {
            console.log("error SilentRenew");
        });

        this._manager.events.addAccessTokenExpiring(() => {
            console.log("access token expiring");
        });

        this._manager.events.addAccessTokenExpired(() => {
            console.log("access token expired");
        });
    }

    public refreshCallBack(): void {
        console.log("start refresh callback");
        this._manager.signinSilentCallback()
            .then(data => { console.log("suucess callback") })
            .catch(err => {
                console.log("err callback");
            });
        console.log("end refresh callback");
    }

    getUser(): any {
        return this._user;
    }

    getName(): any {
        return this._user.profile.name;
    }

    getAuthorizationHeaderValue(): string {
        return `${this._user.token_type} ${this._user.access_token}`;
    }

    startAuthentication(): Promise<void> {
        return this._manager.signinRedirect();
    }

    completeAuthentication(): Promise<void> {
        return this._manager.signinRedirectCallback().then(user => {
            this._user = user;
        });
    }
}

И мой конфиг:

import { UserManagerSettings } from "oidc-client";

export function getClientSettings(): UserManagerSettings {
    return {
        authority: 'https://login.microsoftonline.com/136544d9-038e-4646-afff-10accb370679',
        client_id: '257b6c36-1168-4aac-be93-6f2cd81cec43',
        redirect_uri: 'http://localhost:4200/auth-callback',
        //redirect_uri: 'https://demoazureadconnectangular5.azurewebsites.net/auth-callback',
        post_logout_redirect_uri: 'http://localhost:4200/',
        //post_logout_redirect_uri: 'https://demoazureadconnectangular5.azurewebsites.net/',
        response_type: "id_token",
        scope: "openid profile",
        filterProtocolClaims: true,
        loadUserInfo: true,
        automaticSilentRenew: true,
        silent_redirect_uri: 'http://localhost:4200/assets/silentrefresh',
        metadata: {
            issuer: "https://sts.windows.net/136544d9-038e-4646-afff-10accb370679/",
            authorization_endpoint: "https://login.microsoftonline.com/136544d9-038e-4646-afff-10accb370679/oauth2/authorize",
            token_endpoint: "https://login.microsoftonline.com/136544d9-038e-4646-afff-10accb370679/oauth2/token",
            //jwks_uri: "https://login.microsoftonline.com/common/discovery/keys",
            jwks_uri: "http://localhost:4200/assets/keys.json",
            //jwks_uri: "https://demoazureadconnectangular5.azurewebsites.net/assets/keys.json",
            //jwks_uri: "http://localhost:50586/api/keys",
            signingKeys: [{ "ApiAccessKey": "NgixniZ0S1JHxo7GPEZYa38OBTxSA98AqJKDX5XqsJ8=" }]
        }
    };
}

Я также пробовал использовать такую ​​статическую страницу:

<head>
    <title></title>
</head>

<body>
    <script src="oidc-client.min.js"></script>
    <script>
        var usermanager = UserManager().signinSilentCallback()
            .catch((err) => {
                console.log(err);
            });
    </script>
</body>

Он никогда не стрелял

Для проверки я изменил срок действия токена ID на 10 минут. Я использую Azure AD Connect (Open Id Connect в Azure), и Microsoft говорит, что он не полностью совместим со стандартом Open ID Connect ... Так что я не знаю, на моей стороне или на стороне Azure.

Кто-нибудь может помочь мне решить эту проблему?


person Anthony Giretti    schedule 14.02.2018    source источник
comment
Когда-нибудь находили решение?   -  person jarodsmk    schedule 04.04.2018
comment
Вы нашли решение?   -  person mperk    schedule 13.03.2019


Ответы (4)


Проблема в том, что вы запрашиваете не access_token из лазурного AD, а только id_token. Вы должны установить response_type в id_token token, чтобы получить оба токена. Для этого изменения также потребуется еще несколько параметров. Например, ресурс для вашего бэкэнда. Я ответил на аналогичный вопрос здесь. Я также использую Angular 5 и клиент oidc. https://stackoverflow.com/a/50922730/8081009 И я отвечу вам здесь также перед https://github.com/IdentityModel/oidc-client-js/issues/504#issuecomment-400056662 Вот что вам нужно настроить, чтобы автоматическое обновление работало.

includeIdTokenInSilentRenew: true
extraQueryParams: {
      resource: '10282f28-36ed-4257-a853-1bf404996b18'
}
response_type: 'id_token token',
scope: 'openid'
loadUserInfo: false,
automaticSilentRenew: true,
silent_redirect_uri: `${window.location.origin}/silent-refresh.html`,
metadataUrl: 'https://login.microsoftonline.com/YOUR_TENANT_NAME.onmicrosoft.com/.well-known/openid-configuration',
signingKeys: [
    add here keys from link below
]

https://login.microsoftonline.com/common/discovery/keys

Я также использую другую статическую страницу для конечной точки обратного вызова с тихим обновлением, потому что таким образом пользователь ничего не заметит. Эта страница минимально возможна, поэтому oidc не будет загружать все приложение angular в скрытый iframe, который он использует для тихого обновления. Поэтому рекомендуется, чтобы это было более эффективным.

<head>
  <title></title>
</head>

<body>
  <script src="assets/oidc-client.min.js"></script>
  <script>
    new Oidc.UserManager().signinSilentCallback()
      .catch((err) => {
        console.log(err);
      });
  </script>
</body>
person Janne Harju    schedule 26.06.2018
comment
Я абсолютно согласен. Но в феврале access_token не поддерживался, но Open Is Connect в Azure AD. Запрос access_token выдавал ошибку. Теперь это поддерживается в неявном потоке. Вот и все. - person Anthony Giretti; 02.07.2018

  1. Проверьте, есть ли у вас правильный URI перенаправления в базе данных.

  2. Убедитесь, что вы добавили в свой angular.json файл следующее:

    ...
    "assets": [
        "src/assets",
        "silent-refresh.html",
        "oidc-client.min.js"
        .....
    ],
    ...
    
  3. Отметьте silent-refresh.html:

    <script src="oidc-client.min.js"></script><script>
        var mgr = new Oidc.UserManager();
        mgr.signinSilentCallback().catch(error => {
            console.error(error);
        });
    </script>
    
  4. Убедитесь, что вы не создаете более одного экземпляра UserManager

  5. Вы можете поступить любым способом - automaticSilentRenew: false или automaticSilentRenew: true,. Я рекомендую использовать automaticSilentRenew: false и запускать событие по истечении срока действия.

    https://github.com/IdentityModel/oidc-client-js/wiki

    public renewToken() {
        return this.manager.signinSilent().then(u => {
            this.user = u;
        }).catch(er => {
            console.log(er);
        });
    }
    
    this.manager.events.addAccessTokenExpiring(x => {
        console.log('Acess token expiring event');
        this.renewToken().then(u => {
            console.log('Acess token expiring event renew success');
        });
    });
    

Если вышеуказанные действия не работают, проверьте код сервера идентификации.


Код идентификационного сервера

Запуск

services.AddIdentityServer(options =>
    {
        options.Authentication.CookieLifetime = TimeSpan.FromDays(30);
        options.Authentication.CookieSlidingExpiration = true;
    });
services.AddAuthentication(x => x.DefaultAuthenticateScheme = IdentityServerConstants.DefaultCookieAuthenticationScheme);

Выйти

await HttpContext.SignOutAsync(IdentityServer4.IdentityServerConstants.DefaultCookieAuthenticationScheme);

Благодаря https://github.com/IdentityModel/oidc-client-js/issues/911#issuecomment-617724445

person Ishika Jain    schedule 12.07.2020

Самая простая причина может заключаться в том, что не добавляется URL-адрес тихого обновления в качестве URL-адреса перенаправления в конфигурации сервера идентификации.

В базе данных вашего сервера идентификации URL-адреса перенаправления для ваших клиентов должны быть такими:

redirectUrls: [http://localhost:4200/assets/silentrefresh, http://localhost:4200/auth-callback]
person Lasal Sethiya    schedule 13.07.2018

Я использовал другой подход для запуска silentRenw вместо использования

 automaticSilentRenew: true,

Я решил явно вызвать signInSilent () ;. Причина, по которой я столкнулся с некоторыми проблемами, поскольку automaticSilentRenew: true, не работает должным образом.

Я инициализировал событие и метод в своем конструкторе класса UserAuth, который реализует мой интерфейс.

  constructor(private oidcSettings: CoreApi.Interfaces.Authentication.OpenIDConnectionSettings)
    {
     this.userManager.events.addAccessTokenExpiring(() =>
             {
                this.userManager.signinSilent({scope: oidcSettings.scope, response_type: oidcSettings.response_type})
                        .then((user: Oidc.User) =>
                        {
                            this.handleUser(user);
                        })
                        .catch((error: Error) =>
                        {
                           //Work around to handle to Iframe window timeout errors on browsers
                            this.userManager.getUser()
                                .then((user: Oidc.User) =>
                                {
                                    this.handleUser(user);
                                });
                        });
                });
    }

Где в качестве handleUser просто проверяется авторизованный пользователь.

Поэтому, если вы инициализируете процесс signInSilent в своем конструкторе, а затем вызываете signInSilent complete, то есть обратный вызов, он может работать.

person Sohan    schedule 15.02.2018
comment
Спасибо, но addaccessTokenExpiring никогда не срабатывает .... причина в том, что я получаю IdToken, а не AccessToken из Azure Ad - person Anthony Giretti; 15.02.2018
comment
О, я вижу, в этом случае беззвучный вход будет работать только с access_token. Я надеюсь, что поток подключения openID является либо неявным, либо потоком авторизации? - person Sohan; 15.02.2018
comment
Неявный (спа) или базовый (только бэкэнд). Я не могу обновить токен идентификатора с помощью OIDC-Client, поскольку последний в Azure AD не предоставляет access_token. С другими провайдерами я не знаю, потому что никогда не пробовал - person Anthony Giretti; 15.02.2018
comment
Другие поставщики IDP, такие как GLUU или OpenAM, поддерживают правильный поток openID и возвращают access_token и id_token. - person Sohan; 16.02.2018
comment
У меня нет выбора использовать лазурный AD - person Anthony Giretti; 16.02.2018