Angular-auth-oidc-client: Cannot read property 'replace' of undefined

Created on 20 Nov 2017  路  26Comments  路  Source: damienbod/angular-auth-oidc-client

I've implemented the AutoLogin feature and after successful login in the following code I receive an error in browser console which says "Cannot read property replace of undefined"

Is it a bug or I'm missing something in code?

OidcSecurityValidation.prototype.urlBase64Decode = function (str) {
        var /** @type {?} */ output = str.replace('-', '+').replace('_', '/');
        switch (output.length % 4) {
            case 0:
                break;
            case 2:
                output += '==';
                break;
            case 3:
                output += '=';
                break;
            default:
                throw 'Illegal base64url string!';
        }
        return window.atob(output);
    };
    return OidcSecurityValidation;
}());
bug investigate

Most helpful comment

I'm using version 1.17 (planning on upgrading to the latest soon) and I get the same issue. I've turned on the logging and I seem to get that error whenever the nonce fails validation. I've checked the server logs and I can confirm it returns the correct nonce. What I think is happening from reading the logs is that the client sends an authorize request to the server with a nonce which it stores on the local storage but then I think it sends another authorize request (before the other one gets its response) this time with a different nonce which overwrites the one on the local storage. The response for the first authorize request then returns but now that nonce is invalid because it is expecting the nonce of the second authorize request.

I'm not sure if this is exactly whats happening but it is what I can piece together from the logs.

Hope this helps.

All 26 comments

I'm getting the same.

This happens when the server returns no response. Can you check what the server returns?

Greetings Damien

Sometimes, I have the same issue.
If I succeed to catch the server returns, I'll post it.

I'm almost certain this is not an issue with what the server returns. All indications point to some kind of bug/race condition in the auth client.

@Hainesy @IsTypiNOVanS Would love to get more information on this. Looks like I need to improvement the log messages for this no matter what the issue is.

Greetings Damien

@damienbod I'm pretty sure this is not a server issue. I've just seen the error occur again on the client, and the server logs all look fine. I haven't been able to trace beyond this.

I can confirm this issue with the current development branch. also had this problem occasionally since about ~4 months now, but didn't found the time to investigate it further.

Do you wait until the oidc module is setup before handling the authorization callback?

I will add more logging around the authorize callback, to try to add more light on this issue. It could also be, that the app trys to use the access token before it is ready

I'm using version 1.17 (planning on upgrading to the latest soon) and I get the same issue. I've turned on the logging and I seem to get that error whenever the nonce fails validation. I've checked the server logs and I can confirm it returns the correct nonce. What I think is happening from reading the logs is that the client sends an authorize request to the server with a nonce which it stores on the local storage but then I think it sends another authorize request (before the other one gets its response) this time with a different nonce which overwrites the one on the local storage. The response for the first authorize request then returns but now that nonce is invalid because it is expecting the nonce of the second authorize request.

I'm not sure if this is exactly whats happening but it is what I can piece together from the logs.

Hope this helps.

I have this problem occasionally when the token refresh happens and another request is done while the token refresh has not yet completed:

TypeError: str is undefined

Stacktrace:

OidcSecurityValidation.urlBase64Decode
OidcSecurityValidation.getPayloadFromToken
OidcSecurityValidation.isTokenExpired
OidcSecurityService.runTokenValidation

This is what happens on the local storage:
(I've replaced the token with the string TOKEN for clarity)

The storage_silent_renew_running key has been changed from "running" to "".
The userData key has been changed from {"http://schemas.microsoft.com/ws/2008/06/identity/claims/role":["Administrator"],"sub":"eeda22f3-5cbb-4298-9ade-11364f7f9cfc"} to "".
The authorizationResult key has been changed from {"id_token":"TOKEN","token_type":"Bearer","expires_in":"3600","scope":"openid%20email%20roles","state":"15120513517320.8940695338043914","session_state":"TOKEN"} to "".
The session_state key has been changed from "SESSION STATE" to "".
The _isAuthorized key has been changed from true to false.
The authorizationData key has been changed from "TOKEN" to "".
The authorizationDataIdToken key has been changed from "TOKEN" to "".
The redirectUrl key has been changed from  to /.
The authNonce key has been changed from "N0.429912304392828461513690042961" to "N0.2803600694724051513690043469".

Then I make a GET request to the identity server:

GET https://localhost:5001/connect/authorize?client_id=portal&redirect_uri=https://localhost:4430&response_type=id_token token&scope=openid email roles portal&nonce=N0.2803600694724051513690043469&state=15120513517320.8940695338043914

Which returns with a 302: Found status code. At this time another XHR request from the applications starts to make a database request, which fails because the Bearer token is not set at this point.

Also, from this point on, the token refresh does not work anymore besides the CheckSession ist constantly polling in the background, because the ID Token is not set in the local storage anymore.

(Version used: Commit e537d7403d6bf544c70913dea0af0fce79b72af4)

I was able to reproduce the issue in commit 73771cdff70a15e4a9410761783c480479c99db9.

I get the same. Unfortunately, I don't have anything more to add that's not already been said - but thought it worth mentioning anyway.

Is the checksession always active when this happens?

I'll check next time I'm working on it (which will unfortunately be after Christmas I'm afraid). I only work for this client one day a week, so can't check at the mo (and it doesn't happen all the time either).

In my scenario. First I get warning:

authorizedCallback incorrect nonce

Then userData got empty then I got an warning in my callback component that

oAuthCallback#id_token=eyJhbGciOiJSUzI1NiIsIm(...jwt hash) WebSocket connection to 'ws://localhost:10572/sockjs-node/267/ibkzhlgt/websocket' failed: WebSocket is closed before the connection is established.

Then I get the
ERROR TypeError: Cannot read property 'replace' of undefined error

Don't know websocket warning is related to this issue but, I just get this errors in first silent renew attempt. Then silent renew works without error or warning in my console.

I think I know the reason for this bug now, when the silent renew starts, until it is finished, the tokens cannot be used. If the using app, requires a token in this time, it is not available and so the parse error. Will try to reproduce this. To fix this, I plan to separate the silent renew completely from the first authorize.

Thanks to everyone for the feedback. Will fix this as soon as possible.

Greetings Damien

Please note, that it also happen when user is signed out of STS, and the Angular app does silent_renew.

IsAuthorized: id_token isTokenExpired, start silent renew if active
17:03:00.668 angular-auth-oidc-client.es5.js:695 BEGIN refresh session Authorize
17:03:00.671 angular-auth-oidc-client.es5.js:695 RefreshSession created. adding myautostate: 15145533147370.5138137561867451
17:03:00.676 angular-auth-oidc-client.es5.js:695 startRenew for URL:https://localhost:44360/connect/authorize?client_id=angular&redirect_uri=http%3A%2F%2Flocalhost%3A4200%2Fcallback&response_type=id_token%20token&scope=openid%20email%20profile%20api1&nonce=N0.170677065630323141514563380671&state=15145533147370.5138137561867451&prompt=none
17:03:02.097 angular-auth-oidc-client.es5.js:695 onUserDataChanged: last = undefined, new = 
17:03:02.257 angular-auth-oidc-client.es5.js:695 onUserDataChanged: last = , new = [object Object]
17:03:02.261 angular-auth-oidc-client.es5.js:695 STS server: https://localhost:44360
17:03:02.262 angular-auth-oidc-client.es5.js:695 {issuer: "https://localhost:44360", jwks_uri: "https://localhost:44360/.well-known/openid-configuration/jwks", authorization_endpoint: "https://localhost:44360/connect/authorize", token_endpoint: "https://localhost:44360/connect/token", userinfo_endpoint: "https://localhost:44360/connect/userinfo",聽鈥
17:03:02.265 angular-auth-oidc-client.es5.js:695 AuthWellKnownEndpoints already defined
17:03:02.278 core.es5.js:2925 Angular is running in the development mode. Call enableProdMode() to enable the production mode.
17:03:02.370 angular-auth-oidc-client.es5.js:695 onWellKnownEndpointsLoaded
17:03:02.671 angular-auth-oidc-client.es5.js:695 BEGIN authorizedCallback, no auth data
17:03:02.676 angular-auth-oidc-client.es5.js:695 {error: "login_required", state: "15145533147370.5138137561867451"}
17:03:02.681 angular-auth-oidc-client.es5.js:695 authorizedCallback created, begin token validation
17:03:02.684 angular-auth-oidc-client.es5.js:695 jwks_uri: https://localhost:44360/.well-known/openid-configuration/jwks
17:03:02.711 angular-auth-oidc-client.es5.js:695 authorizedCallback, token(s) validation failed, resetting
17:03:02.715 angular-auth-oidc-client.es5.js:695 onUserDataChanged: last = [object Object], new = 
17:03:02.718 angular-auth-oidc-client.es5.js:695 onUserDataChanged: Logout detected.
17:03:02.805 unauthorized.component.ts:38 --isAuthorizedSubscription isAuthorized: false
17:03:03.683 core.es5.js:1020 ERROR TypeError: Cannot read property 'replace' of undefined
    at OidcSecurityValidation.urlBase64Decode (angular-auth-oidc-client.es5.js:1079)
    at OidcSecurityValidation.getPayloadFromToken (angular-auth-oidc-client.es5.js:907)
    at OidcSecurityValidation.isTokenExpired (angular-auth-oidc-client.es5.js:724)
    at SafeSubscriber.eval [as _next] (angular-auth-oidc-client.es5.js:2151)
    at SafeSubscriber.__tryOrUnsub (Subscriber.js:240)
    at SafeSubscriber.next (Subscriber.js:187)
    at Subscriber._next (Subscriber.js:128)
    at Subscriber.next (Subscriber.js:92)
    at TakeSubscriber._next (take.js:83)
    at TakeSubscriber.Subscriber.next (Subscriber.js:92)

fixed in version 3.0.10

The error is now removed, and if the server session has ended, the silent renew logs warnings to the console in the browser

Can some of you test, thanks Damien

UPDATE: I've just created: #145

--

Hi,
Seems to work now.

However, now I see other bug. After token refresh success, I see that it calls route 'openIDImplicitFlowConfiguration.post_login_route' and in my console I see that in fact that Component (Component that I have under post_login_route) constructor is called, and ngOnInit is called.

Is it bug that refresh token "calls" that route, creating it - but in fact not redirecting there? (my page and browser url stays normal)? (NOTE: at the very first time, when redirecting from STS all works fine - problem is only when refreshing token).

@mpszczolinski

I'll fix this and add an event when the silent renew fails. Then the hosting application can react

when the silent renew fails

... but isn't it more generic problem? I believe that root problem is that 'silent renew' happens in another context, but oidc-client-js 'silent renew' is happenining in the same context.

having above in mind - is the renewed access_token visible in original application context?
is the fact that silent_renew happens in new context is BUG or FEATURE?

(just asking those question to help you implement best solution.)

best regards,
Maciej

Thanks Maciej

The silent renew runs in an iframe, so the logs you see never happen in your main application. I have removed all the routing now from the silent renew. Only the event is sent. The tokens get updated for the main application, makes sense?

Greetings Damien

Here's how to use the event which is now called always in an authorise callback, feedback? @robisim74 @FabianGosebrink

this.oidcSecurityService.onAuthorizationResult.subscribe(
    (authorizationResult: AuthorizationResult) => {
        this.onAuthorizationResultComplete(authorizationResult);
    });

ngOnDestroy(): void {
    this.oidcSecurityService.onAuthorizationResult.unsubscribe();
}

private onAuthorizationResultComplete(authorizationResult: AuthorizationResult) {
    console.log('Auth result received:' + authorizationResult);
    if (authorizationResult === AuthorizationResult.unauthorized) {
        if (window.parent) {
            // sent from the child iframe, for example the silent renew
            window.parent.location.href = '/unauthorized';
        } else {
            // sent from the main window
            window.location.href = '/unauthorized';
        }
    }
}
Was this page helpful?
0 / 5 - 0 ratings

Related issues

Expelz picture Expelz  路  4Comments

mikeandersun picture mikeandersun  路  4Comments

toddtsic picture toddtsic  路  4Comments

yelhouti picture yelhouti  路  4Comments

xaviergxf picture xaviergxf  路  3Comments