ionic-appauth
ionic-appauth copied to clipboard
Refresh Token Failed
Hi @wi3land, I'm using ionic-appauth to manage a login flow using an Azure AD server. The login flow works properly but when i try to refresh token (when the first token is expired) I get an error with the advanced-http plugin. The error is: "{"action":"Refesh Failed","error":"advanced-http: "data" option is configured to support only following data types: Array, Object"}"
I have tried to use both version of plugin (0.5.0 and 0.6.0) but the result is the same.
Can you help me? Thank You in advance
I just upgraded to the latest 0.6.9 and got the same error.
On my Token interceptor:
@Injectable()
export class TokenInterceptor implements HttpInterceptor {
constructor(private authService: AuthService) {}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return from(this.handleAccess(req, next));
}
private async handleAccess(request: HttpRequest<any>, next: HttpHandler): Promise<HttpEvent<any>> {
try {
let changedRequest = request;
const token = await this.authService.getValidToken();
const headerSettings: { [name: string]: string | string[]; } = {};
for (const key of request.headers.keys()) {
headerSettings[key] = request.headers.getAll(key);
}
if (token) {
headerSettings.Authorization = 'Bearer ' + token.accessToken;
}
const newHeader = new HttpHeaders(headerSettings);
changedRequest = request.clone({
headers: newHeader
});
return next.handle(changedRequest).toPromise();
} catch (err) {
console.error(err);
return next.handle(request).toPromise();
}
}
}
I'm getting:
token.interceptor.ts:32 Error: Unable To Obtain Valid Token
at AuthService.<anonymous> (auth-service.js:420)
at step (auth-service.js:33)
at Object.next (auth-service.js:14)
at fulfilled (auth-service.js:5)
at ZoneDelegate.invoke (zone-evergreen.js:364)
at Object.onInvoke (core.js:41667)
at ZoneDelegate.invoke (zone-evergreen.js:363)
at Zone.run (zone-evergreen.js:123)
at zone-evergreen.js:857
at ZoneDelegate.invokeTask (zone-evergreen.js:399)
{action: "Refesh Failed", error: "Http failure response for http://localhost:13810/connect/token: 400 Bad Request"}
action: "Refesh Failed"
error: "Http failure response for http://localhost:13810/connect/token: 400 Bad Request"
__proto__: Object
Note that ONLY has happened to me when I actually had to refresh the token, I mean, it was not valid since I was AFK for a little while. If I just refresh the page the auth service refreshes the token therefore it seems to be there is something being sent to the identity server that is indeed sending back the 400 bad request.
I got the logs from IS4:
2020/09/01 12:56:53.076|DEBUG|CORS request made for path: /.well-known/openid-configuration from origin: http://localhost:8100 |IdentityServer4.Hosting.CorsPolicyProvider|path=/.well-known/openid-configuration, origin=http://localhost:8100
2020/09/01 12:56:53.098|DEBUG|Client list checked and origin: http://localhost:8100 is allowed |IdentityServer4.Services.InMemoryCorsPolicyService|
2020/09/01 12:56:53.160|DEBUG|CorsPolicyService allowed origin: http://localhost:8100 |IdentityServer4.Hosting.CorsPolicyProvider|origin=http://localhost:8100
2020/09/01 12:56:53.264|DEBUG|Request path /.well-known/openid-configuration matched to endpoint type Discovery |IdentityServer4.Hosting.EndpointRouter|path=/.well-known/openid-configuration, endpoint=Discovery
2020/09/01 12:56:53.279|DEBUG|Endpoint enabled: Discovery, successfully created handler: IdentityServer4.Endpoints.DiscoveryEndpoint |IdentityServer4.Hosting.EndpointRouter|endpoint=Discovery, endpointHandler=IdentityServer4.Endpoints.DiscoveryEndpoint
2020/09/01 12:56:53.295|INFO|Invoking IdentityServer endpoint: IdentityServer4.Endpoints.DiscoveryEndpoint for /.well-known/openid-configuration |IdentityServer4.Hosting.IdentityServerMiddleware|endpointType=IdentityServer4.Endpoints.DiscoveryEndpoint, url=/.well-known/openid-configuration
2020/09/01 12:56:53.309|TRACE|Processing discovery request. |IdentityServer4.Endpoints.DiscoveryEndpoint|
2020/09/01 12:56:53.324|DEBUG|Start discovery request |IdentityServer4.Endpoints.DiscoveryEndpoint|
2020/09/01 12:56:53.338|TRACE|Calling into discovery response generator: IdentityServer4.ResponseHandling.DiscoveryResponseGenerator |IdentityServer4.Endpoints.DiscoveryEndpoint|type=IdentityServer4.ResponseHandling.DiscoveryResponseGenerator
2020/09/01 12:56:53.338|TRACE|Invoking result: IdentityServer4.Endpoints.Results.DiscoveryDocumentResult |IdentityServer4.Hosting.IdentityServerMiddleware|type=IdentityServer4.Endpoints.Results.DiscoveryDocumentResult
2020/09/01 12:56:53.522|DEBUG|CORS request made for path: /connect/token from origin: http://localhost:8100 |IdentityServer4.Hosting.CorsPolicyProvider|path=/connect/token, origin=http://localhost:8100
2020/09/01 12:56:53.536|DEBUG|Client list checked and origin: http://localhost:8100 is allowed |IdentityServer4.Services.InMemoryCorsPolicyService|
2020/09/01 12:56:53.548|DEBUG|CorsPolicyService allowed origin: http://localhost:8100 |IdentityServer4.Hosting.CorsPolicyProvider|origin=http://localhost:8100
2020/09/01 12:56:53.610|DEBUG|Request path /connect/token matched to endpoint type Token |IdentityServer4.Hosting.EndpointRouter|path=/connect/token, endpoint=Token
2020/09/01 12:56:53.625|DEBUG|Endpoint enabled: Token, successfully created handler: IdentityServer4.Endpoints.TokenEndpoint |IdentityServer4.Hosting.EndpointRouter|endpoint=Token, endpointHandler=IdentityServer4.Endpoints.TokenEndpoint
2020/09/01 12:56:53.644|INFO|Invoking IdentityServer endpoint: IdentityServer4.Endpoints.TokenEndpoint for /connect/token |IdentityServer4.Hosting.IdentityServerMiddleware|endpointType=IdentityServer4.Endpoints.TokenEndpoint, url=/connect/token
2020/09/01 12:56:53.665|TRACE|Processing token request. |IdentityServer4.Endpoints.TokenEndpoint|
2020/09/01 12:56:53.679|DEBUG|Start token request. |IdentityServer4.Endpoints.TokenEndpoint|
2020/09/01 12:56:53.695|DEBUG|Start client validation |IdentityServer4.Validation.ClientSecretValidator|
2020/09/01 12:56:53.709|DEBUG|Start parsing Basic Authentication secret |IdentityServer4.Validation.BasicAuthenticationSecretParser|
2020/09/01 12:56:53.723|DEBUG|Start parsing for secret in post body |IdentityServer4.Validation.PostBodySecretParser|
2020/09/01 12:56:53.744|DEBUG|client id without secret found |IdentityServer4.Validation.PostBodySecretParser|
2020/09/01 12:56:53.772|DEBUG|Parser found secret: PostBodySecretParser |IdentityServer4.Validation.SecretParser|type=PostBodySecretParser
2020/09/01 12:56:53.782|DEBUG|Secret id found:###
|IdentityServer4.Validation.SecretParser|id=###
2020/09/01 12:56:53.795|TRACE|Calling into client configuration validator: IdentityServer4.Validation.DefaultClientConfigurationValidator |IdentityServer4.Stores.ValidatingClientStore|validatorType=IdentityServer4.Validation.DefaultClientConfigurationValidator
2020/09/01 12:56:53.811|DEBUG|client configuration validation for client ##### succeeded. |IdentityServer4.Stores.ValidatingClientStore|clientId=###
2020/09/01 12:56:53.825|DEBUG|Public Client - skipping secret validation success |IdentityServer4.Validation.ClientSecretValidator|
2020/09/01 12:56:53.842|DEBUG|Client validation success |IdentityServer4.Validation.ClientSecretValidator|
2020/09/01 12:56:53.859|TRACE|Calling into token request validator: IdentityServer4.Validation.TokenRequestValidator |IdentityServer4.Endpoints.TokenEndpoint|type=IdentityServer4.Validation.TokenRequestValidator
2020/09/01 12:56:53.892|DEBUG|Start token request validation |IdentityServer4.Validation.TokenRequestValidator|
2020/09/01 12:56:53.907|DEBUG|Start validation of refresh token request |IdentityServer4.Validation.TokenRequestValidator|
2020/09/01 12:56:53.922|TRACE|Start refresh token validation |IdentityServer4.Validation.TokenValidator|
2020/09/01 12:56:53.937|DEBUG|refresh_token grant with value: 4ES9bNOtRUNaR1mHrX_yB6wKy7cKMTRRFK7DJ7OmFMU not found in store. |IdentityServer4.Stores.DefaultRefreshTokenStore|grantType=refresh_token, key=4ES9bNOtRUNaR1mHrX_yB6wKy7cKMTRRFK7DJ7OmFMU
2020/09/01 12:56:53.937|WARN|Invalid refresh token |IdentityServer4.Validation.TokenValidator|
2020/09/01 12:56:53.961|WARN|Refresh token validation failed. aborting
I managed to capture some of the flow to try give more details. There is a success Token request:
And there are more Token requests afterwards which will fail (the 400 Bad Request) subsequentially:
I've checked out the code and read a lot in the docs and different articles and to me it seems that either the TokenResponseJson serialization/deserialization produces a different string somehow or the token https://github.com/wi3land/ionic-appauth/blob/master/src/auth-service.ts#L267 is invalid for some reason because maybe there is a race condition between that and the session update within the session observer https://github.com/wi3land/ionic-appauth/blob/2edd1c47f78a1fb30a9e2197fcad74b544d0ed0f/src/auth-observer.ts#L47 . That could produce an invalid Authorization header value and that could cause the failure?
Hi @wi3land , another update on this one. I cloned your repo (https://github.com/wi3land/ionic-appauth-capacitor-demo) which was very useful as I did set up the lifetime of the token to 10 seconds to try to reproduce the problem. If you configure your identity server 4 with the following client settings:
SlidingRefreshTokenLifetime = 10,
RefreshTokenExpiration = TokenExpiration.Sliding,
RefreshTokenUsage = TokenUsage.OneTimeOnly
Then when I click on the REFRESH TOKEN button after 15-20 seconds and I can reproduce the issue:
I workaround I found to get this working was that hooking up to the RefreshFailed
event and firing off a signIn
request to get a new token back which has worked but I would have thought that if the refresh token fails perhaps there should be another mechanism to actually "refresh the token" without manually having to hook up to the RefreshFailed event. Does that make sense?
HI @wi3land ,
Did you have a chance to take a look at this one? It's a quite weird one and yet is not resolved by the look of it. Would be nice to get to the bottom of it - happy to help investigating if needed
Thanks, Carlos.
Just for anyone's interest I was able to "fix" this error by setting a random positive number such as "10" to the await this.authService.getValidToken(10)
code in the token interceptor. Not sure why, but that refreshed the token correctly...
Hi, I know it's a quite old post, but I found myself against the same issue, AAD rejects refresh requests if authorization header is present, I just added a condition in my token interceptor to not include authorization header if the current URL contains the Authorization server host and problem solved.
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { if (req.url.indexOf(environment.auth_config.server_host) > -1) { // some IDP does not accept auth token, so just skip it return next.handle(req); } else { ...
Hope this helps.
I am encountering the same error as @CarlosTorrecillas but unfortuntely the solution does not help. The first refresh works fine, but after debugging I found out that the isValid already evaulates to false after the first refresh for unkwown reasons. Did you manage to fix it or to find a working workaround?
I have an idea of what might be happening. Seems as your using a 1 time refresh token. I think the refresh token isn't getting changed over to the new refresh token on successful token refresh.
Sadly there isn't anything that can be done about this from this library. I am using the base token handler from AppAuthJS. This handles all token requests and return a new token back to this library.
Please raise the issue with them to see if they can help.
Hi @Siedlerchr , Am also facing the issue with refreshToken , when I try to call refreshToken am getting action:Refresh Failed. I tried the solution suggested by @CarlosTorrecillas but does it work. Could please let me know if you have any inputs on the failure . go the method this.authService.refreshToken();
Thanks & Regards Sandeep N
@sandeepwip I actually solved it by overwriting this method and implementing an addtional Subject as kind of Semaphore:
export class CustomAuthService extends AuthService {
constructor(browser?: Browser, storage?: StorageBackend, requestor?: Requestor) {
super(browser, storage, requestor);
this.endSessionHandler = new CustomEndSessionHandler(browser); //just a custom end session handler because my server component does have a different endpoint for deleting.
this.addActionListener(action => {
if (action.action === AuthActions.RefreshSuccess) {
this.refreshTokenInProgress = false;
this.tokenSubject.next(this.session.token);
}
});
}
tokenSubject = new BehaviorSubject<TokenResponse>(null);
refreshTokenInProgress = false;
/**
* Get a valid token as Observable
*/
public getTokenAlwaysValid(): Observable<TokenResponse> {
this.checkTokenAndRenew();
return this.tokenSubject.pipe(filter((token) => token !== null), take(1));
}
public async checkTokenAndRenew(buffer: number = AUTH_EXPIRY_BUFFER) {
if (this.session.token) {
if (!this.session.token.isValid(buffer)) {
if (!this.refreshTokenInProgress) {
this.tokenSubject.next(null);
this.refreshTokenInProgress = true;
await this.refreshToken();
}
} else {
this.tokenSubject.next(this.session.token);
}
}
}
In your auth.module.ts you have to add this class as provider
Hi @Siedlerchr ,
Thanks for the reply, but we are unable to resolve the issue as per your suggestions,
My only concern is i have downloaded the demo cordova & ran the app and added my configuration , i am able to login successfully and get user details.
But when press on refreshToken i get message as Refresh {action:Refresh Failed}
Please suggest to get new token on press of RefreshToken.
Thanks
sandeep N