I'm trying to get to a point where I can dynamically register new clients for my Oauth server. The problem that I have (or the issue that I'm trying to understand) is that initially without using the password encoder the client secrets where stored in the DB as plain text.
After googling around I found that one has to set a PasswordEncoder which will handle the encoding/decoding of the client secret.
#Override
public void configure(ClientDetailsServiceConfigurer configurer) throws Exception {
configurer.jdbc(dataSource).passwordEncoder(getPasswordEncoder());
}
Now I do in fact see the encoded client secret being stored in the DB. The issue is that now when requesting a token from the service I'm forced to use the encoded client secret when I request a new token (instead of using the plain text version)
curl -k -u 'TEST_USER:05a2fcc7-7759-4354-9366-a7c8cf650aae' http://localhost:8080/oauth/token -X POST -d 'response_type=token&client_id=TEST_USER&username=TEST_USERNAME&password=TEST_PASSWORD&scope=process&grant_type=password'
{"error":"unauthorized","error_description":"Full authentication is required to access this resource"}
Where as if I use the encoded client secret, the service generates the token as it should.
The thing that I was trying to achieve was having the password encoded in the DB and being able to use the plain text version i the request.
What am I missing or doing wrong?
In case anyone else is looking, the solution to this is to use a DelegatingPasswordEncoder instead of BCrypt directly as I was using (which would not be obvious from the code sample in the original question).
#Bean
public PasswordEncoder getPasswordEncoder(){
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
Related
Ive been struggling with implementing the Authorization Code Grant flow for the Fitbit API. Ive set up the WebSecurityConfigurerAdapter, and I get correctly redirected to the Fitbit /oauth2/authorize page, where I can give permission. However, when I allow my application access, it says the following: authorization_request_not_found. Also, the url does not include a code.
I cannot really find good documentation on how to implement the next step using Spring Boot.
It would be greatly appreciated if anyone could point me in the right direction. Thanks
Stack:
The problem is in access token request. Following authorization code grant flow you need to set authorization header to basic. Your client_id and secret concatenated with a colon and encoded to Base64 will be your basic authorization header value.
You can find more info in official docs: Access Token Request
Implementing this in spring security is quite simple. Just follow this tutorial:
Custom Token Request
Convert method of CustomRequestEntityConverter class should look like this one below:
#Override
public RequestEntity<?> convert(OAuth2AuthorizationCodeGrantRequest req) {
RequestEntity<?> entity = defaultConverter.convert(req);
MultiValueMap<String, String> headers = entity.getHeaders();
String authorization = Base64.getEncoder().encodeToString(BASIC_AUTHORIZATION.getBytes());
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setBasicAuth(authorization);
httpHeaders.addAll(headers);
return new RequestEntity<>(entity.getBody(), httpHeaders, entity.getMethod(), entity.getUrl());
}
Im looking to create an angular application which login against a new authentication server created in springboot and return a jwt.
The idea is to create the application to be able to generate and sign the jwt token with a private key based on the user/password provided in the screen, the authentication server will validate the login information in database and generate the jwt token.
After that, a request will be sent to another microservice and in here I need to be able to validate the token, but this microservice wont be connected to the authentication service or database in any way, it will just validate the integrity of the token using a public key.
Im looking everywhere and I dont find the clue to be able to validate the token, I found this piece of code but for some reason when I execute the rest API exposed this code is not executed:
#Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
Resource resource = new ClassPathResource("public.txt");
String publicKey = null;
try {
publicKey = IOUtils.toString(resource.getInputStream(), Charset.defaultCharset());
} catch (final IOException e) {
throw new RuntimeException(e);
}
converter.setVerifierKey(publicKey);
return converter;
}
Does what im trying to do makes any sense?
Thanks
Regards
Your auth server will will need to be the single issuer of JWTs to your microservices. So, when a user logs in and successfully authenticates, your auth server will issue a JWT signed with a private key (signing MUST be asymmetric - RS256 is one example) you keep on the auth server only; do not give this private key to other microservices that you wish to validate JWTs inside of. What you can do is derive a public key based on the private key you sign your tokens with and publish that to an endpoint on your auth server that requires no authentication - the public key will be represented in the form of a JWK (see link to spec). Google does something similar here. Then, in each of your microservices, you will need to devise a way to make a GET request to the public key endpoint on your auth server every X minutes and cache the public key in each microservice.
Then whenever a request comes into one of your microservices, you grab the JWT, check its validity, and grant access/authorization if the token is valid. The beauty of using a private/public key pair and asymmetric key signing is that you can validate a token based on the public key alone, but not sign it. So as long as each service has the public key from your /cert endpoint, they can validate a token without ever needing to talk to the auth server or knowing the private key.
This will require a little more work up front, but will yield you massive amount of ease, flexibility, and peace of mind in the future knowing only one source knows your private key.
I suggest using this library to do JWT validation.
The overall architecture will end up looking something like this:
Hi Stackoverflow team,
I am facing an issue in my REST Call which I am clueless about after trying to dig into the HTTP errors.
Somehow the authorization isn't working , eventhough the generation and fetch of the JWT token is successful.
Short Description of what I have in my Springboot App :
(Available for analysis of the problem at)
https://github.com/vivdso/SpringAuthentication
A DbRepository call that talks to a backend MongoDb collection named UserAccounts which has roles and credential details stored including the passwords (Ciphertexts).
A JWT token generation mechanism that returns a token which has to be attached to the HTTP Headers for the subsequent API Calls.
The flow in short.
".....:8080/auth" method post Content-Type appliction/json body:{"username":"user","password":"sample"} Response should be a jwt token
and then
Try the autheticated url .....:8080/order.
****EXPECTED RESULT : Header" Authorization:{$jwtToken from step 6} Actual Result: :( Error : 403 forbidden, this should be fully authenticated and should let the user access this api. Expected Result: "Hello here is my order"****
This is just a simple application with not too many details to worry about.
Any help will be appreciated.
Thanks in advance.
in your code I couldn't find the filter registration.
Try to add it in the WebSecurityConfig.java
#Bean
public CustomAuthenticationTokenFilter authenticationTokenFilterBean() throws Exception {
CustomAuthenticationTokenFilter authenticationTokenFilter = new CustomAuthenticationTokenFilter ();
authenticationTokenFilter.setAuthenticationManager(authenticationManagerBean());
return authenticationTokenFilter;
}
and then register it with
http
.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
inside the configuration method
et me know
This was a role mismatch issue. Was not matching with the role in jwt.
Changed the code to correct the role and it worked fine -
public CustomDbRepository(){
List<String> roles = new ArrayList<>(1);
//roles.add("ROLE_USER");
roles.add("USER");
I've found similar issue but it's unanswered, so I suppose I'm going to duplicate question a little.
I am using Spring OAuth2 to implement separate resource and custom authentification servers.
I've already configured interaction with auth server through issuing&validating JWT tokens and everything seems fine.
Now I'm trying to add SSO functionality but really stuck with it. I've researched the official Spring examples and attached guide but it is very short worded when it comes to connecting SSO part with custom server authentication. And actually author uses only external provider resource ('user' info) to show process.
I think it is normal thing to have all this SSO means of authentication and also custom registration. I can see it works well with stackoverflow for example.
I am loking for directions where to find any info about handling on resource server different kind of tokens issued by multiply SSO providers and also from custom auth server.
Maybe I can use auth chain to do this and some mean to distinguish token format to know how to process it. Is it possible with Spring OAuth2? Or I need to do this magic somehow manually?
For now I have just one 'maybe strange' idea:
To not involve my own resource server with this SSO stuff at all. After receiving Facebook (for example) token - just exchange it for api JWT token with custom auth server (associating or creating user on the way) and then work with resource server on standard basics
EDITED:
I've found at least something. I've read about configuring filters in authorization chain and translate given social tokens to my custom JWT-s as 'post authenticate'(not a crazy idea after all). But it mostly done with SpringSocial.
So now question is: how to do that?
Forgot to say that I am using Password Grant for authentication on custom server. Clients will be only trusted application and I do not even sure about browser client (thinking about only native mobile options). Even if I decide to have browser client I'll make sure it's going to have backend to store sencetive information
Ok, so after struggling to implement such behavior I've stuck with two different libraries (Spring Social & OAuth2). I decided to go my own way and do it with just Spring OAuth2:
I have the resource server, authentication server and client(backed up by Java and uses OAuth2 Client library, but it can be any other client) - my resources can be consumed only with my own JWT auth token given by my own auth server
in a case of a custom registration: client obtains JWT token(with refresh token) from auth server and sends it to the res server. Res server validates it with public key and gives the resource back
in a case of SSO: client obtains Facebook(or other social platform token) and exchanges it for my custom JWT token with my custom auth server. I've implemented this on my auth server using custom SocialTokenGranter(currently handles facebook social token only. For every social network I'll need separate grant type). This class makes an additional call to facebook auth server to validate token and obtain user info. Then it retrieves the social user from my db or creates new and returns JWT token back to the client. No user merging is done by now. it is out of scope for now.
public class SocialTokenGranter extends AbstractTokenGranter {
private static final String GRANT_TYPE = "facebook_social";
GiraffeUserDetailsService giraffeUserDetailsService; // custom UserDetails service
SocialTokenGranter(
GiraffeUserDetailsService giraffeUserDetailsService,
AuthorizationServerTokenServices tokenServices,
OAuth2RequestFactory defaultOauth2RequestFactory,
ClientDetailsService clientDetailsService) {
super(tokenServices, clientDetailsService, defaultOauth2RequestFactory, GRANT_TYPE);
this.giraffeUserDetailsService = giraffeUserDetailsService;
}
#Override
protected OAuth2Authentication getOAuth2Authentication(ClientDetails clientDetails, TokenRequest request) {
// retrieve social token sent by the client
Map<String, String> parameters = request.getRequestParameters();
String socialToken = parameters.get("social_token");
//validate social token and receive user information from external authentication server
String url = "https://graph.facebook.com/me?access_token=" + socialToken;
Authentication userAuth = null;
try {
ResponseEntity<FacebookUserInformation> response = new RestTemplate().getForEntity(url, FacebookUserInformation.class);
if (response.getStatusCode().is4xxClientError()) throw new GiraffeException.InvalidOrExpiredSocialToken();
FacebookUserInformation userInformation = response.getBody();
GiraffeUserDetails giraffeSocialUserDetails = giraffeUserDetailsService.loadOrCreateSocialUser(userInformation.getId(), userInformation.getEmail(), User.SocialProvider.FACEBOOK);
userAuth = new UsernamePasswordAuthenticationToken(giraffeSocialUserDetails, "N/A", giraffeSocialUserDetails.getAuthorities());
} catch (GiraffeException.InvalidOrExpiredSocialToken | GiraffeException.UnableToValidateSocialUserInformation e) {
// log the stacktrace
}
return new OAuth2Authentication(request.createOAuth2Request(clientDetails), userAuth);
}
private static class FacebookUserInformation {
private String id;
private String email;
// getters, setters, constructor
}
}
And from class extending AuthorizationServerConfigurerAdapter:
private TokenGranter tokenGranter(AuthorizationServerEndpointsConfigurer endpoints) {
List<TokenGranter> granters = new ArrayList<>(Arrays.asList(endpoints.getTokenGranter()));
granters.add(new SocialTokenGranter(giraffeUserDetailsService, endpoints.getTokenServices(), endpoints.getOAuth2RequestFactory(), endpoints.getClientDetailsService()));
return new CompositeTokenGranter(granters);
}
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer
...
.allowFormAuthenticationForClients() // to allow sending parameters as form fields
...
}
Every JWT token request is going to 'host:port + /oauth/token' url
Depending on 'Grant type' the server will handle such requests differently. Currently I have 'password'(default), 'refresh_token' and 'facebook_social'(custom) grant types
For default 'password' Grant type the client should send next parameters:
clientId
clientSecret (depends of the client type. Not for single-page clients)
username
password
scope (if not explicitly set in auth server configuration for current client)
grantType
For 'refresh_token' Grant type the client should send next parameters:
clientId
clientSecret (depends of the client type. Not for single-page clients)
refresh_token
grantType
For 'facebook_social' Grant type the client should send next parameters:
clientId
facebook_social_token (custom field)
grantType
Based on the client design the way to send these requests will be different.
In my case with test Java based client which uses Spring OAuth2 library to obtain the social token I do the token exchange procedure with the redirect in controller(controller being invoked using url defined in facebook dev page configuration).
It can be handled in two stages: after obtaining facebook social token JavaScript can make a separate explicit call to my auth server to exchange tokens.
You can see Java client implementation examples here, but I doubt that you're going to use Java client in production:https://spring.io/guides/tutorials/spring-boot-oauth2/
Have just installed spring security oauth2 in my eclipse IDE. The service am trying to implement will be consumed by second party users through their installed applications hence i chose to use password grant type. As per my understanding of Oauth2 the following request should work for the demo sparklr2 service without the need of me encording the username and password parameters. i.e
POST http://localhost:8080/sparklr2/oauth/token?grant_type=password&client_id=my-trusted-client&scope=trust&username=marissa&password=koala
but i keep getting
<oauth>
<error_description>
Full authentication is required to access this resource
</error_description>
<error>unauthorized</error>
</oauth>
am i missing something in this request or do i need to enable something in the repo
It seems like Spring OAuth2 doesn't support the password grant type for a secret-less OAuth2 client. This might be as per the OAuth2 spec: https://www.rfc-editor.org/rfc/rfc6749#section-4.3.2, although the spec seems to indicate that the client authentication is not always required (that's not very clear to me).
That means that when calling the token endpoint using the password grant type, you need to pass in the client ID and secret (using basic auth), which also mean that you can't use the password grant if the client does not have a secret (you might still be able to use the implicit flow).
In sparklr2, my-trusted-client does not have a secret defined which is why your call fails.
If you want to see the password grant type in action you can try my-trusted-client-with-secret:
curl -u my-trusted-client-with-secret:somesecret "http://localhost:8080/sparklr2/oauth/token?grant_type=password&username=marissa&password=koala"
Although the question is a bit old, I would like to contribute with my findings around this.
It is true that for Spring OAuth you need to specify a client ID in order to access to the token endpoint, but it is not necessary to specify client Secret for password grant type.
Next lines are an example of an Authorization Server client for password grant type without any client Secret. Yout just need to add them in your class that extends AuthorizationServerConfigurerAdapter:
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("clientId")
.authorizedGrantTypes("password")
.authorities("ROLE_CLIENT")
.scopes("read");
}
}
Furthermore, it is indeed possible to avoid the HTTP Basic Authentication in the token endpoint and add our client_id as another request parameter in our POST call.
To achieve this, you just need to add these lines in the same class as before:
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.allowFormAuthenticationForClients();
}
Now we can call the token endpoint by this way, which seems more correct following the examples found in Stormpath webpage
POST http://localhost:8080/sparklr2/oauth/token?grant_type=password&client_id=clientId&scope=read&username=marissa&password=koala