Saturday, May 31, 2014

Securing REST Services with Spring Security and OAuth2

Get the code: https://github.com/iainporter/oauth2-provider

This post will walk through setting up an OAuth2 provider service for protecting access to REST resources. The code is available in github. You can fork the code and start writing services that will be protected by OAuth access. This is a separate module but builds on services covered in a previous series that includes:


It would be useful but not required to be familiar with some of the technologies covered such as:

* OAuth2 Protocol
* Spring Security
* Spring Integration
* Spring Data
* Jersey/JAX-RS
* Gradle / Groovy
* MongoDB

Resource Owner Password Flow

Subsequent posts will deal with the other types of authorization flow, such as using third party providers (Facebook, Google, etc). The intent of this post is a walk through of the Resource Owner Password flow. This is a typical use case if you are the system of record for user credentials and trust client applications. It is simply the exchange of the user's username and password for an access token. This token can then be used on subsequent requests to authorize access to resources. It is also important to support token expiration and by extension token refresh.

Let's test out the code and then I'll walk through the application and explain how it is built

Building the project


Check out the source code


> git clone  git@github.com:iainporter/oauth2-provider.git

> cd oauth2-provider

> ./gradlew clean build integrationTest

Running the Web Application


The application uses MongoDB as the persistence store. Before running the application ensure that mongod is running on port 27017. If you don't have mongo installed get it here

Once mongoDB is installed and running fire up the web application using the following command

> ./gradlew tomcatRun


To try it out you can open a browser window and navigate to http://localhost:8080/oauth2-provider/index.html

Testing with Curl


To create a user:

> curl -v -X POST \
   -H "Content-Type: application/json" \
   -H "Authorization: Basic MzUzYjMwMmM0NDU3NGY1NjUwNDU2ODdlNTM0ZTdkNmE6Mjg2OTI0Njk3ZTYxNWE2NzJhNjQ2YTQ5MzU0NTY0NmM=" \
   -d '{"user":{"emailAddress":"user@example.com"}, "password":"password"}' \
   'http://localhost:8080/oauth2-provider/v1.0/users'

The result:

{"apiUser":
   {"emailAddress":"user@example.com",
   "firstName":null,
   "lastName":null,
   "age":null,
   "id":"8a34d009-3558-4c8c-a8da-1ad2b2a393c7",
   "name":"user@example.com"},
   "oauth2AccessToken":
   {"access_token":"7e0e4708-7837-4a7e-9f87-81c6429b02ac",
   "token_type":"bearer",
   "refresh_token":"d0f248ab-e30f-4a85-860c-bd1e388a39b5",
   "expires_in":5183999,
   "scope":"read write"
   }
}


Requesting an access token:


> curl -v -X POST \
   -H "Content-Type: application/json" \
   -H "Authorization: Basic MzUzYjMwMmM0NDU3NGY1NjUwNDU2ODdlNTM0ZTdkNmE6Mjg2OTI0Njk3ZTYxNWE2NzJhNjQ2YTQ5MzU0NTY0NmM=" \
   'http://localhost:8080/oauth2-provider/oauth/token?grant_type=password&username=user@example.com&password=password'


The result:

{
  "access_token":"a838780e-35ef-4bd5-92c0-07a45aa74948",
  "token_type":"bearer",
  "refresh_token":"ab06022f-247c-450a-a11e-2ffab116e3dc",
  "expires_in":5183999
}


Refreshing a token:


> curl -v -X POST \
   -H "Content-Type: application/json" \
   -H "Authorization: Basic MzUzYjMwMmM0NDU3NGY1NjUwNDU2ODdlNTM0ZTdkNmE6Mjg2OTI0Njk3ZTYxNWE2NzJhNjQ2YTQ5MzU0NTY0NmM=" \
   'http://localhost:8080/oauth2-provider/oauth/token?grant_type=refresh_token&refresh_token=ab06022f-247c-450a-a11e-2ffab116e3dc'


The result:

{
   "access_token":"4835cd11-8bb7-4b76-b857-55c6e7f36fc4",
   "token_type":"bearer",
   "refresh_token":"ab06022f-247c-450a-a11e-2ffab116e3dc",
   "expires_in":5183999
}


That's all that is needed to support registration of Users and managing tokens on their behalf. The source code also contains everything needed to support lost password and email verification. See previous posts for how to set it up.

In a typical deployment the authorization server and the resource server(s) would be separate, but for the purposes of this tutorial they are managed within the same application.
Let's delve into the details:

Web Context


There are two servlets.

A Jersey servlet as the default to handle all resource calls:
<servlet-mapping>
        <servlet-name>jersey-servlet</servlet-name>
        <url-pattern>/*</url-pattern>
</servlet-mapping>
A Spring servlet to handle all oauth calls:
<servlet-mapping>
        <servlet-name>spring</servlet-name>
        <url-pattern>/oauth/*</url-pattern>
</servlet-mapping>
Wire in spring security by defining a filter:
    <filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
        <init-param>
            <param-name>contextAttribute</param-name>
            <param-value>org.springframework.web.servlet.FrameworkServlet.CONTEXT.spring</param-value>
        </init-param>
    </filter>
and then map it to the root context so all calls are filtered:
    <filter-mapping>
        <filter-name>springSecurityFilterChain</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

Configuring OAuth flows to support


<oauth:authorization-server client-details-service-ref="client-details-service" token-services-ref="tokenServices">
        <oauth:refresh-token/>
        <oauth:password/>
    </oauth:authorization-server>

In this scenario only password flow and refresh token are being supported. The default token endpoint is /oauth/token.

Protecting the token endpoint


It is a good idea to protect access to token requests to only those client applications that you know about. Using Spring security we can set up basic authentication on calls to the token endpoint:
    <http pattern="/oauth/token" create-session="stateless" authentication-manager-ref="clientAuthenticationManager"
          xmlns="http://www.springframework.org/schema/security">
        <anonymous enabled="false"/>
        <http-basic entry-point-ref="clientAuthenticationEntryPoint"/>
        <access-denied-handler ref="oauthAccessDeniedHandler"/>
    </http>

Next we configure the authentication manager and client details service
    <bean id="clientCredentialsTokenEndpointFilter"
          class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter">
        <property name="authenticationManager" ref="clientAuthenticationManager"/>
    </bean>

    <authentication-manager id="clientAuthenticationManager" xmlns="http://www.springframework.org/schema/security">
        <authentication-provider user-service-ref="client-details-user-service"/>
    </authentication-manager>


    <bean id="client-details-user-service" class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService">
        <constructor-arg ref="client-details-service" />
    </bean>

Depending on how many clients we expect to access the service will determine what type of client details service we implement. A simple file-based service might be sufficient. If you wanted to go a lot further with client sign up and managing API keys then you would have to involve a persistence tier for the client details service. Configuring with the default Spring file-based service is trivial:

<oauth:client-details-service id="client-details-service">

        
        <oauth:client
                client-id="353b302c44574f565045687e534e7d6a"
                secret="286924697e615a672a646a493545646c"
                authorized-grant-types="password,refresh_token"
                authorities="ROLE_TEST"
                access-token-validity="${oauth.token.access.expiresInSeconds}"
                refresh-token-validity="${oauth.token.refresh.expiresInSeconds}"
                />

        
        <oauth:client
                client-id="7b5a38705d7b3562655925406a652e32"
                secret="655f523128212d6e70634446224c2a48"
                authorized-grant-types="password,refresh_token"
                authorities="ROLE_WEB"
                access-token-validity="${oauth.token.access.expiresInSeconds}"
                refresh-token-validity="${oauth.token.refresh.expiresInSeconds}"
                />

        
        <oauth:client
                client-id="5e572e694e4d61763b567059273a4d3d"
                secret="316457735c4055642744596b302e2151"
                authorized-grant-types="password,refresh_token"
                authorities="ROLE_IOS"
                access-token-validity="${oauth.token.access.expiresInSeconds}"
                refresh-token-validity="${oauth.token.refresh.expiresInSeconds}"
                />

        
        <oauth:client
                client-id="302a7d556175264c7e5b326827497349"
                secret="4770414c283a20347c7b553650425773"
                authorized-grant-types="password,refresh_token"
                authorities="ROLE_ANDROID"
                access-token-validity="${oauth.token.access.expiresInSeconds}"
                refresh-token-validity="${oauth.token.refresh.expiresInSeconds}"
                />

</oauth:client-details-service>

Accessing the oauth endpoint now requires a basic authentication header with the client id and secret concatenated with a ":" separator and base64 encoded.
e.g. -H "Authorization: Basic MzUzYjMwMmM0NDU3NGY1NjUwNDU2ODdlNTM0ZTdkNmE6Mjg2OTI0Njk3ZTYxNWE2NzJhNjQ2YTQ5MzU0NTY0NmM="

Configuring User Authentication Services


The Resource Owner Password flow requires an authentication manager for managing users.

    <bean id="passwordEncoder" class="org.springframework.security.crypto.password.StandardPasswordEncoder"/>

    <sec:authentication-manager alias="userAuthenticationManager">
        <sec:authentication-provider user-service-ref="userService">
            <sec:password-encoder ref="passwordEncoder"/>
        </sec:authentication-provider>
    </sec:authentication-manager>

The password encoder is used to encrypt the password on authentication. The user service also uses the same encoder to encrypt passwords before persisting them.
The user service must implement UserDetailsService and retrieve the user by username.
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        notNull(username, "Mandatory argument 'username' missing.");
        User user = userRepository.findByEmailAddress(username.toLowerCase());
        if (user == null) {
            throw new AuthenticationException();
        }
        return user;
    }

Configuring Token Services


The final piece of configuration is to manage storage and retrieval of access tokens.

    <bean id="tokenServices" class="org.springframework.security.oauth2.provider.token.DefaultTokenServices">
        <property name="tokenStore" ref="tokenStore"/>
        <property name="supportRefreshToken" value="true"/>
        <property name="clientDetailsService" ref="client-details-service"/>
    </bean>

Spring has a handy in-memory implementation that is useful when testing:

     <bean id="tokenStore" class="org.springframework.security.oauth2.provider.token.InMemoryTokenStore"/>

For this tutorial I have chosen MongoDB as the persistence technology, but it would be easy to wire in an alternative such as MySql or Redis It is a matter of implementing org.springframework.security.oauth2.provider.token.TokenStore

<bean id="tokenStore" class="com.porterhead.oauth2.mongodb.OAuth2RepositoryTokenStore">
      <constructor-arg ref="OAuth2AccessTokenRepository"/>
      <constructor-arg ref="OAuth2RefreshTokenRepository"/>
</bean>

Protecting access to resources


First we need to configure a spring resource server filter. This will check that there is a valid access token in the request header.

    <oauth:resource-server id="resourceServerFilter" token-services-ref="tokenServices"/>

Spring security has some useful classes for fine-grain control of roles and permissions. I prefer to use JSR-250 annotations. Luckily it is easy to wire this in. First we need to enable JSR-250 annotations with the following configuration:

    <sec:global-method-security jsr250-annotations="enabled" access-decision-manager-ref="accessDecisionManager"/>

Configure an access manager that uses the Jsr250Voter

    <bean id="accessDecisionManager" class="org.springframework.security.access.vote.UnanimousBased">
        <property name="decisionVoters">
            <list>
                <bean class="org.springframework.security.access.annotation.Jsr250Voter"/>
            </list>
        </property>
    </bean>

Now we can protect REST resource methods with JSR-250 annotations such as @RolesAllowed

Trying it out


Typically the glue between the OAuth server and the application is a user identifier. When a client gets an access token for a user the next step is to typically load data related to that user. This will usually involve building a url with the userId as part of the path
i.e. /v1.0/users/{id}/someresource
A useful service to provide is a way to get user information based on the access token. The application can identify the user that owns the token and return information on that user to the client.
@Path("/v1.0/me")
@Component
@Produces({MediaType.APPLICATION_JSON})
@Consumes({MediaType.APPLICATION_JSON})
public class MeResource extends BaseResource {

    @RolesAllowed({"ROLE_USER"})
    @GET
    public ApiUser getUser(final @Context SecurityContext securityContext) {
        User requestingUser = loadUserFromSecurityContext(securityContext);
        if(requestingUser == null) {
            throw new UserNotFoundException();
        }
        return new ApiUser(requestingUser);
    }

    protected User loadUserFromSecurityContext(SecurityContext securityContext) {
        OAuth2Authentication requestingUser = (OAuth2Authentication) securityContext.getUserPrincipal();
        Object principal = requestingUser.getUserAuthentication().getPrincipal();
        User user = null;
        if(principal instanceof User) {
            user = (User)principal;
        } else {
            user = userRepository.findByEmailAddress((String)principal);
        }
        return user;
    }
}

To test this out start up the application:

> ./gradlew tomcatRun


Execute the following curl statement, substituting the access token that was returned from the login curl above

> curl -v -X GET \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer [your token here]" \
  'http://localhost:8080/oauth2-provider/v1.0/me'

To try out some of the other services included in the code see previous posts:


169 comments:

  1. Does it work with java 8?
    I got error
    ..
    ..
    com.porterhead.user.UserServiceTest > badNameRequest FAILED
    java.lang.ClassFormatError at UserServiceTest.java:48

    .
    while building
    and while running

    org.springframework.beans.factory.BeanDefinitionStoreException: Failed to read candidate component class: file [/home/mshanmuga/rnd/oauth2-provider/build/classes/main/com/porterhead/filter/StaticContentFilter.class]; nested exception is org.springframework.core.NestedIOException: ASM ClassReader failed to parse class file - probably due to a new Java class file version that isn't supported yet: file [/home/mshanmuga/rnd/oauth2-provider/build/classes/main/com/porterhead/filter/StaticContentFilter.class]; nested exception is java.lang.IllegalArgumentException

    ...

    ReplyDelete
  2. Thanks for that.
    I had not tested against Java 8

    Disabled emma and updated spring dependencies to work with Java 8

    ReplyDelete
  3. Very helpful. Thanks very much !

    ReplyDelete
  4. Hello, nice post.

    Can you give some advices to implement a persistence service for client applications ? (I saw there is an implementation with mysql (JdbcTokenStore), but how do I do with mongodb?

    Thanks

    ReplyDelete
  5. If you pull the code you will see that the implementation of the TokenStore is for MongoDB

    ReplyDelete
    Replies
    1. Yes, but I meant store clients_id in Mongo rather than in the xml file :)

      Delete
    2. If you look at the Spring JdbcClientDetailsImplementationService then all you have to do for a MongoDB service is implement:

      ClientDetailsService and ClientRegistrationService

      in a similar fashion to com.porterhead.oauth2.mongodb.OAuth2RepositoryTokenStore

      Delete
  6. any chance to change the xml configs to java based ones?

    ReplyDelete
    Replies
    1. That was on my list of things to do. Could be a while though...
      You could always submit a pull request :-)

      Delete
    2. Tried to configure it for the past 2 days with no success :D, it would be interesting though because of the lack of examples.
      Great work by the way ;)

      Delete
    3. Since I desided to go with the XML configuration, I am facing this issue, the application boots up normally but when trying to get a token as saw on you example, I am getting this response:
      {
      error: "unauthorized"
      error_description: "There is no client authentication. Try adding an appropriate authentication filter."
      }
      any ideas on what causes this to happen?

      Delete
    4. can you post:

      1. the command you used to start the app
      2. The curl request you sent to it
      3. The stacktrace of the error

      Delete
    5. I've been trying to do this as well except I'm trying to get it to run with Spring Boot. I have the dependencies stabalized, just need that Java Config. Perhaps I'll upload it in a bit if it helps.

      Delete
  7. This comment has been removed by the author.

    ReplyDelete
  8. Amazing post! Any chance of merging your previous code that contains the user registration along with this new one so we can have a complete example and we can test it using the browser rather then curl.

    ReplyDelete
    Replies
    1. All of the registration code and Lost Password code is included. If you want to use it set it up as explained in the previous posts.

      By default it will run with an in-memory mail sender.

      To run it execute:

      > ./gradlew tomcatRun

      and then open the location in your browser: http://localhost:8080/oauth2-provider/index.html

      make sure you have mongod running on port 27017

      Delete
  9. Excuse my ignorance but how do we go about protecting our own web services. Perhaps you can point me to some tutorial (on Spring security or something). Thanks again for an amazing framework.

    ReplyDelete
    Replies
    1. The spring OAuth2 sample covers setting up a separate resource server and the sample code shows how to do it: http://projects.spring.io/spring-security-oauth/docs/oauth2.html
      My code sample has the auth server and resource server together and uses JSR250 annotations so you can protect your services using @RolesAllowed

      Delete
  10. Sorry for the late response, I got this working using programmatic configuration.
    Unfortunately my Gradle knowledge is very limited so I cannot configure the project and do a PL.
    I have created a Gist (https://gist.github.com/maxsap/da154f99a2dbb414471a), if you like you can use that to configure the programmatic configuration.
    Thank you for the help :D

    ReplyDelete
    Replies
    1. forgot to mention that I have updated Spring, Jersey versions and also the model is now compatible with JPA.

      Delete
    2. Are you using JWT instead of storing the token details?

      Delete
    3. xfghsdrgsdrgtsdfg srgts gsdrgsdfgdfgdfgsdfgsdfghttp://www.moneycontrol.com/mutual-funds/nav/axis-long-term-equity-fund/MAA011A nonymousMay 10, 2015 at 1:31 AM
      Are you using JWT instead of storing the token details?

      Delete
  11. I have developed a web app (with REST services) with its own ant build scripts, etc. Is there any way to use your sample project to secure my web app but maintain both as a separate projects so one can focus on user registration and authentication and token management and the other on the actual webapp functionality?

    ReplyDelete
  12. I have been playing around with it and having some problems with the web-based components.
    I can create a user account by visiting http://localhost:8080/oauth2-provider/index.html - and I can log in. Once I login, I can see the dashboard with my user name and it says email unverified. When i refresh the page I get an empty dashboard with the console error: [Error] TypeError: 'undefined' is not an object (evaluating 'oauth2.user.user.firstName')

    After I login (using web-based login form), hitting http://localhost:8080/oauth2-provider/v1.0/me gives an error:
    {"errorCode":null,"consumerMessage":"Internal System error","applicationMessage":"Server error","validationErrors":[]} and on server:
    Internal Server Error: org.springframework.security.access.AccessDeniedException: Access is denied


    Also I'm trying to manually verify users by updating the mongodb manually (db.app_user.update({'verified':false},{$set:{'verified':true}});) and all the accounts are verified but it still shows it as unverified.

    Thanks for any assistance.



    ReplyDelete
    Replies
    1. The web-based components I included are in no way production quality. I included them merely to give an easy visual door on the functionality. I would use curl as a means to test out the code.

      Having said that I'll take a look at the problems you mentioned

      Delete
    2. You cannot access the url http://localhost:8080/oauth2-provider/v1.0/me from a browser as that call is protected and requires an access token in the header.

      You would have to add a view page and construct the request with JQuery.

      Delete
    3. Thanks Iain. I understand that the web component are not production quality but I want to try to develop a web-based login and registration and want to understand why the oauth2 variable is not returning a user after a page is refreshed.

      Delete
  13. This comment has been removed by the author.

    ReplyDelete
  14. I have been testing and its very good. This could become a powerful framework. Here are a few features that would be nice to have and perhaps you can give us guidance on how to best implement them:
    1- CORS support (probably through a ContainerResponseFilter)
    2- Support for 3rd party keys (to allow 3rd party developers access to your API)
    3- and of course integration with 3rd party resource servers (FB, Twitter, LinkedIn, etc) as you mentioned for your future series

    Thanks again for a great product...

    ReplyDelete
    Replies
    1. Thanks for giving it a work out.
      Those are some good suggestions (first I have heard of CORS)
      I would be happy to merge any Pull Requests if you want to contribute

      Delete
  15. Hi Iain, have you tried running this in using the production spring profile ? I get tons of errors when doing so (clean build integrationTest)
    Works perfectly fine with the default profile.

    ReplyDelete
    Replies
    1. I would not recommend trying to run tests with anything other than the default profile. This is configured to use in-memory proxies.
      Using the production profile means that your real database will be used for inserting and deleting data.

      If you try and execute the following:

      ./gradlew tomcatRun -Dspring.profiles.active=production

      then you will also get errors if you have not set up your production database config and mail properties.

      Delete
    2. This comment has been removed by the author.

      Delete
    3. What are the steps to move this into production. I guess you need a mongodb up and running and to configure the mail server? What file is used to specify the mail server configuration? If we are creating a WAR to deploy on a remote server, do we have to create one that is production ready? and if yes, how is this done?
      Thanks

      Delete
    4. I added instructions to the ReadMe (https://github.com/iainporter/oauth2-provider)

      Delete
  16. I have a client that uploads a file via REST but cannot pass the access token in header (because the client uses a form) but can pass the token as a form field. How can I manually identify the user by looking up the ApiUser using the access token in my resource class (that implements BaseResource)?

    ReplyDelete
    Replies
    1. I don't quite understand why the client cannot set an authorization header.

      But, you can achieve what you need by injecting the OAuth2AccessTokenRepository into your service class and do a look up with the following method:

      public OAuth2AuthenticationAccessToken findByTokenId(String tokenId);

      The resultant Access Token object will contain the username.

      You can use the UserRepository to do a look up and find the user.

      Delete
    2. The client is using a Form to submit the file (using GWT) and you cannot assign headers to a request initiated from a form (at least using GWT)

      Delete
  17. For the createUser API, I can't figure out why the response's ApiUser contains a "Name" field set to the email address. I debugged and I can't see that field at all. Any idea ?

    {"apiUser":
    {"emailAddress":"user@example.com",
    "firstName":null,
    "lastName":null,
    "age":null,
    "id":"8a34d009-3558-4c8c-a8da-1ad2b2a393c7",
    "name":"user@example.com"},

    ReplyDelete
    Replies
    1. Look at com.porterhead.user.api.ApiUser which implements Principal

      @Override
      public String getName() {
      return this.getEmailAddress();
      }

      Delete
  18. Hi Iain, I've noticed that when loading users using

    protected User loadUserFromSecurityContext(SecurityContext securityContext)

    it returns the "original' user object.

    For example, using the MeResource, if you create a user without a name, get a auth_token, call the "/me" resource, all is well. But then, add a name to that user, then call the "/me" resource again on that user, and you will not get the name. The reason is that the user returned is actually from :

    oAuth2AuthenticationAccessToken document : {authentication { userAuthentication { principal :..... } } }

    I found this stackoverflow question that seems similar but wanted to get your thoughts :

    http://stackoverflow.com/questions/7889660/how-to-reload-spring-security-principal-after-updating-in-hibernate

    ReplyDelete
    Replies
    1. A solution is to make sure the user is refreshed on a GET call.
      I modified the GET api to ensure that happens.

      I also added a /v1.0/users{id} PUT to allow updates.

      Sample curl:

      curl -v -X PUT \
      -H "Content-Type: application/json" \
      -H "Authorization: Bearer {your oauthToken}" \
      -d '{"firstName":"Foo", "lastName": "Bar", "emailAddress":"foobar@example.com"}' \
      'http://localhost:8080/oauth2-provider/v1.0/users/{userId}'

      and the GET:

      curl -v -X GET \
      -H "Content-Type: application/json" \
      -H "Authorization: Bearer {your oauthToken}" \
      'http://localhost:8080/oauth2-provider/v1.0/users/{userId}'

      Delete
    2. Yes of course that makes sense. I was hoping I could avoid a query to the DB but don't think that will be possible. thanks !

      Delete
  19. Hi Iain, can you please discuss a little on how to add Facebook (and other social apps) to this workflow ? Do we need to first login to FB client side and then pass the access token ? ( like in your previous rest-java work) or is this a different approach ?

    ReplyDelete
    Replies
    1. Been busy on other things but I'll tackle this next

      Delete
  20. Great job Iain, I'm new with REST and your series helped a lot. Thanks

    ReplyDelete
  21. This comment has been removed by the author.

    ReplyDelete
  22. Hi
    Thank you for the great post
    i have a problem of receiving an empty response when i send a correct token request.
    so if i execute the token request command mentioned at the post exactly as its is i receive an empty success response, but if i change the command parameters to give me an error, like change the encoded client id, i got an error response

    like the following :
    ===================================
    correct command :
    --------------------------------------------

    curl -v -X POST \
    -H "Content-Type: application/json" \
    -H "Authorization: Basic MzUzYjMwMmM0NDU3NGY1NjUwNDU2ODdlNTM0ZTdkNmE6Mjg2OTI0Njk3ZTYxNWE2NzJhNjQ2YTQ5MzU0NTY0NmM=" \
    'http://localhost:8080/oauth/oauth2/token?grant_type=password&username=user@example.com&password=password'

    the response in this case :
    ========================================
    * Hostname was NOT found in DNS cache
    * Trying 127.0.0.1...
    * Connected to localhost (127.0.0.1) port 8080 (#0)
    > POST /oauth/oauth2/token?grant_type=password&username=user@example.com&password=password HTTP/1.1
    > User-Agent: curl/7.35.0
    > Host: localhost:8080
    > Accept: */*
    > Content-Type: application/json
    > Authorization: Basic MzUzYjMwMmM0NDU3NGY1NjUwNDU2ODdlNTM0ZTdkNmE6Mjg2OTI0Njk3ZTYxNWE2NzJhNjQ2YTQ5MzU0NTY0NmM=
    >
    < HTTP/1.1 200 OK
    * Server Apache-Coyote/1.1 is not blacklisted
    < Server: Apache-Coyote/1.1
    < Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE
    < Access-Control-Max-Age: 3600
    < Access-Control-Allow-Headers: x-requested-with
    < Content-Length: 0
    < Date: Wed, 03 Sep 2014 02:10:19 GMT
    <
    * Connection #0 to host localhost left intact



    now when i change the request and give a wrong client id, like changing one letter in the authorization header:
    i got the following response:
    ================================================

    < HTTP/1.1 401 Unauthorized
    * Server Apache-Coyote/1.1 is not blacklisted
    < Server: Apache-Coyote/1.1
    < Cache-Control: no-store
    < Pragma: no-cache
    < WWW-Authenticate: Basic realm="oauth", error="unauthorized", error_description="No client with requested id: 353b302c44577&565045687e534e7d6a"
    < Content-Type: application/json;charset=UTF-8
    < Transfer-Encoding: chunked
    < Date: Wed, 03 Sep 2014 02:12:28 GMT
    <
    * Connection #0 to host localhost left intact
    {"error":"unauthorized","error_description":"No client with requested id: 353b302c44577&565045687e534e7d6a"}


    why i am getting this empty response in success case?

    ReplyDelete
    Replies
    1. I can't replicate your problem. The url looks wrong.
      If you have not modified any of the client details then the following should work to register a user and then login:

      Register:

      curl -v -X POST -H "Content-Type: application/json" \
      -H "Authorization: Basic MzUzYjMwMmM0NDU3NGY1NjUwNDU2ODdlNTM0ZTdkNmE6Mjg2OTI0Njk3ZTYxNWE2NzJhNjQ2YTQ5MzU0NTY0NmM=" \
      -d '{"user":{"emailAddress":"user@example.com"}, "password":"password"}' \
      'http://localhost:8080/oauth2-provider/v1.0/users'

      login:

      curl -v -X POST \
      -H "Content-Type: application/json" \
      -H "Authorization: Basic MzUzYjMwMmM0NDU3NGY1NjUwNDU2ODdlNTM0ZTdkNmE6Mjg2OTI0Njk3ZTYxNWE2NzJhNjQ2YTQ5MzU0NTY0NmM=" \
      'http://localhost:8080/oauth2-provider/oauth/token?grant_type=password&username=user@example.com&password=password'

      The response from the login:

      {"access_token":"c0deeea5-9dee-43bc-80cd-4ae476fc8696","token_type":"bearer","refresh_token":"9450289b-f939-40f6-ac13-b312ebf48fb9","expires_in":2269879,"scope":" write read"}

      Delete
  23. hi
    what part is responsible for parsing the base64 authorization code and get the client id from it?

    ReplyDelete
    Replies
    1. The mechanism for this is provided by Spring Security.

      If you look in src/main/resources/META-INF/spring/oauth/oauth2-configuration.xml
      the token endpoint for retrieving an auth token is defined:


      <http pattern="/oauth/token" create-session="stateless" authentication-manager-ref="clientAuthenticationManager"
      xmlns="http://www.springframework.org/schema/security">
      <anonymous enabled="false"/>
      <http-basic entry-point-ref="clientAuthenticationEntryPoint"/>
      <access-denied-handler ref="oauthAccessDeniedHandler"/>
      <custom-filter ref="corsFilter" after="LAST"/>
      </http>


      A BasicAuthenticationFilter is added to the call chain by Spring. This filter handles decoding of the Basic Header and delegates to an authenticationManager to authenticate with the decoded client name and client secret.

      In this case an In-memory ClientDetailsService matches the credentials against the clients set up in src/main/resources/META-INF/spring/oauth/client-details.xml

      Delete
  24. Trying to run the generated war file in Tomee but keep getting this error:
    java.lang.NullPointerException
    at com.porterhead.RestResourceApplication.(RestResourceApplication.java:32)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:408)
    at java.lang.Class.newInstance(Class.java:433)
    at org.apache.openejb.config.AnnotationDeployer$ProcessAnnotatedBeans.deploy(AnnotationDeployer.java:2097)
    at org.apache.openejb.config.AnnotationDeployer$ProcessAnnotatedBeans.deploy(AnnotationDeployer.java:1895)
    at org.apache.openejb.config.AnnotationDeployer.deploy(AnnotationDeployer.java:358)
    at org.apache.openejb.config.ConfigurationFactory$Chain.deploy(ConfigurationFactory.java:401)
    at org.apache.openejb.config.ConfigurationFactory.configureApplication(ConfigurationFactory.java:962)
    at org.apache.tomee.catalina.TomcatWebAppBuilder.startInternal(TomcatWebAppBuilder.java:1214)
    at org.apache.tomee.catalina.TomcatWebAppBuilder.configureStart(TomcatWebAppBuilder.java:1087)
    at org.apache.tomee.catalina.GlobalListenerSupport.lifecycleEvent(GlobalListenerSupport.java:130)
    at org.apache.catalina.util.LifecycleSupport.fireLifecycleEvent(LifecycleSupport.java:117)
    at org.apache.catalina.util.LifecycleBase.fireLifecycleEvent(LifecycleBase.java:90)
    at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5378)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:901)
    at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:877)
    at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:649)
    at org.apache.catalina.startup.HostConfig.deployWAR(HostConfig.java:1083)
    at org.apache.catalina.startup.HostConfig$DeployWar.run(HostConfig.java:1880)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)

    Any ideas how to fix this? Thanks

    ReplyDelete
    Replies
    1. I deployed it successfully to Tomcat.

      That stacktrace does not make sense as line 32 is not valid in the RestResourceApplication class.

      Have you made any made any modifications to that class?

      Delete
    2. Sorry just added System.out.println("rootCtx="+rootCtx); at line 31

      Delete
  25. How do you enable JSP processing within this framework? I tried adding a simple <%= new java.util.Date() %> to a jsp file to no avail. I followed instructions at http://stackoverflow.com/questions/12951047/spring-mvc-jsp-mapping. Is there anything in particular that need to be done to enable JSP within this framework? Thanks

    ReplyDelete
    Replies
    1. That would entail using an MVC approach as opposed to a pure REST application model.
      JQuery has date functions that you can use.

      Delete
    2. I would need to use JSP for my app. So would you recommend splitting the project into 2 apps? Where my JSP-based app uses REST authentication (from the second app based on your framework)

      Delete
    3. I see that Jersey has an extension that might provide what you need: https://jersey.java.net/documentation/latest/mvc.html

      Delete
  26. This comment has been removed by the author.

    ReplyDelete
    Replies
    1. I started receiving the following error when trying to login a user (registration works fine): HTTP Status 500 - Request processing failed; nested exception is org.springframework.data.mapping.model.MappingException: No property clientAuthentication found on entity class org.springframework.security.oauth2.provider.OAuth2Authentication to bind constructor parameter to!

      Do you have any idea what this could be? Thanks a bunch

      Server side log: http://pastebin.com/ZpLLjFQq

      Delete
    2. Yes that is due to the default Mongo converter in Spring not being able to bind to the clientAuthentication property.

      That is why I cobbled together a Custom Converter here:

      com.porterhead.oauth2.mongodb.OAuth2AuthenticationReadConverter

      It is registered by the com.porterhead.configuration.MongoDBConfiguration class.

      Have you made any modifications to the configuration that would cause the ReadConverter class to not be picked up on startup?

      Delete
    3. Thanks Iain,

      Only changes I've made was to add a couple of classes to @EnableMongoRepositories(basePackageClasses = {..}

      It seems that customConversions() is called normally.

      I also created a class that inherits from AbstractBinder for another part of the project but I don't think that should have any effect. Any idea

      Delete
    4. You might want to pull the latest changes as I upgraded to the latest spring and mongoDB releases a couple of months ago

      Delete
    5. I did upgrade to the latest version.

      Delete
    6. Could it be because I'm running it on Tomcat 8? Was working fine and it suddenly stopped working.

      Delete
    7. does ./gradlew integrationTest work?

      Delete
    8. I've imported the project into a maven project so have not tried running the test. Everything was working fine a few days ago so I'm at a loss. customConversions() is being called though so I'm not sure why this is happening. Is there anything I need to be looking out for to make sure it works.

      Delete
    9. The penny dropped,
      You must be loading a token that was created before my changes.
      The later versions of Spring Security changed the object model and there is no longer a
      clientAuthentication property to load.

      If you can I would suggest dropping your tables and repopulating

      Delete
  27. Hello,

    I've integrated Swagger to this project (oauth2-provider) for providing a clean documentation of the REST API.

    Swagger is simply run as a servlet (com.wordnik.swagger.jersey.config.JerseyJaxrsConfig) defined in web.xml. I'd like to programmatically configure this servlet using properties (api version, application name, application description, etc.) defined in app-.properties.

    The first step I took was to extend JerseyJaxrsConfig so I can perform custom initialization. For example, one could access PropertySourcesPlaceholderConfigurer using something like context.getBean("mySpringBean") however I'm not sure if that would be the proper way to access the application properties. How would you do it?

    Thanks!

    Swagger + Jersey2: http://wordnik.github.io/swagger-core/service/java/tomcat-jersey-2.html

    ReplyDelete
    Replies
    1. I have used Swagger on previous projects and it worked great.

      Not sure on the config but presumably as it is Spring based you can annotate with @Value

      Delete
    2. Swagger is easy to integrate, but enabling oauth2 is slightly more difficult. By any chance, did you already do it in the past?

      First of all maybe, Swagger UI allows to enter an API key
      http://petstore.swagger.wordnik.com/

      In your blog you mention only once the term "API key" but it's not precisely described. Is the API key the client id or the 64base encoded string "client_id:client_secret"?

      Delete
    3. I think you will need to add some spring config for each of the urls that are accessed by Swagger.
      An example:

      <http pattern="/api-docs/v1.0/users/**" create-session="stateless" use-expressions="true" entry-point-ref="http403EntryPoint">
      <intercept-url pattern="/api-docs/v1.0/users/**" access="permitAll()"/>
      </http>

      Delete
    4. In Swagger, I redirect the user to a login page when he wants to access a method which requires oauth2 authentication. The server returns an access token which is then given to Swagger.

      The access arrives here in Swagger:
      window.authorizations.add("oauth2", new ApiKeyAuthorization("Authorization", "Bearer " + b, "header"));

      So far so good! :)

      However I get an error in the JS console when doing a request:
      XMLHttpRequest cannot load http://localhost:8080/rest-oauth2/v1.0/events/1. Request header field Authorization is not allowed by Access-Control-Allow-Headers.

      However I have no problem with a similar curl request and the authorization header (-H "Authorization: Bearer 206d391a-5f11-49db-a9b6-0aa86b884405").

      Both browser and curl requests are executed from localhost, which is an allowed origin.

      Thanks!

      Delete
    5. (This is most likely an issue with Swagger, I'll let you know once I found the answer)

      Delete
    6. Ok I've nailed down what the issue is. :)

      Swagger UI sends an OPTIONS request to the oauth provider to know if the "Authorization" header is supported.

      Remote Address:127.0.0.1:8080
      Request URL:http://localhost:8080/rest-oauth2/v1.0/items/1
      Request Method:OPTIONS
      Status Code:200 OK

      Request Headers
      Accept:*/*
      Accept-Encoding:gzip,deflate,sdch
      Accept-Language:en-US,en;q=0.8
      Access-Control-Request-Headers:accept, authorization, content-type
      Access-Control-Request-Method:GET
      Cache-Control:no-cache
      Connection:keep-alive
      DNT:1
      Host:localhost:8080
      Origin:http://localhost
      Pragma:no-cache
      Referer:http://localhost/swagger/
      User-Agent:Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.104 Safari/537.36

      Response Headers
      Access-Control-Allow-Headers:X-HTTP-Method-Override, Content-Type, x-requested-with
      Access-Control-Allow-Methods:GET, POST, PUT, DELETE
      Access-Control-Allow-Origin:http://localhost
      Access-Control-Max-Age:3600
      Allow:HEAD,GET,OPTIONS
      Content-Length:818
      Content-Type:application/vnd.sun.wadl+xml
      Last-Modified:Sun, 26 Oct 2014 06:09:37 CET
      Server:Jetty(6.1.25)

      The response returned is 200 OK but the browser console shows the exception "XMLHttpRequest cannot load http://localhost:8080/rest-oauth2/v1.0/items/1. Request header field Authorization is not allowed by Access-Control-Allow-Headers."

      JerseyCrossOriginResourceSharingFilter doesn't support OPTIONS request, however SpringCrossOriginResourceSharingFilter does. Moreover the above response shows that the exception is thrown when the request meet JerseyCrossOriginResourceSharingFilter (Access-Control-Allow-Headers:X-HTTP-Method-Override, Content-Type, x-requested-with).

      Moreover, could you please explain me why the curl GET request below, which includes an "Authorization" header, works fine even though neither Jersey nor Spring CORS filters list "Authorization" as an allowed header?

      $ curl -v -X GET -H "Content-Type: application/json" -H "Authorization: Bearer " 'http://localhost:8080/rest-oauth2/v1.0/items/544777392dbf3cc5a3393bf7'

      Delete
    7. Not sure why it worked but I have added it as an allowed header and allow OPTIONS in the Jersey filter.

      Delete
  28. Hi,

    Starting from the GitHub project 'oauth2-provider', I've created a new package to give the possibility to users to create/get/update Items. The implementation of this package is based on the package com.porterhead.user (Item, ApiItem, ItemRepository, ItemService, ItemResource, CreateItemRequest, etc.).

    For getting items, the method:
    public ApiItem getItem(final @PathParam("id") String itemId, final @Context SecurityContext securityContext)

    doesn't work, but everything works fine if reference to the security context is removed:
    public ApiItem getItem(final @PathParam("id") String itemId)

    However, I'd like to access the security context (as UserResource does) so that I can extract from it the id of the logged in user and thus return only the object associated to the user.

    What am I missing? I suspect authentification-managers.xml should be edited but I don't know how.

    ReplyDelete
    Replies
    1. Pull the latest code and check out my latest Blog Post

      http://porterhead.blogspot.co.uk/2014/10/implementing-jsr-250-hierarchical-roles.html

      It has an example of using SecurityContext

      Delete
    2. I had another look at it but I think I didn't miss anything.

      I didn't mention it but the exception I encounter is:

      org.gradle.tooling.GradleConnectionException: Could not execute build using Gradle distribution 'http://services.gradle.org/distributions/gradle-2.0-bin.zip'.
      at org.gradle.tooling.internal.consumer.ResultHandlerAdapter.onFailure(ResultHandlerAdapter.java:55)
      ...
      Caused by: java.lang.ClassCastException: org.gradle.messaging.remote.internal.PlaceholderException cannot be cast to com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationException
      at com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException.toString(IllegalAnnotationsException.java:62)
      ...

      The service then shutdown when the exception is thrown.

      I did some experiments and I can't figure out what the problem is, especially since it looks like its not deterministic. As a remainder, I've simply added a new package (x.y.z.item) to the oauth2-provider to manage persistent items.

      - The above exception is only thrown when executing a request using Swagger.
      - The exception is never thrown when using curl commands

      I was spamming three get requests (from users, me, items) in Swagger and at some point the exception is thrown though I didn't manage to identify the condition behind the exception.

      Correct output (I didn't add oauth support to Swagger yet):
      {
      "errorCode": "401",
      "consumerMessage": "You do not have the appropriate privileges to access this resource",
      "applicationMessage": "Access is denied",
      "validationErrors": []
      }

      And at some point, the exception is thrown:

      Run A:
      GET http://localhost:8080/rest-oauth2/v1.0/events/1 401 (Unauthorized) shred.bundle.js:2608
      GET http://localhost:8080/rest-oauth2/v1.0/events/1 401 (Unauthorized) shred.bundle.js:2608
      OPTIONS http://localhost:8080/rest-oauth2/v1.0/me net::ERR_CONNECTION_RESET shred.bundle.js:2608
      OPTIONS http://localhost:8080/rest-oauth2/v1.0/me net::ERR_CONNECTION_RESET shred.bundle.js:2608
      OPTIONS http://localhost:8080/rest-oauth2/v1.0/me net::ERR_CONNECTION_RESET shred.bundle.js:2608
      OPTIONS http://localhost:8080/rest-oauth2/v1.0/me net::ERR_CONNECTION_RESET shred.bundle.js:2608
      OPTIONS http://localhost:8080/rest-oauth2/v1.0/me net::ERR_CONNECTION_REFUSED shred.bundle.js:2608

      Run B:
      GET http://localhost:8080/rest-oauth2/v1.0/events/1 401 (Unauthorized) shred.bundle.js:2608
      OPTIONS http://localhost:8080/rest-oauth2/v1.0/users/1 net::ERR_EMPTY_RESPONSE shred.bundle.js:2608
      OPTIONS http://localhost:8080/rest-oauth2/v1.0/users/1 net::ERR_EMPTY_RESPONSE shred.bundle.js:2608
      OPTIONS http://localhost:8080/rest-oauth2/v1.0/users/1 net::ERR_CONNECTION_RESET shred.bundle.js:2608
      GET http://localhost:8080/rest-oauth2/v1.0/users/1 net::ERR_CONNECTION_REFUSED

      (ERR_CONNECTION_REFUSED because the exception shutdown the service)

      Any idea what the issue with this hit-and-run bug?

      Delete
    3. I would need to see some config/code to understand what you are trying to achieve.

      Delete
  29. Hello,

    I'd like to enable the HTTPS/SSL mode for your project "oauth2-provider".

    Do the settings regard only the container tomcat/jetty or do they involve Jersey or Spring components? Could you please mention briefly the steps you perform to user SSL?

    Thanks!

    ReplyDelete
    Replies
    1. Enabling SSL is something done at the container level.
      Also to consider is the hosting service which may have different set up requirements (AWS, Heroku, etc)

      Delete
    2. I did it using Gretty, which is a awesome Gradle plugin. Check it out! (it provides the latest version of Tomcat and Jetty unlike the default tomcat and jetty Gradle plugin in addition to be super easy to configure and well documented)

      https://github.com/akhikhl/gretty

      Delete
  30. Hi

    web context goes into web.xml

    Which file the following head scripts goes..


    Configuring OAuth flows to support
    Protecting the token endpoint
    Configuring User Authentication Services
    Protecting access to resources

    ReplyDelete
  31. Dear Iain Porter,

    I have read your code. It is a very good demo. I found that you are using AJAX if I want to POST, PUT, GET, something to the server. But if I want to to the old method, by using the "FORM POST" action, how can I modify the code to do so?

    ReplyDelete
  32. Hi Iain . Happy New Year from Madrid :)

    I have been able to implement the Spring Integration part in the sample project oauth2-provider-master to have guaranteed delivery in the MongoDB database. Works great.

    What other rest-java-master project features would be interesting to implement in oauth2-provider-master? I'd like to take (merge) the best things about both projects. What else should be added to oauth2-provider-master project?

    ReplyDelete
    Replies
    1. The oauth2-provider project is the most up to date project and actively updated.
      It should contain most of the useful code from the rest-java project (apart form the SI stuff) and more besides.
      Btw. If you have anything that you want to contribute to it then by all means issue a pull request.

      Delete
    2. Thanks Iain. I could contribute uploading the necessary changes to implement guaranteed delivery with MongoDB, although i'm totally newbie in GitHub ... should i do it through the section 'pull request' ?.

      Delete
    3. This link explains it quite well: https://help.github.com/articles/creating-a-pull-request/

      Delete
  33. Excellent post Iain. Thanks for sharing. A couple of questions.

    - Is it possible to timeout access tokens? I've played with the app a few times, and i seem to be able to access the API's with the original access_token days later. Any advice?
    - I'd like to add a last_logged_in_date. Any advice on how i may go about doing this?

    Thanks!
    Anre

    ReplyDelete
    Replies
    1. Arne,

      If you look in src/main/resources/properties/application.properties you will see two properties that effect the expiration date of the access tokens and refresh tokens:

      #Expiry time for access tokens in seconds
      oauth.token.access.expiresInSeconds=5184000

      #Expiry time for refresh tokens in seconds
      oauth.token.refresh.expiresInSeconds=5184000

      If you want a last logged in date then the easiest solution would be to add a custom filter and add it to the chain for the /oauth/token endpoint.
      Look at oauth2-configuration.xml for an example of this using the corsFilter

      Delete
  34. Hello,
    Thx alot for your effort. I need to further help. If you could help, i would be appreciated. I want to position mysql instead of mongodb. How can i achieve that?

    ReplyDelete
    Replies
    1. I'm trying to do the same, but with MariaDB which is a drop in replacement for mysql.

      I'll share when I have any succes

      Delete
  35. And tbh, i'm wandering that why did not you include social user function that you have written on your previous post. That would be really perfect unrivalled full stack sample

    ReplyDelete
    Replies
    1. Time permitting my next task is to add in support for Authorization grant type

      Delete
  36. Very nice posts, Thank you.

    I have a question about the refresh_token.

    How can we run then via curl?
    I tried:
    curl -X POST -H "Content-Type: application/x-www-form-urlencoded" -H "Authorization: Bearer MzUzYjMwMmM0NDU3NGY1NjUwNDU2ODdlNTM0ZTdkNmE6Mjg2OTI0Njk3ZTYxNWE2NzJhNjQ2YTQ5MzU0NTY0NmM=" -H "Cache-Control: no-cache" -H "Postman-Token: 8c089968-8136-7368-2a3c-ad88cc0eb1c1" -d 'grant_type=refresh_token&refresh_token=7352b75a-d468-499d-85f1-cce1751d1fda' http://localhost:8080/oauth2-provider/oauth/token

    Thank you

    ReplyDelete
    Replies
    1. This should work:

      curl -v -X POST \
      -H "Content-Type: application/json" \
      -H "Authorization: Basic [BASE 64 Encoded client credentials]" \
      'http://localhost:8080/oauth2-provider/oauth/token?grant_type=refresh_token&refresh_token=[The value of the refresh token returned from login]"'

      Delete
  37. ./gradlew appRun -Dspring.profiles.active=production -DMONGODB_HOST=localhost -DMONGODB_PORT=27017

    This was not working for me (could not send email ... and more)

    So after some time...

    ----------
    ./gradlew appRun

    +

    build.gradle :
    --------
    ...
    gretty{
    jvmArg '-Dspring.profiles.active=production'
    jvmArg '-DMONGODB_HOST=localhost'
    jvmArg '-DMONGODB_PORT=27017'
    }
    ...
    --------

    ReplyDelete
    Replies
    1. or

      gretty {
      jvmArgs = ['-Dspring.profiles.active=production',
      '-DMONGODB_HOST=localhost',
      '-DMONGODB_PORT=27017']
      }

      Delete
    2. Adding in gretty caused the problem. It looks like gretty does not support system properties.
      I have reverted to using tomcat explicitly in the build.

      This should work for you:

      ./gradlew tomcatRun -Dspring.profiles.active=production -DMONGODB_HOST=localhost -DMONGODB_PORT=27017

      Delete
  38. Hola !
    Quisiera saber si es posible dar seguridad a un servicio Rest con Switchyard y resteasy.
    Soy novato en esto y si es posible me gustaría que dejaran un ejemplo del como se realizaría.

    ReplyDelete
  39. Has anyone successfully migrated from MongoDB to MySQL ?

    Any tips?


    grtz,
    Harry

    ReplyDelete
  40. I just found this:

    https://github.com/ben-manes/gradle-versions-plugin

    "Displays a report of the project dependencies that are up-to-date, exceed the latest version found, have upgrades, or failed to be resolved."

    ReplyDelete
  41. Mr Porter,
    I try to apply your solution into my existing project. I stucked with the error that i shared below. And i can not forward. However, I am sure that all complaint classes are actually exists under right package. Seems to be that i am missing something.
    Could you plz lighten me? If you wanna see the project. Here it is=> https://github.com/webyildirim/trafikhaberci


    thx

    tomcat error log start such as:
    SEVERE: Context initialization failed
    org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#0': Invocation of init method failed; nested exception is org.springframework.beans.factory.CannotLoadBeanClassException: Cannot find class [com.trafficalarm.rest.security.HierarchicalJsr250Voter] for bean with name 'roleVoter' defined in class path resource [spring/security/security-configuration.xml]; nested exception is java.lang.ClassNotFoundException: com.trafficalarm.rest.security.HierarchicalJsr250Voter
    Related cause: org.springframework.beans.factory.CannotLoadBeanClassException: Cannot find class [com.trafficalarm.rest.security.HierarchicalJsr250Voter] for bean with name 'roleVoter' defined in class path resource [spring/security/security-configuration.xml]; nested exception is java.lang.ClassNotFoundException: com.trafficalarm.rest.security.HierarchicalJsr250Voter
    Related cause: org.springframework.beans.factory.CannotLoadBeanClassException: Cannot find class [com.trafficalarm.rest.filter.spring.SpringCrossOriginResourceSharingFilter] for bean with name 'corsFilter' defined in class path resource [spring/oauth/oauth2-configuration.xml]; nested exception is java.lang.ClassNotFoundException: com.trafficalarm.rest.filter.spring.SpringCrossOriginResourceSharingFilter
    Related cause: org.springframework.beans.factory.CannotLoadBeanClassException: Cannot find class [com.trafficalarm.rest.security.OAuthRestEntryPoint] for bean with name 'oauthRestEntryPoint' defined in class path resource [spring/oauth/oauth2-configuration.xml]; nested exception is java.lang.ClassNotFoundException: com.trafficalarm.rest.security.OAuthRestEntryPoint

    ReplyDelete
  42. Any succes porting to mysql ??


    please share.

    ReplyDelete
    Replies
    1. in "token-store.xml"











      in "build.gradle"

      // Database
      compile "mysql:mysql-connector-java:5.1.34"

      // DataSource (HikariCP)
      compile "com.zaxxer:HikariCP:2.3.2"

      in "app-production.properties"

      #Database Configuration
      db.driver=com.mysql.jdbc.Driver
      db.url=jdbc:mysql://localhost:3306/dbname
      db.username=root
      db.password=pass123

      Delete
    2. token-store:
      (I can't publish xml ...)

      http://pastebin.com/PP9UG7m5

      Delete
  43. Hi All, Does any one has used OAuth2 client (other than Apache Oltu) in order to connect to the external social sites or Cloud Sites? Have anyone implemented Spring OAuth2 working example?

    ReplyDelete
  44. Could you please tell us how to remove the mongodb from this project and replace it by mysql.

    I removed the oauth2 form mongo but cant do the same for users and verificationtoken.

    thank you

    ReplyDelete
    Replies
    1. anyone any luck? Tried porting java-rest to newer dependencies. But I keep getting errors because of hibernate 3.6 -> 4.3

      Delete
  45. Could you help me with this ??

    http://stackoverflow.com/questions/28455911/can-not-set-token-defaulttokenservices-field-userconfiguration-tokenservices-to

    thank you.

    ReplyDelete
    Replies
    1. Hey, i occured with the same situation same on yours. Your stack overflow question has removed. Could you resolve it?

      Delete
  46. Very good, thank you so much!

    ReplyDelete
  47. I am trying to follow your example. i see that the code is already converted to Java annotation - great. Do you have an example to use MySQL instead of Mongo for JDBC?

    ReplyDelete
  48. How can we save a new user that is logging in using OAuth 2 (using Twitter/Google/FB credentials?) - The client will pass an access_token but how do we create a new user or login a user with just an access_token? We can retrieve the email address of the user using the token but then how do we tell your framework to create the user (without a password)?

    ReplyDelete
    Replies
    1. Good question. That involves implementing the Authorization grant type. The spring oauth2 sample project has a good example of how to do this.
      It is on my long list of things to do as soon as my regular workload clears up a bit.

      Delete
    2. Thanks for your response. How long would it take someone with Spring experience to implement this? I'm thinking I could outsource this and then contribute the code to your project.

      Delete
    3. Good point. I am also interested in this subject

      Delete
  49. This comment has been removed by the author.

    ReplyDelete
  50. Mr Porter,
    I have face an exception in my trials. If you could review it, i would be appreciated.
    Here is my github: https://github.com/webyildirim/trafikhaberci

    Thank you..

    And the exception is:
    org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userConfiguration': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private org.springframework.security.oauth2.provider.token.DefaultTokenServices com.trafficalarm.rest.configuration.UserConfiguration.tokenServices; nested exception is java.lang.IllegalArgumentException: Can not set org.springframework.security.oauth2.provider.token.DefaultTokenServices field com.trafficalarm.rest.configuration.UserConfiguration.tokenServices to com.sun.proxy.$Proxy67
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:292)
    ...
    Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private org.springframework.security.oauth2.provider.token.DefaultTokenServices com.trafficalarm.rest.configuration.UserConfiguration.tokenServices; nested exception is java.lang.IllegalArgumentException: Can not set org.springframework.security.oauth2.provider.token.DefaultTokenServices field com.trafficalarm.rest.configuration.UserConfiguration.tokenServices to com.sun.proxy.$Proxy67
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:508)
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:87)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:289)
    ... 26 more
    Caused by: java.lang.IllegalArgumentException: Can not set org.springframework.security.oauth2.provider.token.DefaultTokenServices field com.trafficalarm.rest.configuration.UserConfiguration.tokenServices to com.sun.proxy.$Proxy67
    at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(Unknown Source)
    at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(Unknown Source)
    at sun.reflect.UnsafeObjectFieldAccessorImpl.set(Unknown Source)
    at java.lang.reflect.Field.set(Unknown Source)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:504)
    ... 28 more

    ReplyDelete
    Replies
    1. The problem was solved after adding @EnableAspectJAutoProxy(proxyTargetClass=true)
      Detail is here: http://stackoverflow.com/questions/29349692/throwing-an-exception-while-implementing-oauth2

      Delete
  51. Hi Porter,
    In memory token store setting is not working for dev profile, always it is checking for mongo instance

    ReplyDelete
  52. This comment has been removed by the author.

    ReplyDelete
  53. I've added an ArrayList to the User (and ApiUser) classes. I'm able to persist an instance of the User class with the new ArrayList but when the user requests his profile or logging-in again, the new ArrayList data is lost.

    I noticed that oAuth2AuthenticationAccessToken keeps an old instance of the user class and when the user requests a protected resource (like getting his profile) or logging-in again, it uses that instance. Do we need to refresh the token after we update the User instance class? Thanks

    ReplyDelete
    Replies
    1. I would need to see some code to be able to comment.
      The User class already persists an ArrayList property (roles)

      Delete
    2. Well this problem also exists for the Roles property. Basically, if you update the user's role property, when he logs in again it uses the user instance from oAuth2AuthenticationAccessToken and you will lose any changes you've made to User.roles. The issue here is that there's a cached version of the User instance in both oAuth2AuthenticationAccessToken and oAuth2AuthenticationRefreshToken, and any request using the access token will use that instance and override the User instance.

      Delete
    3. You can try this by logging user in, modify Roles in mongo, and then log user again and you will notice that the roles revert back to what you had before you made the manual changes.

      Delete
    4. Ah, I see what you mean. The token needs to be updated when a change is made to the user. As a workaround your method to retrieve the user details should not rely on the cached info in the Security Context but make a callback to the UserRepository to get the data.

      Delete
    5. Great that worked. Did not realize that the User info cached in Security Context was out of sync. Thanks

      Delete
    6. I solved this by modify the TokenStore implementation. So it first removes the token if if exists, and then inserts the new "updated" token, in method storeAccessToken. Like so:

      removeAccessToken(token);

      OAuth2AuthenticationAccessToken oAuth2AuthenticationAccessToken =
      new OAuth2AuthenticationAccessToken(
      token,
      authentication,
      authenticationKeyGenerator.extractKey(authentication));

      oAuth2AccessTokenRepository.insert(oAuth2AuthenticationAccessToken);

      Do you see any pitfalls with this solution?

      Delete
  54. What is the difference with your project vs Spring Cloud Security?

    ReplyDelete
  55. If we curl with Authorization Basic MzUzYjMwMmM0NDU3NGY1NjUwNDU2ODdlNTM0ZTdkNmE6Mjg2OTI0Njk3ZTYxNWE2NzJhNjQ2YTQ5MzU0NTY0NmM= will give us the token. Then anyone who can steal above header can get the user resource, then how can we secure it?

    ReplyDelete
    Replies
    1. You can't access a resource with the basic header. That is only for accessing endpoints that return a token. And you can't get a token unless you provide credentials.
      With that said all communications using the OAuth2 protocol should be over SSL.

      Delete
    2. I would like to separate Authentication server and Resource server completely, which means the tokenstore cannot be shared as well as they geographically separated. Based on your project, we need to share the access token between authorization server and resource server, so how can we separate them? Is JWT solution for it?

      Delete
  56. i was able create test user using curl, but i don't see any collections in mongodb.

    > use test
    switched to db test
    > show collections
    >

    ReplyDelete
    Replies
    1. the name of the mongo db can be found in src/main/resources/properties/application.properties

      The property: mongo.db.name=oauth-provider

      > use oauth-provider

      Delete
  57. Just wondering why the authentication part is repeated both in the refresh token and access token.

    "authentication" : {
    "storedRequest" : {
    "authorities" : [
    {
    "role" : "ROLE_USER",
    "_class" : "org.springframework.security.core.authority.SimpleGrantedAuthority"
    }
    ],
    "approved" : true,
    "responseTypes" : [ ],
    "extensions" : {

    },
    "clientId" : "353b302c44574f565045687e534e7d6a",
    "scope" : [
    " write",
    "read"
    ],
    "requestParameters" : {

    }
    },
    "userAuthentication" : {
    "_class" : "org.springframework.security.authentication.UsernamePasswordAuthenticationToken",
    "principal" : "4285a4d6-288a-4732-aa5a-a304b2131652",
    "credentials" : "37c31d7b5cd8691e193e601810353d07bef26273d229cd2cb9fcca5e7b35624c78e5a807a93d19e9",
    "authorities" : [
    {
    "role" : "ROLE_USER",
    "_class" : "org.springframework.security.core.authority.SimpleGrantedAuthority"
    }
    ],
    "authenticated" : true
    },
    "authorities" : [
    {
    "role" : "ROLE_USER",
    "_class" : "org.springframework.security.core.authority.SimpleGrantedAuthority"
    }
    ],
    "authenticated" : false
    },

    ReplyDelete
  58. I'm getting following error after updating oauth to 2.0.7.RELEASE. Any idea?

    curl -v -X POST \
    > -H "Content-Type: application/json" \
    > -H "Authorization: Basic MzUzYjMwMmM0NDU3NGY1NjUwNDU2ODdlNTM0ZTdkNmE6Mjg2OTI0Njk3ZTYxNWE2NzJhNjQ2YTQ5MzU0NTY0NmM=" \
    > --data-urlencode 'username=test@example.com' --data-urlencode 'password=password' --data-urlencode 'grant_type=password' \
    > 'http://localhost:8080/oauth/token'
    * Hostname was NOT found in DNS cache
    * Trying ::1...
    * Connected to localhost (::1) port 8080 (#0)
    > POST /oauth/token HTTP/1.1
    > User-Agent: curl/7.37.1
    > Host: localhost:8080
    > Accept: */*
    > Content-Type: application/json
    > Authorization: Basic MzUzYjMwMmM0NDU3NGY1NjUwNDU2ODdlNTM0ZTdkNmE6Mjg2OTI0Njk3ZTYxNWE2NzJhNjQ2YTQ5MzU0NTY0NmM=
    > Content-Length: 65
    >
    * upload completely sent off: 65 out of 65 bytes
    < HTTP/1.1 400 Bad Request
    * Server Apache-Coyote/1.1 is not blacklisted
    < Server: Apache-Coyote/1.1
    < Cache-Control: no-cache, no-store, max-age=0, must-revalidate
    < Pragma: no-cache
    < Expires: 0
    < X-XSS-Protection: 1; mode=block
    < X-Frame-Options: DENY
    < X-Content-Type-Options: nosniff
    < Cache-Control: no-store
    < Pragma: no-cache
    < Content-Type: application/json;charset=UTF-8
    < Transfer-Encoding: chunked
    < Date: Fri, 08 May 2015 19:57:32 GMT
    < Connection: close
    <
    * Closing connection 0
    {"error":"invalid_request","error_description":"Missing grant type"}

    ReplyDelete
  59. I'd like to allow a client request to GET a resource, but allow only registered users to POST. Something like this:





    Would really appreciate if you could give pointers on how to achieve this. So far my attempts been unsuccessful :(

    Thank you in advance.
    Mike

    ReplyDelete
    Replies
    1. shoot, the sample code did not got through. here is what I had in mind:

      < http pattern="/rest/v1.0/myobject/**"
      create-session="never"
      entry-point-ref="oauthAuthenticationEntryPoint"
      xmlns="http://www.springframework.org/schema/security" >

      < intercept-url pattern="/rest/v1.0/myobject" method="GET" access="ROLE_CLIENT"/>
      < intercept-url pattern="/rest/v1.0/myobject" method="POST" access="ROLE_USER"/>

      Delete
  60. what is the purpose of userconfiguration.java? Is it really needed?

    ReplyDelete
  61. where is the userAuthenticationManager is used?...none of the end point use this authentication manager...How the flow authenticates the username and password ?...

    ReplyDelete
  62. Hi, I manage to implement the above code and I tested it through rest client. It works. My question is I have a web application with a login which is using spring security. Now I want my login service to authenticate through the webservice using oauth2. That is my web app will consume your web service. Do you have a tutorial on this?
    Hope to get a reply soon.
    Thanks in advance

    ReplyDelete
    Replies
    1. I have some basic web client pages that you can use as the basis for your own implementation.

      See https://github.com/iainporter/oauth2-provider/blob/master/src/main/webapp/index.html

      Delete
  63. Hi Iain,
    Thak you for explaing and proving this blog and code. I downloaded and did the build for the first time. The create user worked but when I try to get the token via curl I am getting {"error":"invalid_request","error_description":"Missing grant type"}

    Also trying to login via the http://localhost:8080/oauth2-provider/index.html on the gradle console I am getting these errors
    logbak: 16:59:36.466 com.porterhead.user.UserService - Credentials [user@example.com] failed to locate a user.
    logbak: 16:59:36.473 o.s.s.o.p.endpoint.TokenEndpoint - Handling error: InvalidGrantException, Bad credentials

    Looks like some thing changed in the new build. I did not change any version defined in the gradle, Using Java 8. Any idea why?

    ReplyDelete
    Replies
    1. This looks like a problem with your mongoDB set up.
      Have you checked that the account exists in your DB?

      Failing that send me your exact curl statements along with the console output from the client and server.

      Delete
  64. Hi Porter,
    I'm not that good in gradle for handling dependencies. So I refactor the code to used maven in my project. Here is the flow of my project:
    1-I have an Authorization server and a resource server in separate projects.
    2-I then included your mongo token provider.
    3-I'm able to login and persist the token without any problem. But when I want to access my protected resource, I have the following stack trace.
    org.springframework.data.mapping.model.MappingInstantiationException: Failed to instantiate org.springframework.security.authentication.UsernamePasswordAuthenticationToken using constructor NO_CONSTRUCTOR with arguments
    at org.springframework.data.convert.ReflectionEntityInstantiator.createInstance(ReflectionEntityInstantiator.java:64) ~[spring-data-commons-1.9.4.RELEASE.jar:na]
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:250) ~[spring-data-mongodb-1.6.4.RELEASE.jar:na]
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:230) ~[spring-data-mongodb-1.6.4.RELEASE.jar:na]
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.readValue(MappingMongoConverter.java:1180) ~[spring-data-mongodb-1.6.4.RELEASE.jar:na]
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.access$200(MappingMongoConverter.java:77) ~[spring-data-mongodb-1.6.4.RELEASE.jar:na]
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter$MongoDbPropertyValueProvider.getPropertyValue(MappingMongoConverter.java:1128) ~[spring-data-mongodb-1.6.4.RELEASE.jar:na]
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter$MongoDbPropertyValueProvider.getPropertyValue(MappingMongoConverter.java:1091) ~[spring-data-mongodb-1.6.4.RELEASE.jar:na]
    at org.springframework.data.mapping.model.PersistentEntityParameterValueProvider.getParameterValue(PersistentEntityParameterValueProvider.java:78) ~[spring-data-commons-1.9.4.RELEASE.jar:na]
    at org.springframework.data.mapping.model.SpELExpressionParameterValueProvider.getParameterValue(SpELExpressionParameterValueProvider.java:63) ~[spring-data-commons-1.9.4.RELEASE.jar:na]
    at org.springframework.data.convert.ReflectionEntityInstantiator.createInstance(ReflectionEntityInstantiator.java:71) ~[spring-data-commons-1.9.4.RELEASE.jar:na]
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:250) ~[spring-data-mongodb-1.6.4.RELEASE.jar:na]
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:230) ~[spring-data-mongodb-1.6.4.RELEASE.jar:na]
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.readValue(MappingMongoConverter.java:1180) ~[spring-data-mongodb-1.6.4.RELEASE.jar:na]
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.access$200(MappingMongoConverter.java:77) ~[spring-data-mongodb-1.6.4.RELEASE.jar:na]
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter$MongoDbPropertyValueProvider.getPropertyValue(MappingMongoConverter.java:1128) ~[spring-data-mongodb-1.6.4.RELEASE.jar:na]
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter.getValueInternal(MappingMongoConverter.java:864) ~[spring-data-mongodb-1.6.4.RELEASE.jar:na]
    at org.springframework.data.mongodb.core.convert.MappingMongoConverter$1.doWithPersistentProperty(MappingMongoConverter.java:279) ~[spring-data-mongodb-1.6.4.RELEASE.jar:na]

    My guess is that there is a problem with converting/mapping of the object in the database back to Java object. So I included the bean OAuth2AuthenticationReadConverter in the project. Sorry my question mine be too silly, I don't know exactly where and how I have to reference this bean.

    Thanks for your help.

    ReplyDelete
    Replies
    1. Look at com.porterhead.configuration.MongoDbConfiguration

      This class extends AbstractMongoConfiguration

      and overrides the method to register the custom conversion:

      @Override
      @Bean
      public CustomConversions customConversions() {
      List> converterList = new ArrayList>();
      OAuth2AuthenticationReadConverter converter = new OAuth2AuthenticationReadConverter();
      converterList.add(converter);
      return new CustomConversions(converterList);
      }

      Delete
    2. After integrating the converter, I now have the following stack trace
      org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type com.mongodb.BasicDBObject to type org.springframework.security.oauth2.provider.OAuth2Authentication
      at org.springframework.core.convert.support.GenericConversionService.handleConverterNotFound(GenericConversionService.java:313) ~[spring-core-4.1.8.RELEASE.jar:4.1.8.RELEASE]
      at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:195) ~[spring-core-4.1.8.RELEASE.jar:4.1.8.RELEASE]
      at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:176) ~[spring-core-4.1.8.RELEASE.jar:4.1.8.RELEASE]
      at org.springframework.data.mongodb.core.convert.MappingMongoConverter.readValue(MappingMongoConverter.java:1174) ~[spring-data-mongodb-1.6.4.RELEASE.jar:na]
      at org.springframework.data.mongodb.core.convert.MappingMongoConverter.access$200(MappingMongoConverter.java:77) ~[spring-data-mongodb-1.6.4.RELEASE.jar:na]

      It's very bizarre. After reading the stack trace, I notice that it seems like there is a problem at this level
      OAuth2AuthenticationAccessToken token = oAuth2AccessTokenRepository.findByTokenId(tokenValue);
      But I don't know why?

      Delete
  65. Can we have maven version?

    ReplyDelete
  66. When I visited the url "/oauth/token", i get the error "Invalid CSRF Token 'null' was found on the request parameter '_csrf' or header 'X-CSRF-TOKEN'."

    ReplyDelete
    Replies
    1. That sounds like a problem with Spring and CSRF.

      See https://docs.spring.io/spring-security/site/docs/current/reference/html/csrf.html

      I also have a companion project for testing out CSRF - https://github.com/iainporter/oauth2-provider-test-client

      Delete
  67. Could you please implements a TokenStore and save tokens into Redis

    ReplyDelete
  68. can any one help me in configuring resource server, oauth2 provider, and the client?. all these 3 components should reside and run in different machine. if you guys coded for this pls share some info.

    ReplyDelete
  69. Can you provide a Java Config equivalent to this xml based configuration?

    ReplyDelete
  70. IF you are looking for a solution like an extension of Spring Social Security, Check this out :
    http://stackoverflow.com/questions/35911723

    ReplyDelete
  71. Hi Ian,

    your example is really awesome! I'm using it and adapting to Spring 4.1.6 and Java 8.
    Of course I had to do some changes in order to work with this environment and finally is working; however I have some problems with this request:


    curl -X POST \
    -H "Content-Type: application/json" \
    -H "Authorization: Basic MzUzYjMwMmM0NDU3NGY1NjUwNDU2ODdlNTM0ZTdkNmE6Mjg2OTI0Njk3ZTYxNWE2NzJhNjQ2YTQ5MzU0NTY0NmM=" \
    'http://localhost:8080/auth/oauth/token?grant_type=password&username=user@example.com&password=password'

    It's response is: {"error":"invalid_grant","error_description":"Bad credentials"}

    While debugging I found that when it attempts to load user data at org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService.loadUserByUsername() is using an instance of InMemoryClientDetailsService which only have client data but no users, so it throws NoSuchClientException.

    Surely I have a missconfiguration in order to work with Spring 4.1.6. I was wondering if you already have seen this problem.

    Can you help me a litle bit with this?

    Thanks in advance.

    ReplyDelete
  72. I also met with the same problem trebor mentioned. {"error":"invalid_grant","error_description":"Bad credentials"} caused by InMemoryClientDetailsService when it tries to check userName in the clientDetailsStore.

    Any idea how to solve this?

    ReplyDelete
  73. First time, it checks for client id then second calls comes with userName. Username is not there in the clientDetailsStore

    ReplyDelete
    Replies
    1. The InMemoryClientDetailsStore is used to store the client authentication details that are stored in client-details.xml
      To make a call to oauth/token or v1.0/users POST you need to Base64 encode the clientId and secretId of one of the client configs in that file and send that as the Basic Authentication header.
      Can you send the curl statements that you are using to 1. register a new user and 2. ask for a token for that user

      Delete
    2. Let me explain the implementation.

      My web.xml

      < servlet>
      < servlet-name>rest
      < servlet-class>org.springframework.web.servlet.DispatcherServlet
      < load-on-startup>1


      < servlet-mapping>
      < servlet-name>rest
      < url-pattern>/


      < listener>
      < listener-class>org.springframework.web.context.ContextLoaderListener



      < context-param>
      < param-name>contextConfigLocation
      < param-value>
      /WEB-INF/spring-security.xml




      < filter>
      < filter-name>springSecurityFilterChain
      < filter-class>org.springframework.web.filter.DelegatingFilterProxy


      < filter-mapping>
      < filter-name>springSecurityFilterChain
      < url-pattern>/*





      rest-servlet.xml

      < beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:context="http://www.springframework.org/schema/context"
      xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:p="http://www.springframework.org/schema/p"
      xsi:schemaLocation="
      http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans.xsd
      http://www.springframework.org/schema/context
      http://www.springframework.org/schema/context/spring-context.xsd
      http://www.springframework.org/schema/mvc
      http://www.springframework.org/schema/mvc/spring-mvc.xsd">
      < context:component-scan base-package="com.oAuth2.ws" />
      < mvc:annotation-driven />


      Delete
    3. Then I have a controller class

      @Controller
      @RequestMapping("/api/users")
      public class RequestController {

      @Autowired
      DataService dataService;

      @RequestMapping(value = "/", method = RequestMethod.GET)
      @ResponseBody
      public List< User> list() {
      return dataService.getUserList();

      }
      }

      and


      public class DataServiceImpl implements DataService {

      @Override
      public List< User> getUserList() {
      List< User> userList = new ArrayList< User>();

      userList.add(new User(1, "user_a", "user_a@example.com", "9898989898"));
      userList.add(new User(2, "user_b", "user_b@example.com", "9767989898"));
      userList.add(new User(3, "user_c", "user_c@example.com", "9898459898"));

      return userList;
      }

      }

      User class is a simple java bean

      Deployed the above code to Tomcat as oAuth2

      Trying to invoke
      http://localhost:8081/oAuth2/oauth/token?grant_type=password&client_id=restapp&client_secret=restapp&username=sajin&password=test&scope=read,write

      Delete
  74. i am not able to put spring-security.xml here due to its size, even after splitting.

    ReplyDelete
  75. Great article Lot's of information to Read...Great Man Keep Posting and update to People..Thanks

    ReplyDelete