-
Notifications
You must be signed in to change notification settings - Fork 161
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Restore connection between client and ServiceWorker after failures #1397
Comments
hi @wermanoid thank you for your issue. I'am not sure to understand what you did but I'am interested to understand well. |
There is no special app code example here, because in code it looks like regular oidc client usage. I was using oauth-mock-server to simulate some errors here is implementation: // index.ts
import { OAuth2Server } from 'oauth2-mock-server';
import { createStateServer } from './state-manager';
// docs https://github.com/axa-group/oauth2-mock-server/blob/master/README.md
const run = async () => {
const { state, responseByResultHandlers } = createStateServer(); // http://localhost:8081
// unfortunately OAuth2Server does not give access to express app instance, so I had to find a way, that allow to modify
// auth responses in runtime, without instant server restarts. This can be achieved by simple get requests from browser
// based on defined query params
const server = new OAuth2Server();
// Generate a new RSA key and add it to the keystore
await server.issuer.keys.generate('RS256');
// Start the server
await server.start(8082, 'localhost');
console.log('Issuer URL:', server.issuer.url); // -> http://localhost:8082
server.service.on('beforeUserinfo', userInfoResponse => {
userInfoResponse.body = {
...userInfoResponse.body,
email: '[email protected]',
name: 'JD',
};
});
server.service.on('beforeTokenSigning', (token, req) => {
const timestamp = Math.floor(Date.now() / 1000);
token.payload.exp = timestamp + state.expireTimeSec;
token.payload.scope =
'offline_access groups tenant_id openid profile email';
token.payload.scp = token.payload.scope.split(' ');
req.body.scope = 'offline_access groups tenant_id openid profile email';
});
server.service.on('beforeResponse', response => {
console.log('Response of type:', state.currentResponse);
// modify token response based on predefined state, so we could modify desired responses in runtime, when needed
responseByResultHandlers[state.currentResponse](response);
});
};
run(); and code to manage responses in real-time: import express from 'express';
let requestCounter = 0;
const state = {
expireTimeSec: 180,
currentResponse: 'e3', // every third token request will fail with random 400 or 401 error
};
const responseByResultHandlers = {
e3: (response: any) => {
if (requestCounter < 2) {
responseByResultHandlers[200](response);
requestCounter += 1;
} else {
responseByResultHandlers[Math.random() > 0.5 ? 401 : 400](response);
requestCounter = 0;
}
},
// always success
200: (response: any) => {
response.body.expires_in = state.expireTimeSec;
response.body.scope =
'offline_access groups tenant_id openid profile email';
response.body.issued_at = response.body.issuedAt;
},
// always invalid_grant
400: (response: any) => {
response.body = {
error: 'invalid_grant',
};
response.statusCode = 400;
},
// always unauthorized
401: (response: any) => {
response.body.error = 'Unauthorized';
response.body.error_message = 'User is unauthorized';
response.statusCode = 401;
},
} as const;
export const createStateServer = (
port = 8081
): {
state: typeof state;
responseByResultHandlers: typeof responseByResultHandlers;
} => {
const app = express();
app.get('/config', (req, res) => {
state.expireTimeSec = Number(req.query?.expireTime) || 180;
state.currentResponse = String(req.query?.responseType) || 'e3';
console.log('New state:', state);
return res.json({ ok: true });
});
app.listen(port, () => {
console.log(
`Auth mock configuration api runnin at http://localhost:${port}`
);
});
return { state, responseByResultHandlers };
}; App to test - anything with oidc client that expects to update tokens on regular basis via SW. I've used So when you have few tabs (at least 3) opened and they all are connected to same SW everything is ok, until auth server returns first failed request. Hope that helps. |
Interesting extension related to error handling in general, a bit off-topic, but: |
Thank you @wermanoid it is a very nice feedback. I will look at what i can do about it ! |
Issue and Steps to Reproduce
You need to run some auth mock server to control responses.
Versions
"@axa-fr/oidc-client": "7.22.8",
Expected
If I open one more tab and login correctly, old tabs should update with correct tokens
Actual
2 tabs are broken, 1 is sometimes functional
Additional Details
I got a workaround, like next:
Tab that hangs with outdate tokens:
token_timer
event in oidctoken_timer
reset some internal timer (2s timeout)token_timer
is not raised any moreoidc.renewTokensAsync()
calls, like:This case happens when oidc emits error events, like:
I have no ideas why there is such difference in
renewTokensAsync
andtryKeepExistingSessionAsync
. Just found manually, that these functions helps to recover client in apps, when auth was renewed in some new tab.Probably it would be a good feature to have smth like that in OIDC client by default? So OIDC client in apps could recover automatically, in case if SW was recreated/updated by working or new tab.
The text was updated successfully, but these errors were encountered: