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.
Related
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.
I have a Spring Boot application that is setup as a Service Provider. My end goal is to be able to call the AWS STS Assume Role with SAML service to generate AWS temporary credentials on behalf of the user with the SAML response used to initially authenticate users of my application.
I found this other question. With that answer I am able to get only the assertion, not the entire response. From my testing, the AWS API call linked above wants the entire response, not just the assertion piece.
I used this Chrome Extension to view the SAML response. When I include everything (outline below)
<samlp:Response>
...
<saml:Assertion>
...
</saml:Assertion>
</samlp:Response>
The AWS STS Assume Role with SAML works. The other related question's answer only provides me the
<saml:Assertion>...</saml:Assertion>
block and the AWS STS Assume Role with SAML fails.
So my question is how do I get the entire SAML Response XML object back in a controller of my Spring Boot application?
I don't know any direct way in spring-security-saml, but maybe you could try to implement your own SAMLProcessingFilter ie simply extending the existing one and overriding the method attemptAuthentication().
Principle:
In this method, you have access to the response returned from the IdP and post back to the SP (at least in a Redirect-POST profile)
You probably have a way to extract what you need from the httpRequest
Then you can store (session, ThreadLocal variable, ...)
And finally you delegate the authentication process to the parent (by calling super.attemptAuthentication())
`
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
if ("POST".equalsIgnoreCase(request.getMethod())) {
String samlResponse = request.getParameter("SAMLResponse");
System.out.println("Original SAML Response (base64 decoded) : " + new
String(Base64.getDecoder().decode(samlResponse), StandardCharsets.UTF_8));
}
return super.attemptAuthentication(request, response);
}
`
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).
I have 2 microservices that I have created with spring boot. 1 microservice has a oauth2 authentication service and the other is an oauth2 resource server.
The resource server uses RemoteTokenService to check if the access token is valid. This works and when I create a rest endpoint and supply a Principal parameter the principal of the logged in user is supplied. Example:
#RequestMapping(method = RequestMethod.GET, value = "/api/user/{id:[0-9]+}")
#PreAuthorize("hasAnyAuthority('ROLE_USER')")
public User getUser(#PathVariable("id") long id, Principal principal) {
}
The thing is that the Principal contains the username and authorities of the logged in user and I also need the user info like id of the user.
I don't want to do an extra rest call to get the user data so I was wandering is there anyway to get the remotetokenservice to return more information?
The RemoteTokenServices makes nothing but calling CheckTokenEndpoint of Spring Security and return back the map values to the resources server.
If you want to get more information then you have to implement CheckTokenEndpoint::checkToken method in the Authentication server.
We managed to solve your problem by having spring-oauth2 with JWT integration. So the Authentication Server generates an access token as a claim (after authentication the user) which can hold more information that could be useful at the Resource server. The resource server in this case didn't need to have a remote call to check the token, but it verify the signature of the JWT to accept the claim.