Silent refresh not working with OIDC-client in Angular 5
Asked Answered
H

6

11

I have an issue with the silent refresh with oidc-client. The signin works fine and I'm able to acquire a token. However, the silent refresh doesn't fire, nothing happens. When I subscribe to methods that check token expiry (methods in subscribeevents in authservice.ts below), these methods never fire - and the method isLoggedIn() always return true even if the token has expired.

Here is my code :

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

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

And my config:

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=" }]
        }
    };
}

I also tried to use a static page like this:

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

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

It's never fired neither

In order to test, I've changed the ID token expiry to 10 min. I use Azure AD Connect (Open Id Connect in Azure) and Microsoft says it's not fully compatible with Open ID Connect standard... So I don't know if it's on my side or Azure side.

Somebody can help me to solve this?

Hospitality answered 14/2, 2018 at 2:6 Comment(4)
Ever find a solution?Clamp
Did you find a solution?Kilt
Obligatory yearly question: ever found a solution?Window
sorry skipped 1 year, did you ever find a solution ?Custody
J
4
  1. Check if you have correct redirect URI in the database.

  2. Check you have added the following in your angular.json file:

    ...
    "assets": [
        "src/assets",
        "silent-refresh.html",
        "oidc-client.min.js"
        .....
    ],
    ...
    
  3. Check 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. Check you do not create more than one instance of UserManager

  5. You can do either way - automaticSilentRenew: false, or automaticSilentRenew: true, I will recommend using automaticSilentRenew: false and trigger an event on expiring.

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

If the above things do not work then check the identity server code.


Identity server code

Startup

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

Logout

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

Thanks to https://github.com/IdentityModel/oidc-client-js/issues/911#issuecomment-617724445

Jimmie answered 12/7, 2020 at 8:53 Comment(0)
Z
3

The problem is that you are not asking access_token from azure AD, only id_token. You must set response_type to id_token token to get both tokens. This change will need also few more parameters. For example resource for your backend. I have answered similar question here. I'm using also Angular 5 and oidc client. https://mcmap.net/q/1019243/-signoutredirect-of-oidc-client-js-against-auth0-returns-no-end-session-endpoint And I answer you here also before https://github.com/IdentityModel/oidc-client-js/issues/504#issuecomment-400056662 Here is what you need to set to get silent renew working.

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

I'm also using different static page for callback endpoint with silent renew because this way user won't notice a thing. This page is minimum possible so oidc won't load whole angular application to hidden iframe what it is using for silent renew. So this is recommended to be more efficient.

<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>
Zulmazulu answered 26/6, 2018 at 7:16 Comment(1)
I totally agree. But in February access_token was not supported but Open Is Connect on azure AD. Asking access_token was throwing an error. Now it’s supported in implicit flow. That’s it.Hospitality
M
1

Simplest reason can be, not adding silent renew url as a redirect url in identity server configuration.

In your identity server database, redirect urls for your clients should be like this

redirectUrls: [http://localhost:4200/assets/silentrefresh, http://localhost:4200/auth-callback]
Meadow answered 13/7, 2018 at 11:28 Comment(0)
I
1

Sharing information just in case if it helps anyone.

I also faced this same issue but in my case, silent userManager.signinSilent was not working in Chrome incognito but when I test it on normal chrome window then it was working as expected.

Root cause: This issue happens due to Cookies settings:

Error in chrome console :

A cookie associated with a cross-site resource at https://example.com/ was set without the SameSite attribute. It has been blocked, as Chrome now only delivers cookies with cross-site requests if they are set with SameSite=None and Secure. 

Solution: You will need allow thirds party cookies in Chrome setting OR you can also add particular site to allow cookies:

Chrome setting: At the top right, click More More and then Settings -> Click Privacy and security -> Cookies and other site data.

Ignominy answered 3/3, 2023 at 12:46 Comment(0)
M
0

I have used some different approach to initate the silentRenw, instead of using

 automaticSilentRenew: true,

I decided to explicitly call the signInSilent();. Reason for doing the i was facing some issues as automaticSilentRenew: true, was not working as expected.

I initialized the event and method in my UserAuth class constructor that implements my interface

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

Where as handleUser is just check for logged in user.

So if you initialize the signInSilent process in your constructor and then call signInSilent complete i.e. callback it may work.

Mephitis answered 15/2, 2018 at 6:18 Comment(5)
Thank you, but addaccessTokenExpiring never fire.... the reason is : I get an IdToken and not an AccessToken from Azure AdHospitality
Ohh i see, in that case signIn silent will work only with access_token. I hope the openID connect flow is either implicit or authorization flow?Mephitis
Implicit (spa) or basic (backend only). I can’t renew ID token with OIDC-Client because Azure AD , this last one doesn’t provide access_token. With other providers I don’t know because I have never triedHospitality
Other IDP providers like GLUU or OpenAM does support proper openID flow and returns the access_token and id_token.Mephitis
I have no choice to use azure ADHospitality
V
0
Not sure what oidc-client.js version you are using, this should never have worked.

```
new Oidc.UserManager().signinSilentCallback()
                  .catch((err) => {
                      console.log(err);
                  });
``

Usermanager is in **Oidc** object. 
Vestry answered 2/6, 2018 at 15:13 Comment(1)
Not sure what you mean. That code looks fine to me. Working in our app.Bladdernut

© 2022 - 2024 — McMap. All rights reserved.