Skip to content

Commit 9bff618

Browse files
committed
fix spring authorization server response. Fixes springdoc#2123
1 parent d647fc7 commit 9bff618

File tree

5 files changed

+202
-707
lines changed

5 files changed

+202
-707
lines changed

springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocSecurityOAuth2Customizer.java

+57-19
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@
22

33
import java.lang.reflect.Field;
44

5-
import com.nimbusds.jose.jwk.JWKSet;
65
import io.swagger.v3.oas.annotations.enums.ParameterIn;
76
import io.swagger.v3.oas.models.OpenAPI;
87
import io.swagger.v3.oas.models.Operation;
98
import io.swagger.v3.oas.models.PathItem;
109
import io.swagger.v3.oas.models.headers.Header;
10+
import io.swagger.v3.oas.models.media.ArraySchema;
1111
import io.swagger.v3.oas.models.media.Content;
12+
import io.swagger.v3.oas.models.media.MapSchema;
1213
import io.swagger.v3.oas.models.media.MediaType;
1314
import io.swagger.v3.oas.models.media.ObjectSchema;
1415
import io.swagger.v3.oas.models.media.Schema;
@@ -21,6 +22,7 @@
2122
import org.slf4j.Logger;
2223
import org.slf4j.LoggerFactory;
2324
import org.springdoc.core.configuration.oauth2.SpringDocOAuth2AuthorizationServerMetadata;
25+
import org.springdoc.core.configuration.oauth2.SpringDocOAuth2Token;
2426
import org.springdoc.core.configuration.oauth2.SpringDocOAuth2TokenIntrospection;
2527
import org.springdoc.core.customizers.GlobalOpenApiCustomizer;
2628
import org.springdoc.core.utils.SpringDocAnnotationsUtils;
@@ -31,10 +33,7 @@
3133
import org.springframework.http.HttpMethod;
3234
import org.springframework.http.HttpStatus;
3335
import org.springframework.security.oauth2.core.OAuth2Error;
34-
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;
3536
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
36-
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationConsentAuthenticationToken;
37-
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenRevocationAuthenticationToken;
3837
import org.springframework.security.oauth2.server.authorization.web.NimbusJwkSetEndpointFilter;
3938
import org.springframework.security.oauth2.server.authorization.web.OAuth2AuthorizationEndpointFilter;
4039
import org.springframework.security.oauth2.server.authorization.web.OAuth2AuthorizationServerMetadataEndpointFilter;
@@ -49,6 +48,7 @@
4948
import org.springframework.security.web.util.matcher.RequestMatcher;
5049

5150
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
51+
import static org.springframework.http.MediaType.TEXT_HTML_VALUE;
5252

5353
/**
5454
* The type Spring doc security o auth 2 customizer.
@@ -95,7 +95,10 @@ private void getOAuth2TokenRevocationEndpointFilter(OpenAPI openAPI, SecurityFil
9595
Object oAuth2EndpointFilter =
9696
new SpringDocSecurityOAuth2EndpointUtils(OAuth2TokenRevocationEndpointFilter.class).findEndpoint(securityFilterChain);
9797
if (oAuth2EndpointFilter != null) {
98-
ApiResponses apiResponses = buildApiResponsesWithBadRequest(SpringDocAnnotationsUtils.resolveSchemaFromType(OAuth2TokenRevocationAuthenticationToken.class, openAPI.getComponents(), null), openAPI);
98+
ApiResponses apiResponses = new ApiResponses();
99+
apiResponses.addApiResponse(String.valueOf(HttpStatus.OK.value()), new ApiResponse().description(HttpStatus.OK.getReasonPhrase()));
100+
buildApiResponsesOnInternalServerError(apiResponses);
101+
buildApiResponsesOnBadRequest(apiResponses, openAPI);
99102

100103
Operation operation = buildOperation(apiResponses);
101104
Schema<?> schema = new ObjectSchema()
@@ -119,15 +122,19 @@ private void getOAuth2TokenIntrospectionEndpointFilter(OpenAPI openAPI, Security
119122
Object oAuth2EndpointFilter =
120123
new SpringDocSecurityOAuth2EndpointUtils(OAuth2TokenIntrospectionEndpointFilter.class).findEndpoint(securityFilterChain);
121124
if (oAuth2EndpointFilter != null) {
122-
ApiResponses apiResponses = buildApiResponsesWithBadRequest(SpringDocAnnotationsUtils.resolveSchemaFromType(SpringDocOAuth2TokenIntrospection.class, openAPI.getComponents(), null), openAPI);
125+
ApiResponses apiResponses = new ApiResponses();
126+
buildApiResponsesOnSuccess(apiResponses, SpringDocAnnotationsUtils.resolveSchemaFromType(SpringDocOAuth2TokenIntrospection.class, openAPI.getComponents(), null));
127+
buildApiResponsesOnInternalServerError(apiResponses);
128+
buildApiResponsesOnBadRequest(apiResponses, openAPI);
129+
123130
Operation operation = buildOperation(apiResponses);
124-
Schema<?> schema = new ObjectSchema()
131+
Schema<?> requestSchema = new ObjectSchema()
125132
.addProperty("token", new StringSchema())
126133
.addProperty(OAuth2ParameterNames.TOKEN_TYPE_HINT, new StringSchema())
127134
.addProperty("additionalParameters", new ObjectSchema().additionalProperties(new StringSchema()));
128135

129136
String mediaType = org.springframework.http.MediaType.APPLICATION_FORM_URLENCODED_VALUE;
130-
RequestBody requestBody = new RequestBody().content(new Content().addMediaType(mediaType, new MediaType().schema(schema)));
137+
RequestBody requestBody = new RequestBody().content(new Content().addMediaType(mediaType, new MediaType().schema(requestSchema)));
131138
operation.setRequestBody(requestBody);
132139
buildPath(oAuth2EndpointFilter, "tokenIntrospectionEndpointMatcher", openAPI, operation, HttpMethod.POST);
133140
}
@@ -143,7 +150,9 @@ private void getOAuth2AuthorizationServerMetadataEndpoint(OpenAPI openAPI, Secur
143150
Object oAuth2EndpointFilter =
144151
new SpringDocSecurityOAuth2EndpointUtils(OAuth2AuthorizationServerMetadataEndpointFilter.class).findEndpoint(securityFilterChain);
145152
if (oAuth2EndpointFilter != null) {
146-
ApiResponses apiResponses = buildApiResponses(SpringDocAnnotationsUtils.resolveSchemaFromType(SpringDocOAuth2AuthorizationServerMetadata.class, openAPI.getComponents(), null));
153+
ApiResponses apiResponses = new ApiResponses();
154+
buildApiResponsesOnSuccess(apiResponses, SpringDocAnnotationsUtils.resolveSchemaFromType(SpringDocOAuth2AuthorizationServerMetadata.class, openAPI.getComponents(), null));
155+
buildApiResponsesOnInternalServerError(apiResponses);
147156
Operation operation = buildOperation(apiResponses);
148157
buildPath(oAuth2EndpointFilter, "requestMatcher", openAPI, operation, HttpMethod.GET);
149158
}
@@ -159,7 +168,17 @@ private void getNimbusJwkSetEndpoint(OpenAPI openAPI, SecurityFilterChain securi
159168
Object oAuth2EndpointFilter =
160169
new SpringDocSecurityOAuth2EndpointUtils(NimbusJwkSetEndpointFilter.class).findEndpoint(securityFilterChain);
161170
if (oAuth2EndpointFilter != null) {
162-
ApiResponses apiResponses = buildApiResponses(SpringDocAnnotationsUtils.resolveSchemaFromType(JWKSet.class, openAPI.getComponents(), null));
171+
ApiResponses apiResponses = new ApiResponses();
172+
Schema<?> schema = new MapSchema();
173+
schema.addProperty("keys", new ArraySchema().items(new ObjectSchema().additionalProperties(true)));
174+
175+
ApiResponse response = new ApiResponse().description(HttpStatus.OK.getReasonPhrase()).content(new Content().addMediaType(
176+
APPLICATION_JSON_VALUE,
177+
new MediaType().schema(schema)));
178+
apiResponses.addApiResponse(String.valueOf(HttpStatus.OK.value()), response);
179+
buildApiResponsesOnInternalServerError(apiResponses);
180+
buildApiResponsesOnBadRequest(apiResponses, openAPI);
181+
163182
Operation operation = buildOperation(apiResponses);
164183
operation.responses(apiResponses);
165184
buildPath(oAuth2EndpointFilter, "requestMatcher", openAPI, operation, HttpMethod.GET);
@@ -177,7 +196,10 @@ private void getOAuth2TokenEndpoint(OpenAPI openAPI, SecurityFilterChain securit
177196
new SpringDocSecurityOAuth2EndpointUtils(OAuth2TokenEndpointFilter.class).findEndpoint(securityFilterChain);
178197

179198
if (oAuth2EndpointFilter != null) {
180-
ApiResponses apiResponses = buildApiResponsesWithBadRequest(SpringDocAnnotationsUtils.resolveSchemaFromType(OAuth2AccessTokenResponse.class, openAPI.getComponents(), null), openAPI);
199+
ApiResponses apiResponses = new ApiResponses();
200+
buildApiResponsesOnSuccess(apiResponses, SpringDocAnnotationsUtils.resolveSchemaFromType(SpringDocOAuth2Token.class, openAPI.getComponents(), null));
201+
buildApiResponsesOnInternalServerError(apiResponses);
202+
buildApiResponsesOnBadRequest(apiResponses, openAPI);
181203
buildOAuth2Error(openAPI, apiResponses, HttpStatus.UNAUTHORIZED);
182204
Operation operation = buildOperation(apiResponses);
183205
Schema<?> schema = new ObjectSchema().additionalProperties(new StringSchema());
@@ -196,7 +218,14 @@ private void getOAuth2AuthorizationEndpoint(OpenAPI openAPI, SecurityFilterChain
196218
Object oAuth2EndpointFilter =
197219
new SpringDocSecurityOAuth2EndpointUtils(OAuth2AuthorizationEndpointFilter.class).findEndpoint(securityFilterChain);
198220
if (oAuth2EndpointFilter != null) {
199-
ApiResponses apiResponses = buildApiResponsesWithBadRequest(SpringDocAnnotationsUtils.resolveSchemaFromType(OAuth2AuthorizationConsentAuthenticationToken.class, openAPI.getComponents(), null), openAPI);
221+
ApiResponses apiResponses = new ApiResponses();
222+
223+
ApiResponse response = new ApiResponse().description(HttpStatus.OK.getReasonPhrase()).content(new Content().addMediaType(
224+
TEXT_HTML_VALUE,
225+
new MediaType()));
226+
apiResponses.addApiResponse(String.valueOf(HttpStatus.OK.value()), response);
227+
buildApiResponsesOnInternalServerError(apiResponses);
228+
buildApiResponsesOnBadRequest(apiResponses, openAPI);
200229
apiResponses.addApiResponse(String.valueOf(HttpStatus.MOVED_TEMPORARILY.value()),
201230
new ApiResponse().description(HttpStatus.MOVED_TEMPORARILY.getReasonPhrase())
202231
.addHeaderObject("Location", new Header().schema(new StringSchema())));
@@ -221,30 +250,39 @@ private Operation buildOperation(ApiResponses apiResponses) {
221250
}
222251

223252
/**
224-
* Build api responses api responses.
253+
* Build api responses api responses on success.
225254
*
255+
* @param apiResponses the api responses
226256
* @param schema the schema
227257
* @return the api responses
228258
*/
229-
private ApiResponses buildApiResponses(Schema schema) {
230-
ApiResponses apiResponses = new ApiResponses();
259+
private ApiResponses buildApiResponsesOnSuccess(ApiResponses apiResponses, Schema schema) {
231260
ApiResponse response = new ApiResponse().description(HttpStatus.OK.getReasonPhrase()).content(new Content().addMediaType(
232261
APPLICATION_JSON_VALUE,
233262
new MediaType().schema(schema)));
234263
apiResponses.addApiResponse(String.valueOf(HttpStatus.OK.value()), response);
264+
return apiResponses;
265+
}
266+
267+
/**
268+
* Build api responses api responses on internal server error.
269+
*
270+
* @param apiResponses the api responses
271+
* @return the api responses
272+
*/
273+
private ApiResponses buildApiResponsesOnInternalServerError(ApiResponses apiResponses) {
235274
apiResponses.addApiResponse(String.valueOf(HttpStatus.INTERNAL_SERVER_ERROR.value()), new ApiResponse().description(HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase()));
236275
return apiResponses;
237276
}
238277

239278
/**
240-
* Build api responses with bad request api responses.
279+
* Build api responses on bad request.
241280
*
242-
* @param schema the schema
281+
* @param apiResponses the api responses
243282
* @param openAPI the open api
244283
* @return the api responses
245284
*/
246-
private ApiResponses buildApiResponsesWithBadRequest(Schema schema, OpenAPI openAPI) {
247-
ApiResponses apiResponses = buildApiResponses(schema);
285+
private ApiResponses buildApiResponsesOnBadRequest(ApiResponses apiResponses, OpenAPI openAPI) {
248286
buildOAuth2Error(openAPI, apiResponses, HttpStatus.BAD_REQUEST);
249287
return apiResponses;
250288
}
Original file line numberDiff line numberDiff line change
@@ -1,151 +1,54 @@
11
package org.springdoc.core.configuration.oauth2;
22

3-
import java.net.URL;
4-
import java.time.Instant;
53
import java.util.List;
6-
import java.util.Map;
74

85
import com.fasterxml.jackson.annotation.JsonProperty;
96
import io.swagger.v3.oas.annotations.media.Schema;
107

11-
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationServerMetadataClaimAccessor;
128
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationServerMetadataClaimNames;
139

1410
/**
1511
* The type Spring doc o auth 2 authorization server metadata.
1612
*
13+
* @ses <a href="https://github.com/spring-projects/spring-authorization-server/blob/main/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationServerMetadata.java">OAuth2AuthorizationServerMetadata</a>
1714
* @author bnasslahsen
15+
* @author yuta.saito
1816
*/
1917
@Schema(name = "OAuth2AuthorizationServerMetadata")
20-
public class SpringDocOAuth2AuthorizationServerMetadata implements OAuth2AuthorizationServerMetadataClaimAccessor {
21-
22-
23-
@Override
24-
public Map<String, Object> getClaims() {
25-
return null;
26-
}
27-
28-
@Override
29-
public <T> T getClaim(String claim) {
30-
return OAuth2AuthorizationServerMetadataClaimAccessor.super.getClaim(claim);
31-
}
32-
33-
@Override
34-
public boolean hasClaim(String claim) {
35-
return OAuth2AuthorizationServerMetadataClaimAccessor.super.hasClaim(claim);
36-
}
37-
38-
@Override
39-
public String getClaimAsString(String claim) {
40-
return OAuth2AuthorizationServerMetadataClaimAccessor.super.getClaimAsString(claim);
41-
}
42-
43-
@Override
44-
public Boolean getClaimAsBoolean(String claim) {
45-
return OAuth2AuthorizationServerMetadataClaimAccessor.super.getClaimAsBoolean(claim);
46-
}
47-
48-
@Override
49-
public Instant getClaimAsInstant(String claim) {
50-
return OAuth2AuthorizationServerMetadataClaimAccessor.super.getClaimAsInstant(claim);
51-
}
52-
53-
@Override
54-
public URL getClaimAsURL(String claim) {
55-
return OAuth2AuthorizationServerMetadataClaimAccessor.super.getClaimAsURL(claim);
56-
}
57-
58-
@Override
59-
public Map<String, Object> getClaimAsMap(String claim) {
60-
return OAuth2AuthorizationServerMetadataClaimAccessor.super.getClaimAsMap(claim);
61-
}
62-
63-
@Override
64-
public List<String> getClaimAsStringList(String claim) {
65-
return OAuth2AuthorizationServerMetadataClaimAccessor.super.getClaimAsStringList(claim);
66-
}
67-
68-
@Override
18+
public interface SpringDocOAuth2AuthorizationServerMetadata {
6919
@JsonProperty(OAuth2AuthorizationServerMetadataClaimNames.ISSUER)
70-
public URL getIssuer() {
71-
return OAuth2AuthorizationServerMetadataClaimAccessor.super.getIssuer();
72-
}
20+
String issuer();
7321

74-
@Override
7522
@JsonProperty(OAuth2AuthorizationServerMetadataClaimNames.AUTHORIZATION_ENDPOINT)
76-
public URL getAuthorizationEndpoint() {
77-
return OAuth2AuthorizationServerMetadataClaimAccessor.super.getAuthorizationEndpoint();
78-
}
23+
String authorizationEndpoint();
7924

80-
@Override
8125
@JsonProperty(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT)
82-
public URL getTokenEndpoint() {
83-
return OAuth2AuthorizationServerMetadataClaimAccessor.super.getTokenEndpoint();
84-
}
26+
String tokenEndpoint();
8527

86-
@Override
8728
@JsonProperty(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED)
88-
public List<String> getTokenEndpointAuthenticationMethods() {
89-
return OAuth2AuthorizationServerMetadataClaimAccessor.super.getTokenEndpointAuthenticationMethods();
90-
}
29+
List<String> tokenEndpointAuthMethodsSupported();
9130

92-
@Override
9331
@JsonProperty(OAuth2AuthorizationServerMetadataClaimNames.JWKS_URI)
94-
public URL getJwkSetUrl() {
95-
return OAuth2AuthorizationServerMetadataClaimAccessor.super.getJwkSetUrl();
96-
}
97-
98-
@Override
99-
@JsonProperty(OAuth2AuthorizationServerMetadataClaimNames.SCOPES_SUPPORTED)
100-
public List<String> getScopes() {
101-
return OAuth2AuthorizationServerMetadataClaimAccessor.super.getScopes();
102-
}
32+
String jwksUri();
10333

104-
@Override
10534
@JsonProperty(OAuth2AuthorizationServerMetadataClaimNames.RESPONSE_TYPES_SUPPORTED)
106-
public List<String> getResponseTypes() {
107-
return OAuth2AuthorizationServerMetadataClaimAccessor.super.getResponseTypes();
108-
}
35+
List<String> responseTypesSupported();
10936

110-
@Override
11137
@JsonProperty(OAuth2AuthorizationServerMetadataClaimNames.GRANT_TYPES_SUPPORTED)
112-
public List<String> getGrantTypes() {
113-
return OAuth2AuthorizationServerMetadataClaimAccessor.super.getGrantTypes();
114-
}
38+
List<String> grantTypesSupported();
11539

116-
@Override
11740
@JsonProperty(OAuth2AuthorizationServerMetadataClaimNames.REVOCATION_ENDPOINT)
118-
public URL getTokenRevocationEndpoint() {
119-
return OAuth2AuthorizationServerMetadataClaimAccessor.super.getTokenRevocationEndpoint();
120-
}
41+
String revocationEndpoint();
12142

122-
@Override
12343
@JsonProperty(OAuth2AuthorizationServerMetadataClaimNames.REVOCATION_ENDPOINT_AUTH_METHODS_SUPPORTED)
124-
public List<String> getTokenRevocationEndpointAuthenticationMethods() {
125-
return OAuth2AuthorizationServerMetadataClaimAccessor.super.getTokenRevocationEndpointAuthenticationMethods();
126-
}
44+
List<String> revocationEndpointAuthMethodsSupported();
12745

128-
@Override
12946
@JsonProperty(OAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT)
130-
public URL getTokenIntrospectionEndpoint() {
131-
return OAuth2AuthorizationServerMetadataClaimAccessor.super.getTokenIntrospectionEndpoint();
132-
}
47+
String introspectionEndpoint();
13348

134-
@Override
13549
@JsonProperty(OAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT_AUTH_METHODS_SUPPORTED)
136-
public List<String> getTokenIntrospectionEndpointAuthenticationMethods() {
137-
return OAuth2AuthorizationServerMetadataClaimAccessor.super.getTokenIntrospectionEndpointAuthenticationMethods();
138-
}
139-
140-
@Override
141-
@JsonProperty(OAuth2AuthorizationServerMetadataClaimNames.REGISTRATION_ENDPOINT)
142-
public URL getClientRegistrationEndpoint() {
143-
return OAuth2AuthorizationServerMetadataClaimAccessor.super.getClientRegistrationEndpoint();
144-
}
50+
List<String> introspectionEndpointAuthMethodsSupported();
14551

146-
@Override
14752
@JsonProperty(OAuth2AuthorizationServerMetadataClaimNames.CODE_CHALLENGE_METHODS_SUPPORTED)
148-
public List<String> getCodeChallengeMethods() {
149-
return OAuth2AuthorizationServerMetadataClaimAccessor.super.getCodeChallengeMethods();
150-
}
53+
List<String> codeChallengeMethodsSupported();
15154
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package org.springdoc.core.configuration.oauth2;
2+
3+
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
4+
import com.fasterxml.jackson.databind.annotation.JsonNaming;
5+
import io.swagger.v3.oas.annotations.media.Schema;
6+
7+
/**
8+
* The type Spring doc o auth 2 token.
9+
*
10+
* @see <a href="https://github.com/spring-projects/spring-security/blob/main/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/endpoint/DefaultOAuth2AccessTokenResponseMapConverter.java">DefaultOAuth2AccessTokenResponseMapConverter</a>
11+
* @author yuta.saito
12+
*/
13+
@Schema(name = "OAuth2Token")
14+
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
15+
public interface SpringDocOAuth2Token {
16+
String getAccessToken();
17+
18+
String getTokenType();
19+
20+
long getExpiresIn();
21+
22+
String getScope();
23+
24+
String getRefreshToken();
25+
}

0 commit comments

Comments
 (0)