I'm new to Spring Boot and Oauth and I'm implementing Oauth Security on my system and I have doubts with JdbcTokenStore queries.
I've seen DEFAULT_ACCESS_TOKEN_SELECT_STATEMENT = "select token_id, token from oauth_access_token where token_id =?" in the JdbcTokenStore code.
I tried to get a token from oauth/token with client_id1 and I checked the token with client_id2, finally I received a successful response because it just filters token_id.
I was expecting an error like "token not found" or something similar.
Should this query has this behavior?
UPDATE
I have a AuthorizationServer and a ResourceServer separated, on my AuthorizationServer I'm getting ClientDetails from database and I have two entries:
client_details_entries
On my Web app, I'm using the first ClientDetails to get a valid token with:
$ curl seiafiscalizacao:seiafiscalizacao123#localhost:8080/seia-auth-server/oauth/token -d grant_type=password -d username=username -d password=pwd
On oauth_access_token I got a new entry associating token_id with client_id:
access_token_entry
On my ResourceServer I have a RemoteTokenServices with these configurations (second ClientDetails):
#Bean
#Primary
public RemoteTokenServices tokenService() {
RemoteTokenServices tokenService = new RemoteTokenServices();
tokenService.setCheckTokenEndpointUrl("http://localhost:8080/seia-auth-server/oauth/check_token");
tokenService.setClientId("seiafiscalizacao2");
tokenService.setClientSecret("seiafiscalizacao123");
return tokenService;
}
Finally, when I try to get any resource from my ResourceServer I'm getting success response even using different client_id:
success_from_resourceserver_?
When I opened JdbcTokenStore code from org.springframework.security.oauth2.provider.token.store I saw private static final String DEFAULT_ACCESS_TOKEN_SELECT_STATEMENT = "select token_id, token from oauth_access_token where token_id = ?";, and I figure out why I got success.
I'm not getting any error or exception, but I'm wonder about check my token with different client_id and get success.
Like I said before, I'm new in Oauth and I dont know if this was expected.
To access ResourceServer I'm using RESTClient from Firefox
My SpringBoot version is 1.5.10.RELEASE
My application server is Wildfly 10.1
By now my project is very complex to share, but if you need I can make a new with few things latter.
The OAuth2 specification doesn't define that, see RFC 6749:
10.3. Access Tokens
[...]
This specification does not provide any methods for the resource server to ensure that an access token presented to it by a given client was issued to that client by the authorization server.
Hence, Spring Security OAuth2 doesn't check it.
Moreover, the RemoteTokenServices is a Spring Security OAuth2 feature and not covered by OAuth2 specification at all, see OAuth 2 Developers Guide
An alternative is the RemoteTokenServices which is a Spring OAuth features (not part of the spec) allowing Resource Servers to decode tokens through an HTTP resource on the Authorization Server (/oauth/check_token).
Related
A question has arised to me when I've tried to use spring-boot-starter-oauth2-resource-server dependency and set up my service as a oauth2 resource service.
I've configured spring oauth2 resource server without spring.security.oauth2.resourceserver.jwt.issuer-uri neither jwk-set-uri properties.
Instead of that, I've instructed spring oauth2 resource server library how to decode JWT. I've just created an ReactiveJwtDecoder:
#Bean
public ReactiveJwtDecoder reactiveJwtDecoder() throws Exception {
Mac mac = Mac.getInstance("HmacSHA256");
SecretKeySpec secretKey = new SecretKeySpec("JAC1O17W1F3QB9E8B4B1MT6QKYOQB36V".getBytes(), mac.getAlgorithm());
return NimbusReactiveJwtDecoder.withSecretKey(secretKey)
.macAlgorithm(MacAlgorithm.HS256)
.build();
}
With that, I'm able to validate jwt tokens.
Question arised in my mind is:
Acording to oauth rfc6749, "Accessing Protected Resources" section, says that:
The client accesses protected resources by presenting the access
token to the resource server. The resource server MUST validate the
access token and ensure that it has not expired and that its scope
covers the requested resource. The methods used by the resource
server to validate the access token (as well as any error responses)
are beyond the scope of this specification but generally involve an
interaction or coordination between the resource server and the
authorization server.
Is spring oauth2 resource server library working well?
There is nothing wrong with such approach. The possible issue here is that
you are using symmetric key. With this approach you are using the same key for both signing a token and validating its signature. It means you need to share the key used to sign tokens with all the applications involved in the authentication process that is not always possible and resource server has too much power.
To keep key on authorization service only, there is an option in the resources server to use special endpoint, exposed by authorization service, to validate the token.
Another approach would be to use asymmetric key pair to sign and validate tokens. In this case private key will be assigned to authorization service only and public key could be shared with all the applications involved in the authentication process.
#Bean
public ReactiveJwtDecoder jwtDecoder() throws GeneralSecurityException {
RSAPublicKey key = (RSAPublicKey) KeyFactory.getInstance("RSA")
.generatePublic(new X509EncodedKeySpec(getKeySpec(properties.getPublicKey())));
return NimbusReactiveJwtDecoder.withPublicKey(key)
.signatureAlgorithm(SignatureAlgorithm.from(properties.getSignatureAlgorithm()))
.build();
}
I'm a bit confused regarding whether I should be accessing my Spring Boot Resource Server via an access_token or an id_token.
First, let me quickly explain my setup:
Spring Boot app as an OAuth 2.0 Resource Server. This is configured as described in the Spring docs: Minimal Configuration for JWTs This app provides secured #Controllers that will provide data for a JavaScript SPA (eg. React)
Google's OAuth 2.0 AP / OpenID Connect already configured (Credentials, Client Id, Client Secret)
A JavaScript SPA app (eg. React) that logs the user into Google and makes requests to the Spring Boot Resource Server for secured data. These requests include the Authorization header (with Bearer token obtained from Google) for the logged in user.
For development purposes, I'm also using Postman to make requests to the Spring Boot Resource Server
I can easily configure Postman to get a token from Google. This token response from Google includes values for access_token, id_token, scope, expries_in and token_type.
However, my requests to the Resource Server are denied when Postman tries to use the value from retrieved token's access_token field as the Bearer in the Authorization header
The only way I'm able to successfully access the secured #Controllers is by using the id_token as the Bearer in the Authorization header.
Is it expected that I should use the id_token as the Bearer in the Authorization header? Or is it expected that I should use the access_token?
Some additional relevant info:
The value of the id_token is a JWT token. The value of the access_token is not a JWT token. I know this because I can decode the id_token on jwt.io but it is unable to decode the value of the access_token. Further, the Spring Boot Resource Server fails with the following when I send the access_token as the Bearer in the Authorization header:
An error occurred while attempting to decode the Jwt: Invalid unsecured/JWS/JWE header: Invalid JSON: Unexpected token ɭ� at position 2.
This blog post Understanding identity tokens says the following:
You should not use an identity token to authorize access to an API.
To access an API, you should be using OAuth’s access tokens, which are intended only for the protected resource (API) and come with scoping built-in.
Looking at at the spring-security-samples for using OAuth2 Resource Server, I see the value of there hard-coded access_token (for testing purposes) is indeed a valid JWT. As opposed to the access_token returned from Google which is not a JWT.
In summary:
I can access my Spring Boot Resource Server using the value of the id_token obtained from Google. The value of the access_token is not a JWT and fails to parse by Spring Boot.
Is there something wrong with my understanding, my configuration or what? Does Google's OpenId Connect behave differently regarding how the access_token works?
Happy to clarify or add more info if needed. Thanks for your consideration and your patience!
The blog post you mentioned is correct in my view, and I believe the OpenID Connect 1.0 spec does not intend for an id_token to be used for access purposes.
Like you, I expected that using Google as an Authorization Server would work out of the box, because Spring Security works with Google as a common OAuth2 provider for providing social login. However, this is not the case, and I believe it is not really intended, because Google is not really your authorization server. For example, I don't believe you can configure Google to work with scopes/permissions/authorities of your domain-specific application. This is different from something like Okta, where there are many options for configuring things in your own tenant.
I would actually recommend checking out Spring Authorization Server, and configuring Google as a federated identity provider. I'm working on a sample for this currently and it will be published within the next week or so (see this branch).
Having said that, if you're still interested in a simple use case where Google access tokens are used for authenticating with your resource server, you would need to provide your own opaque token introspector that uses Google's tokeninfo endpoint. It doesn't match what Spring Security expects, so it's a bit involved.
#EnableWebSecurity
public class SecurityConfiguration {
#Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
// #formatter:off
http
.authorizeRequests((authorizeRequests) -> authorizeRequests
.anyRequest().authenticated()
)
.oauth2ResourceServer(OAuth2ResourceServerConfigurer::opaqueToken);
// #formatter:on
return http.build();
}
#Bean
public OpaqueTokenIntrospector introspector() {
return new GoogleTokenIntrospector("https://oauth2.googleapis.com/tokeninfo");
}
}
public final class GoogleTokenIntrospector implements OpaqueTokenIntrospector {
private final RestTemplate restTemplate = new RestTemplate();
private final String introspectionUri;
public GoogleTokenIntrospector(String introspectionUri) {
this.introspectionUri = introspectionUri;
}
#Override
public OAuth2AuthenticatedPrincipal introspect(String token) {
RequestEntity<?> requestEntity = buildRequest(token);
try {
ResponseEntity<Map<String, Object>> responseEntity = this.restTemplate.exchange(requestEntity, new ParameterizedTypeReference<>() {});
// TODO: Create and return OAuth2IntrospectionAuthenticatedPrincipal based on response...
} catch (Exception ex) {
throw new BadOpaqueTokenException(ex.getMessage(), ex);
}
}
private RequestEntity<?> buildRequest(String token) {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
body.add("access_token", token);
return new RequestEntity<>(body, headers, HttpMethod.POST, URI.create(introspectionUri));
}
}
spring:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: https://accounts.google.com
jwk-set-uri: https://www.googleapis.com/oauth2/v3/certs
I have followed this guide.
I have a simple Oauth2 webapp using code grant authorization flow. I have a #GetMapping endpoint (as per the tutorial) that returns user information retrieved from a service provider I'm using. Since this correctly returns my information to the webapp, I can tell the code grant flow has worked. Code for working endpoint:
#GetMapping("/user")
public Map<String, Object> user(#AuthenticationPrincipal OAuth2User principal) {
return principal.getAttributes();
}
Now I want to create a new #GetMapping where I use the access token to query the API of the service provider that just authorized us.
The problem is that the example above somehow magically makes the request for me based solely on configuration and only returns user info. Now I want to get the access token for this session that Spring stores somewhere to access the API however I like "manually".
How can I extract the access token in my new #GetMapping?
I have been searching a lot, but I am new to both Oauth2 and Spring, and there is so much spring classes and tutorials that I "can't see the forest because of all the trees".
I appriciate any help.
Cheers.
I solved it.
Add the following:
#Autowired
private OAuth2AuthorizedClientService authorizedClientService;
private String getSessionBearerToken() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
OAuth2AuthenticationToken oauthToken = (OAuth2AuthenticationToken) authentication;
OAuth2AuthorizedClient client =
authorizedClientService.loadAuthorizedClient(
oauthToken.getAuthorizedClientRegistrationId(),
oauthToken.getName());
return client.getAccessToken().getTokenValue();
}
This method will get you the access token when you call it.
Hello Everyone
i am working on spring boot project with jwt authentication and spring security ,
i could get access token but have a big problem .
it is
where access token exactly stored?
i mean (in-memory database , cache or something els)
thanks
It is stored in-memory by default.
Clients
If you are an OAuth 2.0 Client, you can retrieve the token in a controller using the #RegisteredOAuth2AuthorizedClient annotation, like so:
#GetMapping("/data")
public String data(#RegisteredOAuth2AuthorizedClient OAuth2AuthorizedClient client) {
String accessToken = client.getAccessToken().getTokenValue();
// ...
}
Or, if you need it at the service layer, then you can retrieve it from the OAuth2AuthorizedClientService.
Note, though, that if you are needing the access token in order to propagate it downstream, you can instead configure the WebClient with Client's ExchangeFilterFunction that will lookup, refresh, and propagate the token for you.
Resource Servers
If you are an OAuth 2.0 Resource Server, you can retrieve it directly from the Authentication.
Here's what that looks like in the controller:
#GetMapping("/data")
public String data(#AuthenticationPrincipal Jwt jwt) {
String accessToken = jwt.getTokenValue();
// ...
}
Note, though, that if you are needing the access token in order to propagate it downstream, you can instead configure the WebClient with Resource Server's ExchangeFilterFunction that will lookup and propagate the token for you.
I try to implement a simple OAuth2 "Client Authentication with Signed JWT" Demo App using Spring Boot and Keycloak as AuthService.
The idea is:
one secured REST service "The Producer"
offering an endpoint GET /person for all users/principals with the role "read_person"
offering an endpoint POST /person for all users/principals with the role "write_person"
another (unsecured) REST service "The Consumer"
offering an enpoint /api open for everybody
calling internal the "producer" viaFeignclient using an RequestInterceptor to pass the AccessToken (signed JWT / JWS)
I read about the docs:
http://www.keycloak.org/docs/latest/securing_apps/topics/oidc/java/client-authentication.html
saying:
Once the client application is started, it allows to download its public >key in JWKS format using a URL such as http://myhost.com/myapp/k_jwks, >assuming that http://myhost.com/myapp is the base URL of your client >application. This URL can be used by Keycloak (see below).
During authentication, the client generates a JWT token and signs it with >its private key and sends it to Keycloak in the particular backchannel >request (for example, code-to-token request) in the client_assertion >parameter.
I googled a lot to find tutorials/demos or docs about this topic but failed so far.
So here my questions:
How do I implement this "k_jwk" endpoint? Do I simple build a #RestController by myself in "the Producer"? How do I configure Keycloak to get aware of this URL?
How do I implement my "Consumer" to get fresh signed JWT from Keycloak?
Update
Removed irritating PS statement.
You don't need to implement the k_jwk endpoint, this is handled by the adapter. Keycloak will by default look at http:///your.app.com/k_jwk(but if needed you can override that in the console).
Then you need to configure your Spring Boot client, just use the same properties as the keycloak.json but in the application.properties format:
...
keycloak.credentials.jwt.client-keystore-file=classpath:keystore-client.jks
keycloak.credentials.jwt.client-keystore-type=JKS
etc ...
You need a token to call the producerbut as you said the entry point will be an insecured endpoint so you might want to use a Service Account for this.
I hope this will help.
Update
I couldnt solve this issue but learned some things about singned JWT in the mean time:
create a so called "Bearer Token" by creating a Json Structure with all necessary claims (sub, nbf, exp ...) by yourself and sign/certificate it with your JKS/Private Key from Keycloak. There are some nice third party libs beside Keycloak to do this.
To get a real AccessToken (JWE/JWS) from Keycloak: send this static final Bearer Token to Keycloak at /auth/realms/$realm/protocol/openid-connect/token/introspect
with QueryParams:
grant_type=client_credentials&client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer&client_assertion=$BEARER_TOKEN
Use the received real AccessToken to access the ResourceServer...