Skip to content

Commit ad66524

Browse files
authored
Fix the Swagger UI not working when MAS is mounted on a prefix (#4515)
2 parents b9a40ef + 514cd8e commit ad66524

File tree

3 files changed

+74
-33
lines changed

3 files changed

+74
-33
lines changed

crates/handlers/src/admin/mod.rs

Lines changed: 57 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -81,35 +81,61 @@ fn finish(t: TransformOpenApi) -> TransformOpenApi {
8181
),
8282
..Default::default()
8383
})
84+
.security_scheme("oauth2", oauth_security_scheme(None))
8485
.security_scheme(
85-
"oauth2",
86-
SecurityScheme::OAuth2 {
87-
flows: OAuth2Flows {
88-
client_credentials: Some(OAuth2Flow::ClientCredentials {
89-
refresh_url: Some(OAuth2TokenEndpoint::PATH.to_owned()),
90-
token_url: OAuth2TokenEndpoint::PATH.to_owned(),
91-
scopes: IndexMap::from([(
92-
"urn:mas:admin".to_owned(),
93-
"Grant access to the admin API".to_owned(),
94-
)]),
95-
}),
96-
authorization_code: Some(OAuth2Flow::AuthorizationCode {
97-
authorization_url: OAuth2AuthorizationEndpoint::PATH.to_owned(),
98-
refresh_url: Some(OAuth2TokenEndpoint::PATH.to_owned()),
99-
token_url: OAuth2TokenEndpoint::PATH.to_owned(),
100-
scopes: IndexMap::from([(
101-
"urn:mas:admin".to_owned(),
102-
"Grant access to the admin API".to_owned(),
103-
)]),
104-
}),
105-
implicit: None,
106-
password: None,
107-
},
108-
description: None,
86+
"token",
87+
SecurityScheme::Http {
88+
scheme: "bearer".to_owned(),
89+
bearer_format: None,
90+
description: Some("An access token with access to the admin API".to_owned()),
10991
extensions: IndexMap::default(),
11092
},
11193
)
11294
.security_requirement_scopes("oauth2", ["urn:mas:admin"])
95+
.security_requirement_scopes("bearer", ["urn:mas:admin"])
96+
}
97+
98+
fn oauth_security_scheme(url_builder: Option<&UrlBuilder>) -> SecurityScheme {
99+
let (authorization_url, token_url) = if let Some(url_builder) = url_builder {
100+
(
101+
url_builder.oauth_authorization_endpoint().to_string(),
102+
url_builder.oauth_token_endpoint().to_string(),
103+
)
104+
} else {
105+
// This is a dirty fix for Swagger UI: when it joins the URLs with the
106+
// base URL, if the path starts with a slash, it will go to the root of
107+
// the domain instead of the API root.
108+
// It works if we make it explicitly relative
109+
(
110+
format!(".{}", OAuth2AuthorizationEndpoint::PATH),
111+
format!(".{}", OAuth2TokenEndpoint::PATH),
112+
)
113+
};
114+
115+
let scopes = IndexMap::from([(
116+
"urn:mas:admin".to_owned(),
117+
"Grant access to the admin API".to_owned(),
118+
)]);
119+
120+
SecurityScheme::OAuth2 {
121+
flows: OAuth2Flows {
122+
client_credentials: Some(OAuth2Flow::ClientCredentials {
123+
refresh_url: Some(token_url.clone()),
124+
token_url: token_url.clone(),
125+
scopes: scopes.clone(),
126+
}),
127+
authorization_code: Some(OAuth2Flow::AuthorizationCode {
128+
authorization_url,
129+
refresh_url: Some(token_url.clone()),
130+
token_url,
131+
scopes,
132+
}),
133+
implicit: None,
134+
password: None,
135+
},
136+
description: None,
137+
extensions: IndexMap::default(),
138+
}
113139
}
114140

115141
pub fn router<S>() -> (OpenApi, Router<S>)
@@ -146,10 +172,13 @@ where
146172
move |State(url_builder): State<UrlBuilder>| {
147173
// Let's set the servers to the HTTP base URL
148174
let mut api = api.clone();
149-
api.servers = vec![Server {
150-
url: url_builder.http_base().to_string(),
151-
..Server::default()
152-
}];
175+
176+
let _ = TransformOpenApi::new(&mut api)
177+
.server(Server {
178+
url: url_builder.http_base().to_string(),
179+
..Server::default()
180+
})
181+
.security_scheme("oauth2", oauth_security_scheme(Some(&url_builder)));
153182

154183
std::future::ready(Json(api))
155184
}

crates/handlers/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,8 @@ where
249249
ACCEPT_LANGUAGE,
250250
CONTENT_LANGUAGE,
251251
CONTENT_TYPE,
252+
// Swagger will send this header, so we have to allow it to avoid CORS errors
253+
HeaderName::from_static("x-requested-with"),
252254
])
253255
.max_age(Duration::from_secs(60 * 60)),
254256
)

docs/api/spec.json

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2527,21 +2527,26 @@
25272527
"type": "oauth2",
25282528
"flows": {
25292529
"clientCredentials": {
2530-
"refreshUrl": "/oauth2/token",
2531-
"tokenUrl": "/oauth2/token",
2530+
"refreshUrl": "./oauth2/token",
2531+
"tokenUrl": "./oauth2/token",
25322532
"scopes": {
25332533
"urn:mas:admin": "Grant access to the admin API"
25342534
}
25352535
},
25362536
"authorizationCode": {
2537-
"authorizationUrl": "/authorize",
2538-
"tokenUrl": "/oauth2/token",
2539-
"refreshUrl": "/oauth2/token",
2537+
"authorizationUrl": "./authorize",
2538+
"tokenUrl": "./oauth2/token",
2539+
"refreshUrl": "./oauth2/token",
25402540
"scopes": {
25412541
"urn:mas:admin": "Grant access to the admin API"
25422542
}
25432543
}
25442544
}
2545+
},
2546+
"token": {
2547+
"type": "http",
2548+
"scheme": "bearer",
2549+
"description": "An access token with access to the admin API"
25452550
}
25462551
},
25472552
"schemas": {
@@ -3725,6 +3730,11 @@
37253730
"oauth2": [
37263731
"urn:mas:admin"
37273732
]
3733+
},
3734+
{
3735+
"bearer": [
3736+
"urn:mas:admin"
3737+
]
37283738
}
37293739
],
37303740
"tags": [

0 commit comments

Comments
 (0)