Skip to content

Commit d559e6b

Browse files
BB2-3353 Initialize oAuth and allow logout flow in openapi (#1247)
* BB2-3353 Initialize oAuth and allow logout * Add swagger documentation and fix request interceptor
1 parent e87d0dd commit d559e6b

File tree

2 files changed

+93
-9
lines changed

2 files changed

+93
-9
lines changed

apps/docs/templates/openapi.html

Lines changed: 81 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,14 @@
4040

4141
<body>
4242
<div id="swagger-ui"></div>
43+
<button id="custom-logout-button">Logout</button>
4344

4445
<script src="{% static 'swagger-ui-4.15.5-dist/swagger-ui-bundle.js' %}" charset="UTF-8"> </script>
4546
<script src="{% static 'swagger-ui-4.15.5-dist/swagger-ui-standalone-preset.js' %}" charset="UTF-8"> </script>
4647
<script>
4748
window.onload = function() {
48-
const ui = SwaggerUIBundle({
49+
let ui;
50+
ui = SwaggerUIBundle({
4951
urls: [
5052
{
5153
url: "{% static 'openapi.yaml' %}",
@@ -60,13 +62,20 @@
6062
],
6163
layout: "StandaloneLayout",
6264
requestInterceptor: (request) => {
63-
const accessToken = localStorage.getItem('access_token');
64-
if (accessToken) {
65-
request.headers['Authorization'] = `Bearer ${accessToken}`;
65+
if (ui) {
66+
const serverUrl = ui.specSelectors.specJson().toJS().servers[0].url;
67+
const accessToken = localStorage.getItem('access_token');
68+
if (accessToken && request.url.startsWith(serverUrl)) {
69+
request.headers['Authorization'] = `Bearer ${accessToken}`;
70+
}
6671
}
67-
72+
6873
return request;
6974
},
75+
onComplete: function() {
76+
initializeOauthData()
77+
addLogoutButton();
78+
},
7079

7180
oauth2RedirectUrl: window.location.origin + "/docs/oauth2-redirect"
7281
});
@@ -83,14 +92,14 @@
8392
// Save OAuth2 details to localStorage
8493
const serializedData = serializeObject(window.swaggerUIRedirectOauth2)
8594
/**
86-
* swaggerUIRedirectOauth2 contains information regarding oauth2 clientId, clientSecret etc.
95+
* swaggerUIRedirectOauth2 contains information regarding oauth2 clientId, clientSecret etc.
8796
* This will be required by static/oauth2-redirect.html and will be deserialized there
8897
*/
8998
localStorage.setItem('swaggerUIRedirectOauth2', serializedData);
9099
window.dispatchEvent(new Event('storage'));
91-
}
100+
}
92101

93-
function serializeObject(obj) {
102+
function serializeObject(obj) {
94103
const serializedObj = { ...obj };
95104
for (const key in serializedObj) {
96105
if (typeof serializedObj[key] === 'function') {
@@ -100,13 +109,77 @@
100109
return JSON.stringify(serializedObj);
101110
}
102111

112+
function deserializeObject(serialized) {
113+
const parsedObj = JSON.parse(serialized);
114+
for (const key in parsedObj) {
115+
if (typeof parsedObj[key] === 'string' && parsedObj[key].startsWith('function')) {
116+
parsedObj[key] = new Function('return ' + parsedObj[key])();
117+
}
118+
}
119+
return parsedObj;
120+
}
121+
103122
// Event listener for authorize button click
104123
document.addEventListener('click', (event) => {
105124
if (event.target && event.target.matches('button.btn.modal-btn.auth.authorize.button')) {
106125
waitForSwaggerUIRedirectOauth2();
107126
}
108127
});
109128

129+
function initializeOauthData() {
130+
const oauth2Data = localStorage.getItem('swaggerUIRedirectOauth2');
131+
if (!oauth2Data) {
132+
return;
133+
}
134+
const oauth2 = deserializeObject(oauth2Data)
135+
const tokenUrl = oauth2.auth.schema.tokenUrl;
136+
const clientId = oauth2.auth.clientId
137+
const clientSecret = oauth2.auth.clientSecret;
138+
const redirectUri = oauth2.redirectUrl
139+
const scopes = oauth2.auth.scopes.join(" ");
140+
ui.initOAuth({
141+
clientId: clientId,
142+
clientSecret: clientSecret,
143+
scopeSeparator: " ",
144+
scopes: scopes
145+
})
146+
}
147+
148+
function addLogoutButton() {
149+
const accessToken = localStorage.getItem('access_token');
150+
if (!accessToken) {
151+
return;
152+
}
153+
154+
const authWrapper = document.querySelector('.auth-wrapper');
155+
if (!authWrapper) {
156+
return;
157+
}
158+
159+
const customButtonsWrapper = document.createElement('div');
160+
customButtonsWrapper.id = 'custom-buttons';
161+
customButtonsWrapper.style.display = 'flex';
162+
customButtonsWrapper.style.alignItems = 'center';
163+
customButtonsWrapper.style.marginLeft = '20px';
164+
165+
const classNames = ['btn', 'modal-btn', 'auth', 'button'];
166+
const logoutButton = document.createElement('button');
167+
168+
logoutButton.id = 'logoutButton';
169+
logoutButton.classList.add(...classNames);
170+
logoutButton.textContent = 'Logout';
171+
logoutButton.style.marginLeft = '10px';
172+
173+
logoutButton.addEventListener('click', function() {
174+
localStorage.removeItem('access_token');
175+
localStorage.removeItem('swaggerUIRedirectOauth2');
176+
logoutButton.style.display = 'none';
177+
});
178+
179+
customButtonsWrapper.appendChild(logoutButton);
180+
authWrapper.parentElement.insertBefore(customButtonsWrapper, authWrapper.nextSibling);
181+
}
182+
110183
};
111184

112185
</script>

static/openapi.yaml

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,18 @@ openapi: 3.0.3
22
info:
33
title: BlueButton 2.0
44
version: 1.0.0
5-
description: "In order to try out the API you'll first need to get an access token and use it in the Authorize -> bearerAuth setting. To get one you can use the [test client](https://sandbox.bluebutton.cms.gov/testclient/), [postman](https://bluebutton.cms.gov/developers/#try-the-api), or your own local setup. The metadata endpoint is unprotected so you can use it without an access code. The metadata results will tell you what query parameters are available for the different fhir endpoints."
5+
description: |
6+
To try out the API using Swagger UI, follow these steps:
7+
8+
1. **Create an Application**: Ensure you have an application set up on [CMS Sandbox](https://sandbox.bluebutton.cms.gov).
9+
2. **Update Application Settings**: Go to your application settings and add [https://sandbox.bluebutton.cms.gov/docs/oauth2-redirect](https://sandbox.bluebutton.cms.gov/docs/oauth2-redirect) to the "Callback URLs / Redirect URIs" field under "App Details - Required Information". Save the updated application settings.
10+
3. **Store Client Credentials**: Note down and securely store your client_id and client_secret from your CMS Sandbox application. These credentials will be used later.
11+
4. **Authorize in Swagger UI**: Open this Swagger UI and click on the "Authorize" button.
12+
5. **Authorize Access**: Within the prompt, input your client_id and client_secret, choose the scopes you want to use in your testing, and click "Authorize" to open a new tab where you will grant consent to your application from a sample beneficiary. In order to log in, you can use a sample beneficiary using the login instructions from [this link](https://sandbox.bluebutton.cms.gov/testclient/authorize-link-v2).
13+
6. **Complete Consent**: Follow the prompts to complete the consent form.
14+
7. **Return to Swagger UI**: After consenting, you will be redirected back to this Swagger UI.
15+
8. **Explore APIs**: You can now start exploring and testing the APIs by providing the relevant parameters.
16+
617
paths:
718
/.well-known/openid-configuration:
819
get:

0 commit comments

Comments
 (0)