These are chat archives for alvarosanchez/spring-security-rest

16th
Dec 2014
Yanis Ikene
@yanisIk
Dec 16 2014 20:25

Hello Alvaro, first I would like to thank you for this wonderful plugin.
However, your plugin doesn't answer to all my needs so I decided to modify it and I want to ask you if this is correct and if you can do it in a future release too.

So, what I need is OAuth but with the client sending the oauth token (for usage with a mobile app for example). Your plugin only allows to OAuth with a website client side, but not with other type of clients like mobile apps.
So here's the new flow :

1) Client authenticate to provider and receives informations.
2) Client login to our grails app : api/authenticate/{provider}
3) The modified RestOAuthController does the following :

    @Secured(['permitAll'])
class RestOauthController extends RestController{
    static responseFormats = ['json']
    static allowedMethods = [authenticate: "POST"]

    def restOauthService

    //Client OAuth flow only
    def authenticate(String provider){

        BaseOAuthClient client = restOauthService.getClient(provider)
        WebContext context = new J2EContext(request, response)

        try {
                String tokenValue = restOauthService.storeAuthentication(provider, context)
        } 
        catch (Exception e) {
                String errorParams
                if (e instanceof UsernameNotFoundException) {
                    restOauthService.registerUser(provider, context) 
                    //Is this necessary or is it already done in DefaultOauthUserDetailsService in catch (UsernameNotFoundException unfe) log.debug "User not found. Creating a new one with default roles: ${defaultRoles}"
                } else {
                    respond ["error" : "&error=${e.cause?.code?:500}&message=${e.message?.encodeAsURL()?:''}"]
                }
        }
        log.debug "Returning access_token in json"
        respond [''access_token'' : tokenValue]
    }    
    }
}
Yanis Ikene
@yanisIk
Dec 16 2014 20:44

4) The client sends back this token in each request

My questions :
1) How do I access the logged user (MyUser not Spring's User) in a controller :
Example :

  class MyUser{
        User user //Spring security User
        static hasOne = ['user']
        static hasMany = [dogs : Dog]
  }
  class Dog{
        MyUser owner
        String name
        static belongsTo = ['owner']
   }
  class DogRestController extends RestController{
        .....
        @Secured['ROLE_USER']
         def index(){
             MyUser myUser = getAuthenticatedUser() //Spring security metaclass method
              //Will this map correctly the User to MyUser ? is my domain well defined ? 
             respond Dog.findAllByOwner(myUser)
          } 
    }
Yanis Ikene
@yanisIk
Dec 16 2014 22:38

I also modified RestOauthService to create a new MyUser when this is the first time OAuth Login/Signup

String storeAuthentication(String provider, WebContext context) {
    .......
    try{
            //is created even if first time login/signup
    OauthUser userDetails = oauthUserDetailsService.loadUserByUserProfile(profile, defaultRoles)
}
catch (UsernameNotFoundException unfe) {
    log.debug "MyUser not found. Creating a new one"
    registerMyUser(userDetails)
}
     ....
}

/** Register a new MyUser with the informations contained in OAuth profile **/
@Transactional
def registerMyUser(OauthUser user){

CommonProfile profile = user.userProfile

Address address = new Address(city: profile.location)
Profile profile = new Profile(name:profile.displayName, emailAddress:profile.email, photoUrl:profile.pictureUrl, address:address )

HobdyUser hobdyUser = new HobdyUser(user: user, profile: profile)

hobdyUser.save() //Do not flush
}    

Is this the correct way to do it ?

Thanks.

Florent Blanvillain
@florent-blanvillain
Dec 16 2014 23:13

Following my question, I think I found the answer and it was incredibly simple !!
Here is a controller's action that seems to do the job:

def switchUser() {
    def username = params.username
    def principal = userDetailsService.loadUserByUsername(username)
    if (principal) {
        def oldToken = request.getHeader("X-Auth-Token")
        tokenStorageService.removeToken(oldToken)

        def newToken = new SecureRandomTokenGenerator().generateToken()
        tokenStorageService.storeToken(newToken, principal)
        render(contentType: "text/json") {
            [
                    username    : username,
                    roles       : principal.authorities.authority,
                    access_token: newToken
            ]
        }
    } else {
        render status: 404
    }
}

Thanks