Skip to content

Commit 92e8c08

Browse files
rozagerardojgrandja
authored andcommitted
Add Token Introspection Endpoint
Closes gh-52
1 parent 4e2626e commit 92e8c08

15 files changed

+2184
-3
lines changed

oauth2-authorization-server/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OAuth2AuthorizationServerConfigurer.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationProvider;
4343
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientCredentialsAuthenticationProvider;
4444
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2RefreshTokenAuthenticationProvider;
45+
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenIntrospectionAuthenticationProvider;
4546
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenRevocationAuthenticationProvider;
4647
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
4748
import org.springframework.security.oauth2.server.authorization.config.ProviderSettings;
@@ -50,6 +51,7 @@
5051
import org.springframework.security.oauth2.server.authorization.web.OAuth2AuthorizationEndpointFilter;
5152
import org.springframework.security.oauth2.server.authorization.web.OAuth2ClientAuthenticationFilter;
5253
import org.springframework.security.oauth2.server.authorization.web.OAuth2TokenEndpointFilter;
54+
import org.springframework.security.oauth2.server.authorization.web.OAuth2TokenIntrospectionEndpointFilter;
5355
import org.springframework.security.oauth2.server.authorization.web.OAuth2TokenRevocationEndpointFilter;
5456
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
5557
import org.springframework.security.web.authentication.HttpStatusEntryPoint;
@@ -65,12 +67,14 @@
6567
*
6668
* @author Joe Grandja
6769
* @author Daniel Garnier-Moiroux
70+
* @author Gerardo Roza
6871
* @since 0.0.1
6972
* @see AbstractHttpConfigurer
7073
* @see RegisteredClientRepository
7174
* @see OAuth2AuthorizationService
7275
* @see OAuth2AuthorizationEndpointFilter
7376
* @see OAuth2TokenEndpointFilter
77+
* @see OAuth2TokenIntrospectionEndpointFilter
7478
* @see OAuth2TokenRevocationEndpointFilter
7579
* @see NimbusJwkSetEndpointFilter
7680
* @see OidcProviderConfigurationEndpointFilter
@@ -81,12 +85,14 @@ public final class OAuth2AuthorizationServerConfigurer<B extends HttpSecurityBui
8185

8286
private RequestMatcher authorizationEndpointMatcher;
8387
private RequestMatcher tokenEndpointMatcher;
88+
private RequestMatcher tokenIntrospectionEndpointMatcher;
8489
private RequestMatcher tokenRevocationEndpointMatcher;
8590
private RequestMatcher jwkSetEndpointMatcher;
8691
private RequestMatcher oidcProviderConfigurationEndpointMatcher;
8792
private final RequestMatcher endpointsMatcher = (request) ->
8893
this.authorizationEndpointMatcher.matches(request) ||
8994
this.tokenEndpointMatcher.matches(request) ||
95+
this.tokenIntrospectionEndpointMatcher.matches(request) ||
9096
this.tokenRevocationEndpointMatcher.matches(request) ||
9197
this.jwkSetEndpointMatcher.matches(request) ||
9298
this.oidcProviderConfigurationEndpointMatcher.matches(request);
@@ -183,12 +189,18 @@ public void init(B builder) {
183189
getAuthorizationService(builder));
184190
builder.authenticationProvider(postProcess(tokenRevocationAuthenticationProvider));
185191

192+
OAuth2TokenIntrospectionAuthenticationProvider tokenIntrospectionAuthenticationProvider =
193+
new OAuth2TokenIntrospectionAuthenticationProvider(
194+
getAuthorizationService(builder));
195+
builder.authenticationProvider(postProcess(tokenIntrospectionAuthenticationProvider));
196+
186197
ExceptionHandlingConfigurer<B> exceptionHandling = builder.getConfigurer(ExceptionHandlingConfigurer.class);
187198
if (exceptionHandling != null) {
188199
exceptionHandling.defaultAuthenticationEntryPointFor(
189200
new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED),
190201
new OrRequestMatcher(
191202
this.tokenEndpointMatcher,
203+
this.tokenIntrospectionEndpointMatcher,
192204
this.tokenRevocationEndpointMatcher)
193205
);
194206
}
@@ -216,6 +228,7 @@ public void configure(B builder) {
216228
authenticationManager,
217229
new OrRequestMatcher(
218230
this.tokenEndpointMatcher,
231+
this.tokenIntrospectionEndpointMatcher,
219232
this.tokenRevocationEndpointMatcher));
220233
builder.addFilterAfter(postProcess(clientAuthenticationFilter), AbstractPreAuthenticatedProcessingFilter.class);
221234

@@ -237,6 +250,12 @@ public void configure(B builder) {
237250
authenticationManager,
238251
providerSettings.tokenRevocationEndpoint());
239252
builder.addFilterAfter(postProcess(tokenRevocationEndpointFilter), OAuth2TokenEndpointFilter.class);
253+
254+
OAuth2TokenIntrospectionEndpointFilter tokenIntrospectionEndpointFilter =
255+
new OAuth2TokenIntrospectionEndpointFilter(
256+
authenticationManager,
257+
providerSettings.tokenIntrospectionEndpoint());
258+
builder.addFilterAfter(postProcess(tokenIntrospectionEndpointFilter), OAuth2TokenEndpointFilter.class);
240259
}
241260

242261
private void initEndpointMatchers(ProviderSettings providerSettings) {
@@ -249,6 +268,8 @@ private void initEndpointMatchers(ProviderSettings providerSettings) {
249268
HttpMethod.POST.name()));
250269
this.tokenEndpointMatcher = new AntPathRequestMatcher(
251270
providerSettings.tokenEndpoint(), HttpMethod.POST.name());
271+
this.tokenIntrospectionEndpointMatcher = new AntPathRequestMatcher(
272+
providerSettings.tokenIntrospectionEndpoint(), HttpMethod.POST.name());
252273
this.tokenRevocationEndpointMatcher = new AntPathRequestMatcher(
253274
providerSettings.tokenRevocationEndpoint(), HttpMethod.POST.name());
254275
this.jwkSetEndpointMatcher = new AntPathRequestMatcher(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
/*
2+
* Copyright 2020-2021 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.oauth2.core;
18+
19+
import static org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames.CLIENT_ID;
20+
import static org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames.SCOPE;
21+
import static org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames.TOKEN_TYPE;
22+
import static org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames.USERNAME;
23+
import static org.springframework.security.oauth2.jwt.JwtClaimNames.AUD;
24+
import static org.springframework.security.oauth2.jwt.JwtClaimNames.EXP;
25+
import static org.springframework.security.oauth2.jwt.JwtClaimNames.IAT;
26+
import static org.springframework.security.oauth2.jwt.JwtClaimNames.ISS;
27+
import static org.springframework.security.oauth2.jwt.JwtClaimNames.JTI;
28+
import static org.springframework.security.oauth2.jwt.JwtClaimNames.NBF;
29+
import static org.springframework.security.oauth2.jwt.JwtClaimNames.SUB;
30+
31+
import java.net.URL;
32+
import java.time.Instant;
33+
import java.util.List;
34+
35+
/**
36+
* TODO This class is a copy from Spring Security (Resource Server) with the difference that we rely on the existing
37+
* {@code OAuth2ParameterNames} and {@code JwtClaimNames} claims. It should be consolidated when merging this codebase into Spring
38+
* Security.
39+
*
40+
* A {@link ClaimAccessor} for the &quot;claims&quot; that may be contained in the Introspection Response.
41+
*
42+
* @author David Kovac
43+
* @author Gerardo Roza
44+
* @since 0.1.1
45+
* @see ClaimAccessor
46+
* @see OAuth2IntrospectionClaimNames
47+
* @see OAuth2IntrospectionAuthenticatedPrincipal
48+
* @see <a target="_blank" href= "https://tools.ietf.org/html/rfc7662#section-2.2">Introspection Response</a>
49+
*/
50+
public interface OAuth2TokenIntrospectionClaimAccessor extends ClaimAccessor {
51+
52+
String ACTIVE = "active";
53+
54+
/**
55+
* Returns the indicator {@code (active)} whether or not the token is currently active
56+
*
57+
* @return the indicator whether or not the token is currently active
58+
*/
59+
default boolean isActive() {
60+
return Boolean.TRUE.equals(this.getClaimAsBoolean(ACTIVE));
61+
}
62+
63+
/**
64+
* Returns the scopes {@code (scope)} associated with the token
65+
*
66+
* @return the scopes associated with the token
67+
*/
68+
default String getScope() {
69+
return this.getClaimAsString(SCOPE);
70+
}
71+
72+
/**
73+
* Returns the client identifier {@code (client_id)} for the token
74+
*
75+
* @return the client identifier for the token
76+
*/
77+
default String getClientId() {
78+
return this.getClaimAsString(CLIENT_ID);
79+
}
80+
81+
/**
82+
* Returns a human-readable identifier {@code (username)} for the resource owner that authorized the token
83+
*
84+
* @return a human-readable identifier for the resource owner that authorized the token
85+
*/
86+
default String getUsername() {
87+
return this.getClaimAsString(USERNAME);
88+
}
89+
90+
/**
91+
* Returns the type of the token {@code (token_type)}, for example {@code bearer}.
92+
*
93+
* @return the type of the token, for example {@code bearer}.
94+
*/
95+
default String getTokenType() {
96+
return this.getClaimAsString(TOKEN_TYPE);
97+
}
98+
99+
/**
100+
* Returns a timestamp {@code (exp)} indicating when the token expires
101+
*
102+
* @return a timestamp indicating when the token expires
103+
*/
104+
default Instant getExpiresAt() {
105+
return this.getClaimAsInstant(EXP);
106+
}
107+
108+
/**
109+
* Returns a timestamp {@code (iat)} indicating when the token was issued
110+
*
111+
* @return a timestamp indicating when the token was issued
112+
*/
113+
default Instant getIssuedAt() {
114+
return this.getClaimAsInstant(IAT);
115+
}
116+
117+
/**
118+
* Returns a timestamp {@code (nbf)} indicating when the token is not to be used before
119+
*
120+
* @return a timestamp indicating when the token is not to be used before
121+
*/
122+
default Instant getNotBefore() {
123+
return this.getClaimAsInstant(NBF);
124+
}
125+
126+
/**
127+
* Returns usually a machine-readable identifier {@code (sub)} of the resource owner who authorized the token
128+
*
129+
* @return usually a machine-readable identifier of the resource owner who authorized the token
130+
*/
131+
default String getSubject() {
132+
return this.getClaimAsString(SUB);
133+
}
134+
135+
/**
136+
* Returns the intended audience {@code (aud)} for the token
137+
*
138+
* @return the intended audience for the token
139+
*/
140+
default List<String> getAudience() {
141+
return this.getClaimAsStringList(AUD);
142+
}
143+
144+
/**
145+
* Returns the issuer {@code (iss)} of the token
146+
*
147+
* @return the issuer of the token
148+
*/
149+
default URL getIssuer() {
150+
return this.getClaimAsURL(ISS);
151+
}
152+
153+
/**
154+
* Returns the identifier {@code (jti)} for the token
155+
*
156+
* @return the identifier for the token
157+
*/
158+
default String getId() {
159+
return this.getClaimAsString(JTI);
160+
}
161+
162+
}

0 commit comments

Comments
 (0)