-
Notifications
You must be signed in to change notification settings - Fork 0
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
Swagger OAUTH Proxy to avoid CORS issue #1
base: main
Are you sure you want to change the base?
Changes from 8 commits
f11ae21
62e75cb
5740d23
6d2cc64
46058a7
bf0b276
4a8377a
cae5ba8
3069467
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
/* | ||
* | ||
* * | ||
* * * | ||
* * * * | ||
* * * * * Copyright 2019-2024 the original author or authors. | ||
* * * * * | ||
* * * * * Licensed under the Apache License, Version 2.0 (the "License"); | ||
* * * * * you may not use this file except in compliance with the License. | ||
* * * * * You may obtain a copy of the License at | ||
* * * * * | ||
* * * * * https://www.apache.org/licenses/LICENSE-2.0 | ||
* * * * * | ||
* * * * * Unless required by applicable law or agreed to in writing, software | ||
* * * * * distributed under the License is distributed on an "AS IS" BASIS, | ||
* * * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* * * * * See the License for the specific language governing permissions and | ||
* * * * * limitations under the License. | ||
* * * * | ||
* * * | ||
* * | ||
* | ||
*/ | ||
package org.springdoc.webmvc.ui.oauth.proxy; | ||
|
||
import io.swagger.v3.oas.annotations.Hidden; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.http.HttpHeaders; | ||
import org.springframework.http.MediaType; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.util.LinkedMultiValueMap; | ||
import org.springframework.util.MultiValueMap; | ||
import org.springframework.web.bind.annotation.PostMapping; | ||
import org.springframework.web.bind.annotation.RequestHeader; | ||
import org.springframework.web.bind.annotation.RestController; | ||
import org.springframework.web.client.RestClient; | ||
import java.nio.charset.StandardCharsets; | ||
import java.util.Base64; | ||
|
||
@RestController | ||
public class SwaggerOauthProxyController { | ||
|
||
private final String GRANT_TYPE_KEY = "grant_type"; | ||
private final String CLIENT_CREDENTIALS_KEY = "client_credentials"; | ||
private final String CLIENT_ID_KEY = "client_id"; | ||
private final String CLIENT_SECRET_KEY = "client_secret"; | ||
private final String AUTHENTICATION_SCHEME_BASIC = "Basic"; | ||
|
||
private final RestClient restClient = RestClient.builder().build(); | ||
private final SwaggerOauthProxyProperties swaggerOauthProxyProperties; | ||
|
||
@Autowired | ||
public SwaggerOauthProxyController(SwaggerOauthProxyProperties swaggerOauthProxyProperties) { | ||
this.swaggerOauthProxyProperties = swaggerOauthProxyProperties; | ||
} | ||
|
||
@Hidden | ||
@PostMapping(path = "${springdoc.swagger-ui.oauth-proxy.path}", produces = MediaType.APPLICATION_JSON_VALUE) | ||
public String redirectSwaggerOauth(@RequestHeader(HttpHeaders.AUTHORIZATION) String authorizationHeader) { | ||
|
||
if (authorizationHeader != null && authorizationHeader.startsWith(AUTHENTICATION_SCHEME_BASIC)) { | ||
|
||
String base64Credentials = authorizationHeader.substring(AUTHENTICATION_SCHEME_BASIC.length()).trim(); | ||
String credentials = new String(Base64.getDecoder().decode(base64Credentials), StandardCharsets.UTF_8); | ||
String[] clientDetails = credentials.split(":", 2); | ||
|
||
MultiValueMap<String, String> body = new LinkedMultiValueMap<>(); | ||
body.add(GRANT_TYPE_KEY, CLIENT_CREDENTIALS_KEY); | ||
body.add(CLIENT_ID_KEY, clientDetails[0]); | ||
body.add(CLIENT_SECRET_KEY, clientDetails[1]); | ||
|
||
ResponseEntity<String> response = restClient.post() | ||
.uri(swaggerOauthProxyProperties.getOauthTokenUri()) | ||
.body(body) | ||
.retrieve() | ||
.toEntity(String.class); | ||
|
||
return response.getBody(); | ||
|
||
} else { | ||
throw new RuntimeException("Authorization header missing or not using Basic Auth"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there maybe from Spring a more specific Exception we can use to indicate 401 Unauthorized because Basic Auth header is missing? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have changed it to a ResponseStatusException |
||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
/* | ||
* | ||
* * | ||
* * * | ||
* * * * | ||
* * * * * Copyright 2019-2024 the original author or authors. | ||
* * * * * | ||
* * * * * Licensed under the Apache License, Version 2.0 (the "License"); | ||
* * * * * you may not use this file except in compliance with the License. | ||
* * * * * You may obtain a copy of the License at | ||
* * * * * | ||
* * * * * https://www.apache.org/licenses/LICENSE-2.0 | ||
* * * * * | ||
* * * * * Unless required by applicable law or agreed to in writing, software | ||
* * * * * distributed under the License is distributed on an "AS IS" BASIS, | ||
* * * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* * * * * See the License for the specific language governing permissions and | ||
* * * * * limitations under the License. | ||
* * * * | ||
* * * | ||
* * | ||
* | ||
*/ | ||
package org.springdoc.webmvc.ui.oauth.proxy; | ||
|
||
import org.springdoc.core.utils.Constants; | ||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | ||
import org.springframework.boot.context.properties.ConfigurationProperties; | ||
import org.springframework.validation.Errors; | ||
import org.springframework.validation.ValidationUtils; | ||
import org.springframework.validation.Validator; | ||
import org.springframework.validation.annotation.Validated; | ||
import java.io.Serializable; | ||
import java.net.URI; | ||
import static org.springdoc.core.utils.Constants.SPRINGDOC_ENABLED; | ||
|
||
@Validated | ||
@ConfigurationProperties(prefix = Constants.SPRINGDOC_SWAGGER_UI_OAUTH_PROXY_PREFIX) | ||
public class SwaggerOauthProxyProperties implements Validator { | ||
|
||
private boolean enabled; | ||
private URI path; | ||
private URI oauthTokenUri; | ||
|
||
public boolean isEnabled() { | ||
return enabled; | ||
} | ||
|
||
public void setEnabled(boolean enabled) { | ||
this.enabled = enabled; | ||
} | ||
|
||
public URI getPath() { | ||
return path; | ||
} | ||
|
||
public void setPath(URI path) { | ||
this.path = path; | ||
} | ||
|
||
public URI getOauthTokenUri() { | ||
return oauthTokenUri; | ||
} | ||
|
||
public void setOauthTokenUri(URI oauthTokenUri) { | ||
this.oauthTokenUri = oauthTokenUri; | ||
} | ||
|
||
@Override | ||
public boolean supports(Class<?> clazz) { | ||
return SwaggerOauthProxyProperties.class.isAssignableFrom(clazz); | ||
} | ||
|
||
@Override | ||
public void validate(Object target, Errors errors) { | ||
if (enabled) { | ||
if (path == null) { | ||
errors.rejectValue("path", "field.required"); | ||
} | ||
if (oauthTokenUri == null) { | ||
errors.rejectValue("oauthTokenUri", "field.required"); | ||
} | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because this is the value in a Map entry, let's maybe call it CLIENT_CREDENTIALS_VALUE.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah yes, much better, thanks!