Skip to content

Commit

Permalink
AAE-23742 Create custom-swagger.js to handle refresh token logic (#1477)
Browse files Browse the repository at this point in the history
  • Loading branch information
mavotto authored Jul 15, 2024
1 parent f1619dc commit 1fdba7a
Show file tree
Hide file tree
Showing 4 changed files with 158 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,12 @@ public JwtAccessTokenProvider jwtAccessTokenProvider(Function<Jwt, JwtAdapter> j
@ConditionalOnMissingBean
public OAuthFlow swaggerOAuthFlow(
@Value("${keycloak.auth-server-url}") String authServer,
@Value("${keycloak.realm}") String realm
@Value("${keycloak.realm}") String realm,
@Value("${spring.security.oauth2.client.provider.keycloak.token-uri}") String tokenUrl
) {
return new OAuthFlow()
.authorizationUrl(authServer + "/realms/" + realm + "/protocol/openid-connect/auth")
.tokenUrl(tokenUrl)
.scopes(new Scopes());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,5 @@ spring.security.oauth2.client.registration.keycloak.scope=openid

spring.security.oauth2.client.provider.keycloak.authorization-uri=${keycloak.auth-server-url}/realms/${keycloak.realm}/protocol/openid-connect/authorize
spring.security.oauth2.client.provider.keycloak.token-uri=${keycloak.auth-server-url}/realms/${keycloak.realm}/protocol/openid-connect/token

springdoc.swagger-ui.oauth.usePkceWithAuthorizationCodeGrant=true
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
function buildFormData(data) {
let formArr = [];

for (let name in data) {
let val = data[name];
if (val !== undefined && val !== '') {
formArr.push(
[name, '=', encodeURIComponent(val).replace(/%20/g, '+')].join(''),
);
}
}
return formArr.join('&');
}

function getAuth() {
return window.ui.authSelectors.authorized().toJS()?.oauth;
}

function tryRefreshOauth2Token() {
const auth = getAuth();
if (!auth) {
console.log(`Swagger is not authorized. Can't refresh token.`);
return;
}

const {schema, clientId, token} = auth;
const errors = [];

if (schema == null) {
errors.push('Invalid auth: missing schema');
}
if (schema?.tokenUrl == null) {
errors.push('Invalid auth schema: missing tokenUrl');
}
if (clientId == null) {
errors.push('Invalid auth: missing clientId');
}

if (token == null) {
errors.push('Invalid auth: missing token');
}
if (token?.refresh_token == null) {
errors.push('Invalid auth: missing refresh token');
}
if (token?.scope == null) {
errors.push('Invalid auth: missing scope');
}
if (errors.length) {
console.log("Can't refresh token due to the following issues:");
errors.forEach(console.log);
return;
}

const form = {
grant_type: 'refresh_token',
refresh_token: token.refresh_token,
client_id: clientId,
scope: token.scope,
};

console.log(`Refreshing token...`);
window.ui.authActions.authorizeRequest({
body: buildFormData(form),
name,
url: schema.tokenUrl,
auth,
});
}

function startClock() {
function tick() {
const remainingRefreshTime = window.tokenRefreshTime - Date.now();

if (remainingRefreshTime < 0) {
clearInterval(window.tokenClockInterval);
tryRefreshOauth2Token();
}
}

if (window.tokenClockInterval) clearInterval(window.tokenClockInterval);

window.tokenClockInterval = setInterval(tick, 500);
tick();
}

let patchTries = 10;

function patchRefreshHook() {
if (!window?.ui?.authActions?.authorizeOauth2) {
if (patchTries) {
patchTries--;
setTimeout(patchRefreshHook, 1000);
console.log(
'Missing patch target function "window.ui.authActions.authorizeOauth2", retrying in 1s...',
);
return;
}
console.log(
'Cannot patch OAuth token refresh hook. Missing patch target function "window.ui.authActions.authorizeOauth2"',
);
return;
}

console.log('Patching OAuth token refresh hook...');
const origAuthorizeOauth2 = window.ui.authActions.authorizeOauth2;

window.ui.authActions.authorizeOauth2 = (payload) => {
// If the token can expire, schedule a token refresh and update the timer
if (payload.token.expires_in) {
const tokenRefreshTimeout = payload.token.expires_in * 750;
console.log(
`Refreshable token detected. Scheduling token refresh in ${(
tokenRefreshTimeout /
1000 /
60
).toFixed(1)}min (expires in ${(payload.token.expires_in / 60).toFixed(
1,
)}min)...`,
);
window.tokenRefreshTime = Date.now() + payload.token.expires_in * 750;

// Start the clock
startClock();
}

return origAuthorizeOauth2(payload);
};

startClock();
}

patchRefreshHook();
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Swagger UI</title>
<link rel="stylesheet" type="text/css" href="./swagger-ui.css"/>
<link rel="stylesheet" type="text/css" href="index.css"/>
<link rel="icon" type="image/png" href="./favicon-32x32.png" sizes="32x32"/>
<link rel="icon" type="image/png" href="./favicon-16x16.png" sizes="16x16"/>
</head>

<body>
<div id="swagger-ui"></div>

<script src="./swagger-ui-bundle.js" charset="UTF-8"></script>
<script src="./swagger-ui-standalone-preset.js" charset="UTF-8"></script>
<script src="./swagger-initializer.js" charset="UTF-8"> </script>
<script src="./custom-swagger.js" charset="UTF-8"> </script>

</body>
</html>

0 comments on commit 1fdba7a

Please sign in to comment.